02/12 18:24 2022
01/03 14:22 2026
Twitter OAuth 2.0の認可の仕組みを利用して、Webアプリを作る場合、どういうアーキテクチャがいいか検討してみました。表題の通り雑です。またセキュリティ的にも裏が取れているわけではないので、参考程度にお願いします。
方法はざっくり以下の3つ(雑すぎ)。3がいいかなぁと思いシーケンスを書いてみる
補足事項
OAuth 2.0 Authorization Code Flow with PKCEというフローを採用している@startuml actor "User" as user box "Cloudflare Pages" #dee6ee participant "トップページ" as clPages end box box "CDN Edge Server" #e6e6fa participant "Cloudflare Functions" as clFunctions database "User" as dbUser database "Session" as dbSession end box box "Twitter" #2bc4ff participant "Twitter認可画面" as twAuth participant "Twitter認可サーバー" as twServer participant "TwitterAPI" as twApi end box activate clPages activate user user->clPages: login with Twitterを押下 clPages->clPages: code_verifier生成 clPages->clPages: code_verifierをcookieに保存(※1) clPages->clPages: code_verifierからcode_challenge生成 clPages->twAuth: code_challenge activate twAuth twAuth->twAuth: Present authorization dialog twAuth-->user deactivate twAuth user->user: Authraize or declines app(※2) user->twServer activate twServer twServer-->twServer: Redirects to your app's callback URL twServer-->clPages: authraization code deactivate twServer clPages->clPages: cookieからcode_verifierを取り出し clPages->clFunctions: code, code_verifier activate clFunctions clFunctions->twServer: code, code_verifier, clientSecret(※3) activate twServer twServer-->clFunctions: accessToken deactivate twServer clFunctions->twApi: 認可情報の取得 activate twApi twApi-->clFunctions: 取得OK deactivate twApi clFunctions->clFunctions: 認可情報からidを取得 clFunctions->clFunctions: userIdのジェネレート clFunctions->dbUser: idで検索 activate dbUser alt 存在しない場合 dbUser-->clFunctions: 取得NG clFunctions->dbUser: id,userId,accessToken,refreshTokenを登録 dbUser-->clFunctions: 登録OK end dbUser-->clFunctions: 取得OK deactivate dbUser clFunctions->clFunctions: SessionIdを生成 clFunctions->dbSession: SessionIdとidを保存 activate dbSession dbSession-->clFunctions: 保存OK deactivate dbSession clFunctions-->clPages: SessionId(SetCookie: SameSite=Lax;Secure;HttpOnly;) deactivate clFunctions clPages-->user: ログインSuccess deactivate clPages deactivate user @enduml
@startuml actor "User" as user box "Cloudflare Pages" #dee6ee participant "トップページ" as clPages end box box "CDN Edge Server" #e6e6fa participant "Cloudflare Functions" as clFunctions database "Cloudflare KV" as clKv end box box "Twitter" #2bc4ff participant "Twitter認可画面" as twAuth participant "Twitter認可サーバー" as twServer participant "TwitterAPI" as twApi end box activate clPages activate user user->clPages: login with Twitterを押下 clPages->clPages: code_verifier生成 clPages->clPages: code_verifierをcookieに保存(※1) clPages->clPages: code_verifierからcode_challenge生成 clPages->twAuth: code_challenge activate twAuth twAuth->twAuth: Present authorization dialog twAuth-->user deactivate twAuth user->user: Authraize or declines app(※2) user->twServer activate twServer twServer-->twServer: Redirects to your app's callback URL twServer-->clPages: authraization code deactivate twServer clPages->clPages: cookieからcode_verifierを取り出し clPages->clFunctions: code, code_verifier activate clFunctions clFunctions->twServer: code, code_verifier, clientSecret(※3) activate twServer twServer-->clFunctions: accessToken deactivate twServer clFunctions-->clPages: set cookie(SameSite=Lax;Secure;HttpOnly;) deactivate clFunctions clPages->clFunctions: 認可情報取得(/api/me) activate clFunctions clFunctions->twApi: 認可情報取得(/users/me) (※4) activate twApi twApi-->clFunctions: 取得OK deactivate twApi alt 認可失敗した場合 alt refreshトークンがヘッダーにある場合 clFunctions->twAuth: tokenのリフレッシュ activate twAuth twAuth-->clFunctions: リフレッシュOK deactivate twAuth clFunctions->twApi: 認可情報取得 activate twApi twApi-->clFunctions: 取得OK deactivate twApi ref over clFunctions, clKv ユーザーデータ取得処理参照 end clFunctions-->clPages: 認可OK, set cookie(token,refresh_token) else それ以外 clFunctions-->clPages: 401 Unauthorized end end group ユーザーデータ取得処理 clFunctions->clFunctions: idを取り出す clFunctions->clKv: idに紐づくユーザーデータを取得 activate clKv clKv-->clFunctions: 取得OK deactivate clKv end clFunctions-->clPages: 認可OK deactivate clFunctions clPages-->user: ログインSuccess deactivate clPages deactivate user @enduml
※1 もやもや箇所。Twitterに遷移してしまうため、永続領域に書き込まないといけない。code_verifierの格納は、secure属性とSameSite=Laxを付与。HttpOnly属性はJSからなので付与できない。プラスで適切なexpire時間を設定する。
document.cookie="code_verifier=hoge;secure;SameSite=Lax"
※2 scopeは以下の通り
users.read tweet.readoffline.access※3
Twitterには、Public ClientとConfidetial Clientが指定できます。サーバーサイドで安全にClientSecretを運用できる今回のケースでは、Confidetial Clientでいいかなと思いました。Public Clientの場合、Authorization: Basicヘッダーを利用せず、ClientSecretも利用しません。Confidetial Clientを指定すると、Public Clientのトークン取得方法が利用できませんが、逆は可能みたいです。
※4 Rate limitsとUsers lookupを参照。アプリレート制限とユーザーレート制限があり、別で計算される
| 項目 | 値 |
|---|---|
| アクセストークンの期限 | 2(h) |
| リフレッシュトークンの期限 | 不明 |
Authenticationより、/2/oauth2/revokeエンドポイントが用意されている。token_type_hintでアクセストークンもしくはリフレッシュトークンを利用して、トークンをrevokeできる。revokeされるのは両方のトークン。
$ curl --location --request POST 'https://api.twitter.com/2/oauth2/revoke' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--basic -u '<ClientId>:<ClientSecret>' \
--data-urlencode 'token_type_hint=refresh_token' \
# --data-urlencode 'token_type_hint=access_token' \
--data-urlencode 'token=xxx'
実際にこの構成でアプリを作ってみるので、更新情報があればまた書こうと思います。仕事で公開されているPOSTのAPIのHTTPレスポンスヘッダー(Access-Control-Allow-Origin)に呼び出し元を指定して、CSRF対策したのを思い出した。
This site uses Google Analytics.