
用法
- 適用於 Full-Stack Web 應用程式,例如:
- 使用 React/Vue/Angular 前端 + Express/Django/Spring Boot 後端
- 第三方 OAuth 登入(例如 Google、Facebook 登入)
- 透過 兩個 API 請求 獲取 access token,提高安全性:
- 先取得 授權碼(authorization code)
- 再交換 access token
流程
- 用戶請求登入:客戶端(Web 應用)引導用戶到 OAuth 授權伺服器進行身份驗證。
- 獲取授權碼:用戶授權後,OAuth 伺服器將授權碼(authorization code) 傳回給客戶端(透過 redirect URL)。
- 交換 access token:
- 客戶端(後端)使用 授權碼 + client secret,向 OAuth 伺服器請求 access token。
- OAuth 伺服器回應 access token,之後客戶端可用該 token 存取 API。
示例
- Google OAuth 登入:
- 用戶透過 Google 登入你的網站
- 你的伺服器用授權碼換 access token
- 伺服器用 access token 請求 Google API 來獲取用戶資料
👉 變體:Authorization Code Flow + PKCE
- 適用於無後端的前端應用(SPA),透過 PKCE(Proof Key for Code Exchange)機制防止攻擊者竊取授權碼。
Use usemodernfullstack.dev to be Authorization Server. (Role: like Google, Facebook, and Github)

//1: Requests authorization
curl -i -X POST 'https://www.usemodernfullstack.dev/oauth/authenticate' -H 'Accept: text/html' -H 'Content-Type: application/x-www-form-urlencoded' -d "response_type=code&client_id=client-1739417575775&state=4nBjkh31&scope=read&redirect_uri=http://localhost:3000/oauth/callback&username=fluber2&password=helloworld"
//2: Grants authorization
HTTP/2 302
x-powered-by: Express
access-control-allow-origin: *
location: http://localhost:3000/oauth/callback?code=dbee77d01e15bcaac3009ab3c6e6e97a9083f9a0&state=4nBjkh31
vary: Accept
content-type: text/html; charset=utf-8
content-length: 246
set-cookie: connect.sid=s%3Ac2l1dQ3F2j-UHfwObb-JjSQeMwG9erda.hXJXES%2FJT%2FsLwWZV7ovYPC0Rhjjl5Ajr1MkpnFwPlr4; Path=/; Expires=Thu, 13 Feb 2025 06:14:42 GMT; HttpOnly
date: Thu, 13 Feb 2025 06:13:42 GMT
server: Fly/12401d8b4 (2025-02-12)
via: 2 fly.io
fly-request-id: 01JKYZ24B2GF7Z6FB65T0FQHYQ-syd
<p>Found. Redirecting to <a href="http://localhost:3000/oauth/callback?code=dbee77d01e15bcaac3009ab3c6e6e97a9083f9a0&state=4nBjkh31">http://localhost:3000/oauth/callback?code=dbee77d01e15bcaac3009ab3c6e6e97a9083f9a0&state=4nBjkh31</a></p>
//3: Sends authorization grant
curl -i -X POST 'https://www.usemodernfullstack.dev/oauth/access_token' -H 'Accept: text/html, application/json' -H 'Content-Type: application/x-www-form-urlencoded' -d "code=dbee77d01e15bcaac3009ab3c6e6e97a9083f9a0&grant_type=authorization_code&redirect_uri=http://localhost:3000/oauth/callback&client_id=client-1739417575775&client_secret=3d74f376cae2e26312e71341bb944210"
//4: Receives access token
HTTP/2 200
x-powered-by: Express
access-control-allow-origin: *
cache-control: no-store
pragma: no-cache
content-type: application/json; charset=utf-8
content-length: 173
etag: W/"ad-1GbOkDAOzuQJOho/9JcjWDyb3oU"
set-cookie: connect.sid=s%3A9RnMM-khc96jttatJZWl2a2mTyucpv9n.YkxEOz3%2Bv3Xb6Fr%2FSIibFgFf47uE9EHXntC6S4XWqGE; Path=/; Expires=Thu, 13 Feb 2025 06:18:54 GMT; HttpOnly
date: Thu, 13 Feb 2025 06:17:54 GMT
server: Fly/12401d8b4 (2025-02-12)
via: 2 fly.io
fly-request-id: 01JKYZ9T8AKD4CVSSRF2A9SFV8-syd
{"access_token":"0242a900f819005c312a9032dc23cc4506afd865","token_type":"Bearer","expires_in":3599,"refresh_token":"42926b8aa369f38de9404547882221196f17b622","scope":"read"}
//5: Sends access token
curl -i -X GET 'https://www.usemodernfullstack.dev/protected/resource' -H 'Accept: text/html' -H 'Authorization: Bearer 0242a900f819005c312a9032dc23cc4506afd865'
//6: Grants access to resource
HTTP/2 200
x-powered-by: Express
access-control-allow-origin: *
content-type: text/html; charset=utf-8
content-length: 7327
etag: W/"1c9f-HM1QaqS+x1cmi1UHCf7AWM2XJSo"
set-cookie: connect.sid=s%3AxIL1OwVJ7efXncnOExh8XkO2Gb5PGu56.h1a3Q5%2FeMKfYqtH0RFBbcaFzl9eZYpoH8eOL0x2kszg; Path=/; Expires=Thu, 13 Feb 2025 06:19:47 GMT; HttpOnly
date: Thu, 13 Feb 2025 06:18:47 GMT
server: Fly/12401d8b4 (2025-02-12)
via: 2 fly.io
fly-request-id: 01JKYZBERAMMQPFJ6GV1PMNEDV-syd
<html>
--snip--
<h1>This page is secured.</h1>
--snip--
</body>
</html>