授權碼與 PKCE 開發人員

上次更新:2026年8月16日

授權碼與 PKCE

透過入口網站建立的應用程式使用 Authorization Code + PKCE(S256) 流程。瀏覽器僅接收短期有效的授權碼 codecode_verifier 須在可信環境中保存並用於換取權杖。機密用戶端另需在後端使用 client_secret;JWT 斷言用戶端另需在後端簽發 client_assertion

下文以正式 Issuer 為例:

ISSUER=https://api-passport.swaymoon.com

實際端點請以 {ISSUER}/.well-known/openid-configuration 返回的探索文件為準。

1. 產生 PKCE

  1. 產生高熵的 code_verifier(43–128 個 URL 安全字元)。
  2. code_challenge = BASE64URL(SHA256(code_verifier))(無填充)。
  3. code_challenge_method = S256

code_verifierstate 存入伺服器端工作階段、加密 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. 使用授權碼換取權杖

向權杖端點發起 POSTapplication/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_verifierstate
  • 機密用戶端:僅在後端使用 client_secret 換取權杖。
  • JWT 斷言用戶端:僅在後端簽發 client_assertion;私鑰與 kid 必須與入口登記的公鑰對應。
  • 非機密用戶端:切勿編造或硬編碼 client_secret;依賴 PKCE 與登記過的 Redirect URI。
  • 使用 HTTPS 回呼與傳輸。
  • 限制 scope 為最小必要集。
  • 妥善處理錯誤:invalid_grantinvalid_clientinvalid_request 等。

下一步:閱讀《權杖與使用者資訊》,完成權杖校驗和本機使用者建檔。