;============================================================ ; iron_oauth.hsp — OAuth 2.0 認証フローモジュール ; ; Authorization Code Flow (PKCE) を実装。 ; WebView2 またはデフォルトブラウザで認証画面を開き、 ; localhost リダイレクトでコードを受け取る。 ; ; API: ; oauth_authorize "auth_url", "client_id", "redirect_uri", "scope" ; → refstr にアクセストークン, stat (0=成功) ; ; oauth_token "token_url", "client_id", "code", "redirect_uri" ; → refstr にトークン JSON レスポンス ; ; 簡易版: ブラウザで認証 → localhost:port にリダイレクト ; → HSP 側で TCP サーバーを一時的に立ち上げて code を取得 ; ; 例: ; #include "iron_oauth.hsp" ; ; Google OAuth example ; oauth_start_listener 8080 ; auth_url = "https://accounts.google.com/o/oauth2/auth" ; auth_url += "?client_id=YOUR_CLIENT_ID" ; auth_url += "&redirect_uri=http://localhost:8080/callback" ; auth_url += "&response_type=code" ; auth_url += "&scope=openid email" ; exec auth_url, 16 ; open in browser ; oauth_wait_code 30 ; wait 30 sec for callback ; if stat == 0 { ; mes "code: " + refstr ; } ; oauth_stop_listener ;============================================================ #ifndef __iron_oauth_hsp__ #define __iron_oauth_hsp__ #module iron_oauth #uselib "ws2_32.dll" #cfunc _WSAStartup "WSAStartup" int, var #cfunc _WSACleanup "WSACleanup" #cfunc _socket "socket" int, int, int #cfunc _bind "bind" int, var, int #cfunc _listen "listen" int, int #cfunc _accept "accept" int, int, int #cfunc _recv "recv" int, var, int, int #cfunc _send "send" int, var, int, int #cfunc _closesocket "closesocket" int #cfunc _htons "htons" int dim _srv_sock, 1 dim _wsa_inited, 1 ;------------------------------------------------------------ ; oauth_start_listener port — TCP リスナー開始 ;------------------------------------------------------------ #deffunc oauth_start_listener int port if _wsa_inited == 0 { sdim wsadata, 512 _WSAStartup 0x0202, wsadata _wsa_inited = 1 } ; AF_INET=2, SOCK_STREAM=1, IPPROTO_TCP=6 _srv_sock = _socket(2, 1, 6) if _srv_sock <= 0 : return -1 ; sockaddr_in: sin_family(2)=AF_INET, sin_port(2)=htons(port), sin_addr(4)=0.0.0.0, zero(8) sdim addr, 16 wpoke addr, 0, 2 ; AF_INET wpoke addr, 2, _htons(port) ; sin_addr = 0 (INADDR_ANY) if _bind(_srv_sock, addr, 16) != 0 { _closesocket _srv_sock : _srv_sock = 0 return -2 } _listen _srv_sock, 1 return 0 ;------------------------------------------------------------ ; oauth_wait_code timeout_sec — コールバックを待つ ; → refstr に code パラメータ, stat (0=成功, -1=タイムアウト) ;------------------------------------------------------------ #deffunc oauth_wait_code int timeout_sec, \ local client, local buf, local req, local code, local pos, local pos2, \ local response, local elapsed ; Simple blocking accept with timeout via non-blocking + poll ; For simplicity, use blocking with a timeout loop elapsed = 0 repeat ; Use select() to check if connection available ; (simplified: just accept with short timeout) client = _accept(_srv_sock, 0, 0) if client > 0 : break await 500 elapsed++ if elapsed >= timeout_sec * 2 : return -1 loop sdim buf, 4096 _recv client, buf, 4095, 0 ; Parse "GET /callback?code=XXXXX HTTP/1.1" req = buf code = "" pos = instr(req, 0, "code=") if pos >= 0 { pos += 5 pos2 = instr(req, pos, "&") if pos2 < 0 { pos2 = instr(req, pos, " ") } if pos2 >= 0 { code = strmid(req, pos, pos2) } else { code = strmid(req, pos, 256) } } ; Send response response = "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n

Authorization successful!

You can close this window.

" _send client, response, strlen(response), 0 _closesocket client return code ;------------------------------------------------------------ ; oauth_stop_listener — リスナー停止 ;------------------------------------------------------------ #deffunc oauth_stop_listener if _srv_sock != 0 { _closesocket _srv_sock _srv_sock = 0 } return #global #endif