Skip to content

Instantly share code, notes, and snippets.

@bills-appworks
Created April 4, 2025 23:55
Show Gist options
  • Save bills-appworks/0a8d359f9d3dc9c66d3453c1361d0259 to your computer and use it in GitHub Desktop.
Save bills-appworks/0a8d359f9d3dc9c66d3453c1361d0259 to your computer and use it in GitHub Desktop.
OAuth概要フロー(クライアント:シングルページアプリケーション版)
@startuml
title
OAuthフロー(PKCE/PAR/DPoP含む)
フロー:認可コードグラント(+リフレッシュトークン)
クライアントタイプ:シングルページアプリケーション(OAuth:Public)
end title
actor "リソースオーナー\n(ユーザー)" as user
participant ua [
ユーザーエージェント
(Webブラウザ等)
]
participant spa [
シングルページ
アプリケーション
(JavaScript等
クライアントサイド)
]
box "OAuthサーバー\n(認可サーバー)"
participant par_endpoint [
プッシュ認可
エンドポイント
]
participant auth_endpoint [
認可
エンドポイント
]
participant token_endpoint [
アクセストークン
エンドポイント
]
end box
participant resource [
リソースサーバー
(API・データリポジトリ等
認可ターゲット)
]
autonumber
note over spa
[SPAでのトークン保護]
認可サーバーから受け取ったトークンの格納先候補
a. LocalStorage
b. SessionStorage
c. cookie
d. メモリ
e. ServiceWorker
f. WebCrypto API
a-cについてはクロスサイトスクリプティングの
リスクが比較的高い
end note
== 認可コードリクエスト ==
user -> ua : アクセスリクエスト(操作)
ua -> spa : アクセスリクエスト(操作)
note over spa
1. PKCEシークレット(ランダム文字列)生成
2. PKCEハッシュ作成(シークレット→SHA256)
3. PKCEハッシュbase64urlエンコード(SHA256→base64url)
end note
note over spa
PKCEシークレットをLocalStorage/SessionStorage格納
 (アクセストークンリクエスト時に参照)
end note
note over spa
アプリ固有状態生成
end note
spa -> par_endpoint : プッシュ認可リクエスト\n(PAR)
note left
Host: <プッシュ認可エンドポイントサーバー>
Content-type: application/x-www-form-urlencoded
cliend_id=<クライアントID>
redirect_uri=<リダイレクトURI>
scope=<スコープ> OIDCであれば+openid
state=<アプリ固有状態>
code_challenge=<PKCEハッシュbase64urlエンコード>
code_challenge_metohd=S256 (ハッシュがSHA256の場合)
end note
spa <-- par_endpoint : リクエストURI応答
note right
{
request_uri: urn:ietf:params:oatuth:request_uri:<リクエストに紐づく識別子>
expires_in: <リクエストURI有効期限>
}
end note
ua <-- spa : 認可エンドポイントへのリダイレクト応答
note over ua, auth_endpoint
client_id=<クライアントID>
request_uri=urn:ietf:params:oatuth:request_uri:<リクエストに紐づく識別子>
end note
ua -> auth_endpoint : リダイレクトリクエスト
note over user, auth_endpoint
ユーザー認証(IDパスワード、MFA等)
 →ユーザーエージェントセッションキャッシュ済でユーザー操作無い場合あり
end note
ua <-- auth_endpoint : 認可意思確認画面応答
user <-- ua : 認可意思確認画面レンダリング
user -> ua : 認可許諾操作
ua -> auth_endpoint :リソースオーナーの認可許諾確認
note over par_endpoint, token_endpoint
リクエスト内容に応じた認可コード生成
end note
ua <-- auth_endpoint : リダイレクトURIへのリダイレクト応答
note over ua, auth_endpoint
code=<認可コード>
state=<アプリ固有状態>
end note
ua -> spa : リダイレクトリクエスト
note over spa
リクエスト時と応答のアプリ固有状態を
一致チェック(CSRF防止)
end note
== アクセストークンリクエスト ==
note over spa
1. DPoP証明生成(ヘッダ・ペイロード)
2. DPoP証明ヘッダ・ペイロードをそれぞれbase64エンコード
3. 署名無しトークン生成 <base64 DPoPヘッダ>.<base64 DPoPペイロード>
4. 署名無しトークンを電子署名し署名生成
5. 署名をbase64エンコード
end note
spa -> token_endpoint : アクセストークンリクエスト
note left
Host: <アクセストークンエンドポイントサーバー>
DPoP: <base64 DPoPヘッダ>.<base64 DPoPペイロード>.<base64署名>
grant_type=authorization_code
code=<認可コード>
redirect_uri=<リダイレクトURI>
code_verifier=<PKCEシークレット>
client_id=<クライアントID>
client_secret=<クライアントシークレット>
end note
note over par_endpoint, token_endpoint
1. DPoP署名検証
2. DPoPクレーム(ヘッダ・ペイロード内容)検証
end note
note over par_endpoint, token_endpoint
1.PKCEシークレットからハッシュ計算
2.最初のリクエストのPKCEハッシュと比較検証
end note
note over par_endpoint, token_endpoint
アクセストークン関連生成
{
token_type: Bearer
access_token: <アクセストークン>
refresh_token: <リフレッシュトークン>
expires_in: <トークン有効期限>
id_token: <(OIDCの場合)IDトークン>
}
end note
spa <-- token_endpoint : アクセストークン応答\n(リフレッシュトークン内包)
note over spa
1. DPoP証明生成(ヘッダ・ペイロード)
2. DPoP証明ヘッダ・ペイロードをそれぞれbase64エンコード
3. 署名無しトークン生成 <base64 DPoPヘッダ>.<base64 DPoPペイロード>
4. 署名無しトークンを電子署名し署名生成
5. 署名をbase64エンコード
end note
spa -> resource
note left
Authorization: <アクセストークン>
DPoP: <base64 DPoPヘッダ>.<base64 DPoPペイロード>.<base64署名>
end note
note over resource
1. DPoP署名検証
2. DPoPクレーム(ヘッダ・ペイロード内容)検証
end note
note over resource
アクセストークン検証
(イントロスペクション:ローカル/リモート)
end note
spa <-- resource : 対象リソースレスポンス
ua <-- spa : アクセスリクエスト(操作)に沿った画面レンダリング
user <-- ua : アクセスリクエスト(操作)結果表示
== アクセストークン有効期間中のリクエスト ==
user -> ua : アクセストークン対象リソースを含む\nアプリ利用操作
ua -> spa : アクセストークン対象リソースを含む\nアプリ操作リクエスト
note over spa
1. DPoP証明生成(ヘッダ・ペイロード)
2. DPoP証明ヘッダ・ペイロードをそれぞれbase64エンコード
3. 署名無しトークン生成 <base64 DPoPヘッダ>.<base64 DPoPペイロード>
4. 署名無しトークンを電子署名し署名生成
5. 署名をbase64エンコード
end note
spa -> resource
note left
Authorization: <アクセストークン>
DPoP: <base64 DPoPヘッダ>.<base64 DPoPペイロード>.<base64署名>
end note
note over resource
1. DPoP署名検証
2. DPoPクレーム(ヘッダ・ペイロード内容)検証
end note
note over resource
アクセストークン検証
(イントロスペクション:ローカル/リモート)
end note
spa <-- resource : 対象リソースレスポンス
ua <-- spa : アプリ利用操作リクエストに沿った応答
user <-- ua : アプリ利用操作結果表示
... トークン有効期間 ...
== リフレッシュトークン ==
note over spa
1. DPoP証明生成(ヘッダ・ペイロード)
2. DPoP証明ヘッダ・ペイロードをそれぞれbase64エンコード
3. 署名無しトークン生成 <base64 DPoPヘッダ>.<base64 DPoPペイロード>
4. 署名無しトークンを電子署名し署名生成
5. 署名をbase64エンコード
end note
spa -> token_endpoint : アクセストークン更新リクエスト
note left
Host: <アクセストークンエンドポイントサーバー>
DPoP: <base64 DPoPヘッダ>.<base64 DPoPペイロード>.<base64署名>
grant_type=refresh_token
refresh_token=<リフレッシュトークン>
client_id=<クライアントID>
client_secret=<クライアントシークレット>
end note
note over par_endpoint, token_endpoint
1. DPoP署名検証
2. DPoPクレーム(ヘッダ・ペイロード内容)検証
end note
note over par_endpoint, token_endpoint
リフレッシュトークン検証
end note
note over par_endpoint, token_endpoint
新アクセストークン関連生成
end note
spa <-- token_endpoint : 新アクセストークン応答\n(新リフレッシュトークン内包)
@enduml
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment