使用者流程(authorization code + PKCE)
使用者本人在你的應用裡操作自己的資料時走這條。發出的 token 是 profile self。
v1 沒有代理。 「A 代表 B 操作」的
delegatedprofile 是 v2 規劃中的範圍,本版不提供。不要用selftoken 加上自訂 header 假裝代理——平台不會承認。
流程
- 產生
code_verifier(43–128 字元的高熵隨機字串)與code_challenge = BASE64URL(SHA256(code_verifier))。 - 把使用者導到授權端點:
https://auth.lightup.tech/oauth2/authorize
?response_type=code
&client_id=tp_example_app
&redirect_uri=<登記過的精確值>
&scope=lightup.profile.self.read lightup.person.metadata.read
&state=<你產生的隨機值>
&code_challenge=<challenge>
&code_challenge_method=S256
- 使用者登入並在同意頁核准。同意頁顯示應用名稱、發布者、請求的能力(用目錄裡的說明文字,不是 raw scope 字串),以及撤銷入口。
- 平台導回你的
redirect_uri,帶code與state。先比對state,不符就中止。 - 用
code換 token:
POST https://auth.lightup.tech/oauth2/token,application/x-www-form-urlencoded。
| 欄位 | 必填 | 說明 |
|---|---|---|
grant_type |
必填 | 固定 authorization_code |
client_id |
必填 | 第三方 client id,形狀 tp_<tenant label>_<slug>,由 amygdala 配發。 |
client_secret |
選填 | confidential client 才有;public client 不送。 |
code |
必填 | /oauth2/authorize 導回時帶的一次性授權碼。用過就失效。 |
code_verifier |
必填 | PKCE verifier,43–128 字元。授權請求送的是它的 S256 雜湊 (code_challenge),plain 不支援。 |
redirect_uri |
必填 | 必須與授權請求送出的那一個完全相同,且已登記在 client 上。 |
code 與 refresh_token 都是一次性的:重送同一個 code 會得到 invalid_grant,
逾時之後不要盲目重試,重新走一次流程。
續期
POST https://auth.lightup.tech/oauth2/token,application/x-www-form-urlencoded。
| 欄位 | 必填 | 說明 |
|---|---|---|
grant_type |
必填 | 固定 refresh_token |
client_id |
必填 | 第三方 client id,形狀 tp_<tenant label>_<slug>,由 amygdala 配發。 |
client_secret |
選填 | confidential client 才有。 |
refresh_token |
必填 | 上一次回應給的那一張。不透明字串,不要解析。 |
scope |
選填 | 可選,只能縮小原本的 scope,不能放大。省略就沿用原本的。 |
token 端點的錯誤
錯誤狀態:400、401、429。
body 是 OAuth 既有的 TokenError 形狀(error + error_description),
不是本 API 其他端點的 Problem——不要把兩者放進同一個解析路徑。
error |
意思 |
|---|---|
invalid_client |
client id/secret 不對,或 client 已停用 |
invalid_grant |
code/refresh token 無效、過期或已用過;PKCE code_verifier 不符 |
invalid_scope |
請求的 scope 不在 client 允許範圍、tenant grant 或方案上限內 |
unsupported_grant_type |
這個 grant 沒開。client_credentials 今天就是這個答案 |
PKCE 是必填
- public client(行動/桌面/SPA)強制 PKCE S256。
- 第三方 confidential client 也必填 PKCE,不吃第一方「confidential 可以不帶 PKCE」的相容路徑。
- 只接受
S256,不接受plain。
Redirect URI
- 一律精確比對。不支援子網域萬用字元。
- 原生應用依 RFC 8252 使用系統瀏覽器加自訂 scheme 或 loopback(
http://127.0.0.1:<port>/...),不要用內嵌 WebView 收 code。
Token 與 refresh
| 項目 | 值 |
|---|---|
| profile | self |
sub |
使用者的 psn_… |
actor.accountId |
使用者的 acc_… |
| TTL | 15 分鐘 |
| refresh | 有,嚴格 rotation |
嚴格 rotation 的意思:每次用 refresh token 都會拿到新的一張,舊的立刻作廢。重放一張已經用掉的 refresh token 會殺掉整個 token family——使用者要重新授權。第三方不會拿到 reuse grace、sliding 或 absolute-max 這些放寬。
實務上:同一個使用者的 refresh 呼叫要序列化,不要讓多個執行緒或多台機器同時拿同一張 refresh token 去換。
ID token 不是 access token
登入拿到的 ID token 只用來在你的應用裡確認使用者身分。拿 ID token 呼叫 API 會被擋在第一關(JOSE header typ 不是 at+jwt),回 401 invalid_token。同理,第一方 storefront token 與內部服務的共用 header 也一律 401。
scope 的三層交集
有效 scope = 租戶管理員安裝時核准的能力集合
∩ client 的 allowed_scopes
∩ 使用者當次同意的項目
缺一不可。使用者可以只同意一部分;請以 token 回應裡的 scope 為準,並在少了能力時降級功能,而不是重試。