授權碼與 PKCE 開發人員
上次更新:2026年8月16日
授權碼與 PKCE
透過入口網站建立的應用程式使用 Authorization Code + PKCE(S256) 流程。瀏覽器僅接收短期有效的授權碼 code;code_verifier 須在可信環境中保存並用於換取權杖。機密用戶端另需在後端使用 client_secret;JWT 斷言用戶端另需在後端簽發 client_assertion。
下文以正式 Issuer 為例:
ISSUER=https://api-passport.swaymoon.com
實際端點請以 {ISSUER}/.well-known/openid-configuration 返回的探索文件為準。
1. 產生 PKCE
- 產生高熵的
code_verifier(43–128 個 URL 安全字元)。 code_challenge = BASE64URL(SHA256(code_verifier))(無填充)。code_challenge_method = S256。
將 code_verifier 和 state 存入伺服器端工作階段、加密 Cookie,或非機密用戶端可用的安全工作階段儲存;避免將其洩漏給第三方指令碼。
2. 引導使用者授權
建構並重新導向到授權端點,例如:
GET /oauth2/authorize?
response_type=code
&client_id=swm_xxxxxxxx
&redirect_uri=https%3A%2F%2Fapp.example.com%2Fcallback
&scope=openid%20name%20picture%20email
&state=RANDOM_STATE
&code_challenge=CHALLENGE
&code_challenge_method=S256
Host: api-passport.swaymoon.com
| 參數 | 要求 |
|---|---|
response_type | 固定 code |
client_id | 由開發者入口網站發放 |
redirect_uri | 必須已登記且完全一致 |
scope | 空格分隔;至少 openid |
state | 用於防止 CSRF;回呼時須校驗其是否與原值一致 |
code_challenge / code_challenge_method | 必填,S256 |
使用者將在 passport.swaymoon.com 完成登入(如有需要)和授權確認。成功後,瀏覽器將跳轉至:
https://app.example.com/callback?code=...&state=...
如果使用者取消授權,應用程式通常不會收到可用的 code,應按照返回的錯誤或取消參數進行處理。
3. 使用授權碼換取權杖
向權杖端點發起 POST(application/x-www-form-urlencoded)。
機密用戶端
在後端使用 HTTP Basic:
POST /oauth2/token
Host: api-passport.swaymoon.com
Authorization: Basic BASE64(urlencode(client_id):urlencode(client_secret))
Content-Type: application/x-www-form-urlencoded
grant_type=authorization_code
&code=AUTHORIZATION_CODE
&redirect_uri=https://app.example.com/callback
&code_verifier=VERIFIER
非機密用戶端
不要傳送 Basic 或 client_secret。在表單中攜帶 client_id:
POST /oauth2/token
Host: api-passport.swaymoon.com
Content-Type: application/x-www-form-urlencoded
grant_type=authorization_code
&code=AUTHORIZATION_CODE
&redirect_uri=https://app.example.com/callback
&client_id=swm_xxxxxxxx
&code_verifier=VERIFIER
JWT 斷言用戶端
不要傳送 Basic 或 client_secret。在後端用登記公鑰對應的私鑰簽發 client_assertion,並放入表單:
POST /oauth2/token
Host: api-passport.swaymoon.com
Content-Type: application/x-www-form-urlencoded
grant_type=authorization_code
&code=AUTHORIZATION_CODE
&redirect_uri=https://app.example.com/callback
&client_id=swm_xxxxxxxx
&code_verifier=VERIFIER
&client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer
&client_assertion=SIGNED_JWT
client_assertion 是一枚 ES256 簽章的 JWT。JWS 頭須含與入口登記公鑰一致的 kid。載荷聲明:
| 聲明 | 值 |
|---|---|
iss | 你的 client_id |
sub | 你的 client_id(與 iss 相同) |
aud | 探索文件中的 token_endpoint(正式環境一般為 https://api-passport.swaymoon.com/oauth2/token) |
jti | 每次請求使用新的唯一值 |
iat | 簽發時間 |
exp | 過期時間(建議數分鐘內,且必須在未來) |
私鑰只留在伺服器。入口拒絕接受私鑰 JWK。可執行的 curl / Python 片段見《範例與疑難排解》。
說明:
redirect_uri必須與授權請求中的值一致。code_verifier必須與授權時提交的code_challenge對應。- 授權碼只能使用一次,且有效期較短。
成功回應(欄位以實際為準)範例:
{
"access_token": "...",
"refresh_token": "...",
"id_token": "...",
"token_type": "Bearer",
"expires_in": 300,
"scope": "openid name picture email"
}
- 機密用戶端與 JWT 斷言用戶端通常會收到
refresh_token。請安全儲存所有權杖,不要將client_secret、私鑰或 Refresh Token 下發至不可信的前端環境。 - 非機密用戶端回應中沒有
refresh_token。Access Token 過期後,請重新發起授權碼 + PKCE 流程。
4. 重新整理存取權杖(機密與 JWT 斷言)
Access Token 過期後,機密用戶端:
POST /oauth2/token
Authorization: Basic ...
Content-Type: application/x-www-form-urlencoded
grant_type=refresh_token
&refresh_token=REFRESH_TOKEN
JWT 斷言用戶端重新整理時同樣不要使用 Basic,改為再簽一枚新的 client_assertion:
POST /oauth2/token
Host: api-passport.swaymoon.com
Content-Type: application/x-www-form-urlencoded
grant_type=refresh_token
&refresh_token=REFRESH_TOKEN
&client_id=swm_xxxxxxxx
&client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer
&client_assertion=SIGNED_JWT
重新整理成功後,請使用新的 access_token,以及回應中可能輪換的 refresh_token。在權杖輪換策略下,舊的 Refresh Token 可能立即失效,請始終以最新回應為準。
非機密用戶端不支援此步驟。
5. 使用者體驗要點
- 未登入使用者會先進入通行帳戶登入頁,再進入授權確認流程。
- 敏感操作可能觸發再次登入(step-up authentication),這是正常的安全機制。
- 同意頁文案與選用權限勾選見《權限與同意》。
6. 安全清單
- 始終保存並校驗
code_verifier和state。 - 機密用戶端:僅在後端使用
client_secret換取權杖。 - JWT 斷言用戶端:僅在後端簽發
client_assertion;私鑰與kid必須與入口登記的公鑰對應。 - 非機密用戶端:切勿編造或硬編碼
client_secret;依賴 PKCE 與登記過的 Redirect URI。 - 使用 HTTPS 回呼與傳輸。
- 限制
scope為最小必要集。 - 妥善處理錯誤:
invalid_grant、invalid_client、invalid_request等。
下一步:閱讀《權杖與使用者資訊》,完成權杖校驗和本機使用者建檔。