
2024年最佳天氣API
"family_name": "Silverman",
"given_name": "Micah",
"locale": "en-US",
"name": "Micah Silverman",
"preferred_username": "micah.silverman@okta.com",
"sub": "00u9vme99nxudvxZA0h7",
"updated_at": 1490198843,
"zoneinfo": "America/Los_Angeles"
}
上面包含了一些聲明profile
。這是因為對用戶信息的請求是使用通過范圍獲得的令牌進行的profile
。換句話說,發出的請求會導致發出令牌。該令牌包含基于原始請求中指定的范圍的某些信息。
使用 OIDC 時,您會聽到各種“流程”的討論。這些流程用于描述不同的常見身份驗證和授權場景。考慮因素包括應用程序的類型(如基于 Web 或原生移動應用程序)、您希望如何驗證令牌(在應用程序中還是在后端),以及您希望如何訪問其他身份信息(進行另一個 API 調用或將其直接編碼到令牌中)。
主要有三種流程:授權碼、隱式和混合response_type
。這些流程由請求中的查詢參數控制/authorization
。在考慮使用哪種流程時,請考慮前向通道與后向通道的要求。前向通道是指直接與 OpenID 提供商 (OP) 交互的用戶代理(例如 SPA 或移動應用程序)。當需要前向通道通信時,隱式流程是一個不錯的選擇。后向通道是指與 OP 交互的中間層客戶端(例如 Spring Boot 或 Express)。當需要后向通道通信時,授權碼流程是一個不錯的選擇。
授權碼流程使用response_type=code
。身份驗證成功后,響應將包含一個code
值。此代碼稍后可以交換為 和access_token
(id_token
請稍等,稍后我們將更深入地討論令牌。)當架構中包含“中間件”時,此流程非常有用。中間件具有 和client id
,client secret
這是code
通過點擊/token
端點將 交換為令牌所必需的。然后可以將這些令牌返回給最終用戶應用程序(例如瀏覽器),而無需瀏覽器知道client secret
。此流程允許通過使用 實現長壽命會話refresh tokens
。 的唯一目的refresh tokens
是獲取新的access tokens
以延長用戶會話。
隱式流程使用response_type=id_token token
或response_type=id_token
。身份驗證成功后,響應將在第一種情況下包含id_token
和,在第二種情況下僅包含 。當您的應用程序直接與后端對話以獲取沒有中間件的令牌時,此流程很有用。它不支持長壽命會話。access_token
id_token
混合流將上述兩種流以不同的組合方式組合在一起 – 以適合用例的方式。例如response_type=code id_token
。此方法可實現一種場景,您可以在應用程序中擁有一個長壽命會話,并立即從/authorization
端點獲取令牌。
有了范圍、聲明和響應類型的基礎,我們現在可以討論令牌了!OIDC 中有三種類型的令牌:id_token
、access_token
和refresh_token
。
根據OIDC 規范, Anid_token
是JWT。這意味著:
規范中有一組規則id_token
用于驗證。 在 中編碼的聲明中id_token
有一個到期日期 ( exp
),必須在驗證過程中遵守。 此外,JWT 的簽名部分與密鑰一起使用,以驗證整個 JWT 未以任何方式被篡改。
訪問令牌用作不記名令牌。不記名令牌意味著不記名令牌持有者無需進一步識別即可訪問授權資源。因此,保護不記名令牌非常重要。如果我能以某種方式獲得并“持有”您的訪問令牌,我就可以偽裝成您。
為了提高安全性,這些令牌通常具有較短的使用壽命(由其到期時間決定)。也就是說,當訪問令牌到期時,用戶必須再次進行身份驗證才能獲得新的訪問令牌,從而限制其為不記名令牌這一事實的暴露。
盡管 OIDC 規范沒有強制要求,Okta 使用 JWT 作為訪問令牌,因為(除其他外)到期時間內置于令牌中。
OIDC 指定/userinfo
返回身份信息且必須受到保護的端點。出示訪問令牌即可使端點可訪問。
以下是使用HTTPie的示例:
http https://micah.oktapreview.com/oauth2/.../v1/userinfo
HTTP/1.1 400 Bad Request
...
WWW-Authenticate: Bearer error="invalid_request", error_description="The access token is missing."
...
讓我們使用過期的訪問令牌再試一次:
http https://micah.oktapreview.com/oauth2/.../v1/userinfo \
Authorization:"Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6Ik93bFNJS3p3Mmt1Wk8zSmpnMW5Dc2RNelJhOEV1elY5emgyREl6X3RVRUkifQ..."
HTTP/1.1 401 Unauthorized
...
WWW-Authenticate: Bearer error="invalid_token", error_description="The token has expired."
...
最后,讓我們嘗試使用有效的訪問令牌:
http https://micah.oktapreview.com/oauth2/.../v1/userinfo \
Authorization:"Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6Ik93bFNJS3p3Mmt1Wk8zSmpnMW5Dc2RNelJhOEV1elY5emgyREl6X3RVRUkifQ..."
HTTP/1.1 200 OK
...
{
"family_name": "Silverman",
"given_name": "Micah",
"groups": [
"ABC123",
"Everyone"
],
"locale": "en-US",
"name": "Micah Silverman",
"preferred_username": "micah+okta@afitnerd.com",
"sub": "...",
"updated_at": 1490198843,
"zoneinfo": "America/Los_Angeles"
}
刷新令牌用于獲取新的訪問令牌。通常,刷新令牌是長期有效的,而訪問令牌是短期有效的。這允許長期存在的會話在必要時可以被終止。以下是一個典型的場景:
你可能會問:為什么要這樣做?這種方法在用戶體驗和安全性之間取得了平衡。想象一下,如果用戶以某種方式受到威脅。或者,他們的訂閱到期了。或者,他們被解雇了。在任何時候,管理員都可以撤銷刷新令牌。然后,上面的第三步將失敗,用戶將被迫(嘗試)通過身份驗證建立新會話。如果他們的帳戶已被暫停,他們將無法進行身份驗證。
區分不同的 token 類型有時會令人困惑。以下是快速參考:
您從 OIDC 流中返回的令牌和/userinfo
端點的內容取決于請求的流類型和范圍。您可以在OIDC 流測試站點scope
上實時查看。在這里,您可以為和設置不同的切換response_type
,這決定了您的應用的流類型。
您的用例將決定使用哪種流程。您是否正在構建需要直接與 OpenID 提供程序 (OP) 交互的 SPA 或移動應用程序?您是否有可以與 OP 交互的中間件,例如 Spring Boot 或 Node.js Express?下面,我們將深入探討一些可用的流程以及何時適合使用它們。
如果您有一個連接到 OIDC OP 的中間件客戶端,并且(不一定)希望令牌返回到最終用戶應用程序(例如瀏覽器),則這種方法很合適。這也意味著最終用戶應用程序永遠不需要知道密鑰。
以下是使用 Okta 開始此流程的示例:
https://micah.okta.com/oauth2/aus2yrcz7aMrmDAKZ1t7/v1/authorize?client_id=0oa2yrbf35Vcbom491t7&response_type=code&scope=openid&state=little-room-greasy-pie&nonce=b1e7b75d-6248-4fc7-bad0-ac5ae0f2e581&redirect_uri=https%3A%2F%2Fokta-oidc-fun.herokuapp.com%2Fflow_result
讓我們詳細分析一下:
鑰匙 | 價值 | 描述 |
組織 URL | https://micah.okta.com | Okta 租戶 |
授權網址 | /oauth2/aus2yrcz7aMrmDAKZ1t7/v1/授權 | 貴組織的默認授權端點 |
客戶端 ID | 0oa2yrbf35Vcbom491t7 | Okta 中定義的 OIDC 應用程序的客戶端 ID |
響應類型 | 代碼 | 指示代碼流的響應類型 |
范圍 | 開放標識 | openid 范圍是必需的 |
狀態 | 小房間油膩餡餅 | 流程結束時返回隨機值 |
隨機數 | b1e7b75d-6248-4fc7-bad0-ac5ae0f2e581 | 編碼到 id_token 中的隨機值,用于稍后驗證 |
重定向 uri | https%3A%2F%2Fokta-oidc-fun.herokuapp.com%2Fflow_result | OP 重定向到的 URL 編碼 URL |
瀏覽器中的情況如下:
請注意,在新屏幕上,您將被重定向回redirect_uri
最初指定的頁面:
在后臺,會使用固定的用戶名和密碼建立會話。則當您單擊鏈接時,您將被重定向到登錄,然后重定向回同一頁面。
在上面的屏幕截圖中,您可以看到返回的代碼和原始的state
。
現在,中間層(在本例中為 Spring Boot 應用程序)可以將該代碼交換為id_token
和。這個中間層將驗證我們之前在授權請求中發送的狀態,并使用客戶端密鑰發出請求,為用戶創建和。access_token
/token
access_token
id_token
當您想要直接與 OIDC OP 交互的客戶端(例如單頁應用程序或移動應用程序)工作時,這是一種合適的方法。
以下是使用 Okta 開始此流程的示例:
https://micah.okta.com/oauth2/aus2yrcz7aMrmDAKZ1t7/v1/authorize?client_id=0oa2yrbf35Vcbom491t7&response_type=id_token+token&scope=openid&state=shrill-word-accessible-iron&nonce=f8c658f0-1eb9-4f8d-8692-5da4e2f24cf0&redirect_uri=https%3A%2F%2Fokta-oidc-fun.herokuapp.com%2Fflow_result
它與授權碼流程幾乎相同,只是 是response_type
或id_token
。token
下面id_token+token
,我們將詳細介紹這些令牌中的內容及其驅動方式,但請記住:id_token
編碼身份信息,access_token
(如果token
指定 則返回)是用于訪問資源的承載令牌。Okta 還使用 JWT 進行access_token
,這樣可以將其他信息編碼到其中。
以下是瀏覽器中的流程:
您將被重定向回redirect_uri
最初指定的位置(帶有返回的令牌和原始的state
):
應用程序現在可以在id_token
本地驗證。使用/introspect
端點來驗證access_token
。它還可以使用access_token
作為承載令牌來訪問受保護的資源,例如/userinfo
端點。
當您希望最終用戶應用程序能夠立即訪問短期令牌(例如id_token
身份信息),并且還希望使用后端服務通過刷新令牌將授權碼交換為更長期令牌時,這是一種合適的方法。
它是授權代碼和隱式代碼流的組合。您可以通過查看它response_type
必須包含和code
中的一個或兩個來發現它id_token
:token
https://micah.okta.com/oauth2/aus2yrcz7aMrmDAKZ1t7/v1/authorize?client_id=0oa2yrbf35Vcbom491t7&response_type=code+id_token+token&scope=openid&state=shrill-word-accessible-iron&nonce=f8c658f0-1eb9-4f8d-8692-5da4e2f24cf0&redirect_uri=https%3A%2F%2Fokta-oidc-fun.herokuapp.com%2Fflow_result
瀏覽器中的情況如下:
您將被重定向回redirect_uri
最初指定的頁面(包含返回的代碼、令牌和原始內容state
):
在下一篇文章中,我們將深入研究如何控制這些令牌中的內容,但現在先來介紹一下:
這些令牌是在啟用所有默認范圍的混合流下產生的。
/userinfo
以下是使用access_token
作為承載令牌的端點的響應:
{
"sub": "00u2yulup4eWbOttd1t7",
"name": "Okta OIDC Fun",
"locale": "en-US",
"email": "okta_oidc_fun@okta.com",
"preferred_username": "okta_oidc_fun@okta.com",
"given_name": "Okta OIDC",
"family_name": "Fun",
"zoneinfo": "America/Los_Angeles",
"updated_at": 1499922371,
"email_verified": true
}
根據 OIDC 規范,與身份相關的信息有兩個主要來源。一個來源是編碼到id_token
JWT中的信息。另一個來源是來自端點的響應/userinfo
,可以使用access_token
不記名令牌訪問。在 Okta,我們也選擇將訪問令牌設為 JWT,這提供了第三個信息來源。(您會在許多 OIDC 實現中看到這一點。)
請求中有很多查詢參數組合/authorization
,它們決定了哪些信息將被編碼到 中。影響最終在返回的令牌和端點id_token
中找到的內容的兩個查詢參數是和。/userinfo
response_type
scope
目前,我們將把scope
重點放在一邊response_type
。在下面的例子中,我們只使用范圍openid
(必需)和email
。我們還將使用隱式流,因為它會立即返回令牌。
給出這個請求:
https://micah.okta.com/oauth2/aus2yrcz7aMrmDAKZ1t7/v1/authorize?client_id=0oa2yrbf35Vcbom491t7&response_type=token&scope=openid+email&state=aboard-insect-fresh-smile&nonce=c96fa468-ca1b-46f0-8974-546f23f9ee6f&redirect_uri=https%3A%2F%2Fokta-oidc-fun.herokuapp.com%2Fflow_result
請注意,這response_type=token
將產生一個access_token
。OIDC 規范中對訪問令牌沒有特定的格式要求,但在 Okta 中我們使用 JWT。查看返回的令牌,我們會看到:
{
"active": true,
"scope": "openid email",
"username": "okta_oidc_fun@okta.com",
"exp": 1501531801,
"iat": 1501528201,
"sub": "okta_oidc_fun@okta.com",
"aud": "test",
"iss": "https://micah.okta.com/oauth2/aus2yrcz7aMrmDAKZ1t7",
"jti": "AT.upPJqU-Ism6Fwt5Fpl8AhNAdoUeuMsEgJ_VxJ3WJ1hk",
"token_type": "Bearer",
"client_id": "0oa2yrbf35Vcbom491t7",
"uid": "00u2yulup4eWbOttd1t7"
}
這主要是資源信息,包括到期日期(exp
)和用戶 ID(uid
)。
如果我們想要獲取用戶的身份/userinfo
信息,我們必須使用作為承載令牌的端點。使用HTTPieaccess_token
的情況如下:
http https://micah.okta.com/oauth2/aus2yrcz7aMrmDAKZ1t7/v1/userinfo Authorization:"Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6Ik93bFNJS3p3Mmt1Wk8zSmpnMW5Dc2RNelJhOEV1elY5emgyREl6X3RVRUkifQ..."
HTTP/1.1 200 OK
...
{
"sub": "00u2yulup4eWbOttd1t7",
"email": "okta_oidc_fun@okta.com",
"email_verified": true
}
我們返回了sub
、email
和email_verified
聲明。這是因為scope=openid+email
原始請求的默認值。我們將在范圍部分查看一些更詳細的響應。
我們來嘗試另一個請求:
https://micah.okta.com/oauth2/aus2yrcz7aMrmDAKZ1t7/v1/authorize?client_id=0oa2yrbf35Vcbom491t7&response_type=id_token&scope=openid+email&state=aboard-insect-fresh-smile&nonce=c96fa468-ca1b-46f0-8974-546f23f9ee6f&redirect_uri=https%3A%2F%2Fokta-oidc-fun.herokuapp.com%2Fflow_result
這次,我使用 請求 ID 令牌response_type=id_token
。響應是 JWT(符合 OIDC 規范的要求),其中包含以下信息:
{
"sub": "00u2yulup4eWbOttd1t7",
"email": "okta_oidc_fun@okta.com",
"ver": 1,
"iss": "https://micah.okta.com/oauth2/aus2yrcz7aMrmDAKZ1t7",
"aud": "0oa2yrbf35Vcbom491t7",
"iat": 1501528456,
"exp": 1501532056,
"jti": "ID.4Mmzy2kj5_B8nGZ_PT4dt8-fzu1tA2W3C5dbEF-N6Us",
"amr": [
"pwd"
],
"idp": "00o1zyyqo9bpRehCw1t7",
"nonce": "c96fa468-ca1b-46f0-8974-546f23f9ee6f",
"email_verified": true,
"auth_time": 1501528157
}
請注意,我們將sub
和email
聲明直接編碼在 JWT 中。在這種類型的隱式流程中,我們沒有針對/userinfo
端點使用的承載令牌,因此身份信息直接嵌入到 JWT 中。
最后我們來看看最后一類隱式流:
https://micah.okta.com/oauth2/aus2yrcz7aMrmDAKZ1t7/v1/authorize?client_id=0oa2yrbf35Vcbom491t7&response_type=id_token+token&scope=openid+email&state=aboard-insect-fresh-smile&nonce=c96fa468-ca1b-46f0-8974-546f23f9ee6f&redirect_uri=https%3A%2F%2Fokta-oidc-fun.herokuapp.com%2Fflow_result
在這里,我們請求響應中的id_token
和。access_token
我們的access_token
主張與以前相同。的id_token
主張如下:
{
"sub": "00u2yulup4eWbOttd1t7",
"email": "okta_oidc_fun@okta.com",
"ver": 1,
"iss": "https://micah.okta.com/oauth2/aus2yrcz7aMrmDAKZ1t7",
"aud": "0oa2yrbf35Vcbom491t7",
"iat": 1501528536,
"exp": 1501532136,
"jti": "ID.fyybPizTmYLoQR20vlR7mpo8WTxB7JwkxplMQom-Kf8",
"amr": [
"pwd"
],
"idp": "00o1zyyqo9bpRehCw1t7",
"nonce": "c96fa468-ca1b-46f0-8974-546f23f9ee6f",
"auth_time": 1501528157,
"at_hash": "T7ij7o69gBtjo6bAJvaVBQ"
}
請注意,這次的信息較少id_token
(在本例中,沒有email_verified
聲明)。因為我們還請求了access_token
,所以我們預計會從端點獲取其余可用的身份信息(基于范圍)/userinfo
。在這種情況下,它產生的信息與之前我們僅請求access_token
將所有可用范圍與所有可能的響應類型相結合,可呈現大量信息:確切地說是 48 種組合。首先,我將列舉每個范圍產生的結果,然后我們將查看結合request_type
和的幾個真實示例scope
。
首先要注意的是,不同的范圍會對端點中編碼id_token
和返回的信息產生影響。以下是范圍和結果聲明的表格。
范圍 | 由此產生的索賠 |
開放標識 | (所有 OIDC 流程都需要) |
輪廓 | 姓名、姓氏、名字、中間名、昵稱、首選用戶名 |
簡介(續) | 個人資料、圖片、網站、性別、出生日期、區域信息、語言環境、updated_at |
電子郵件 | 電子郵件,email_verified |
地址 | 地址 |
電話 | phone_number, phone_number_verified |
讓我們使用所有可能的(默認)范圍類型嘗試每個隱式流。
https://micah.okta.com/oauth2/aus2yrcz7aMrmDAKZ1t7/v1/authorize?client_id=0oa2yrbf35Vcbom491t7&response_type=token&scope=openid+profile+email+address+phone&state=aboard-insect-fresh-smile&nonce=c96fa468-ca1b-46f0-8974-546f23f9ee6f&redirect_uri=https%3A%2F%2Fokta-oidc-fun.herokuapp.com%2Fflow_result
與之前相比,結果的唯一區別access_token
是所有范圍都被編碼到scp
數組聲明中。
這次,當我使用access_token
來訪問/userinfo
端點時,我得到了更多信息:
http https://micah.okta.com/oauth2/aus2yrcz7aMrmDAKZ1t7/v1/userinfo Authorization:"Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6Ik93bFNJS3p3Mmt1Wk8zSmpnMW5Dc2RNelJhOEV1elY5emgyREl6X3RVRUkifQ..."
HTTP/1.1 200 OK
...
{
"sub": "00u2yulup4eWbOttd1t7",
"name": "Okta OIDC Fun",
"locale": "en-US",
"email": "okta_oidc_fun@okta.com",
"preferred_username": "okta_oidc_fun@okta.com",
"given_name": "Okta OIDC",
"family_name": "Fun",
"zoneinfo": "America/Los_Angeles",
"updated_at": 1499922371,
"email_verified": true
}
注意:雖然這不是從profile
范圍定義的聲明的完整列表,但它是我的 Okta 中的用戶具有價值的所有聲明。
讓我們嘗試id_token
隱式流程(仍然具有所有默認范圍):
https://micah.okta.com/oauth2/aus2yrcz7aMrmDAKZ1t7/v1/authorize?client_id=0oa2yrbf35Vcbom491t7&response_type=id_token&scope=openid+profile+email+address+phone&state=aboard-insect-fresh-smile&nonce=c96fa468-ca1b-46f0-8974-546f23f9ee6f&redirect_uri=https%3A%2F%2Fokta-oidc-fun.herokuapp.com%2Fflow_result
id_token
以下是我收到的編碼內容:
{
"sub": "00u2yulup4eWbOttd1t7",
"name": "Okta OIDC Fun",
"locale": "en-US",
"email": "okta_oidc_fun@okta.com",
"ver": 1,
"iss": "https://micah.okta.com/oauth2/aus2yrcz7aMrmDAKZ1t7",
"aud": "0oa2yrbf35Vcbom491t7",
"iat": 1501532222,
"exp": 1501535822,
"jti": "ID.Zx8EclaZmhSckGHOCRzOci2OaduksmERymi9-ad7ML4",
"amr": [
"pwd"
],
"idp": "00o1zyyqo9bpRehCw1t7",
"nonce": "c96fa468-ca1b-46f0-8974-546f23f9ee6f",
"preferred_username": "okta_oidc_fun@okta.com",
"given_name": "Okta OIDC",
"family_name": "Fun",
"zoneinfo": "America/Los_Angeles",
"updated_at": 1499922371,
"email_verified": true,
"auth_time": 1501528157
}
所有(可用的)身份信息都被直接編碼到令牌中,因為我沒有承載令牌來訪問端點/userinfo
。
最后,讓我們嘗試隱式流程的最后一種變體response_type=id_token+token
:
https://micah.okta.com/oauth2/aus2yrcz7aMrmDAKZ1t7/v1/authorize?client_id=0oa2yrbf35Vcbom491t7&response_type=code+id_token+token&scope=openid+profile+email+address+phone&state=aboard-insect-fresh-smile&nonce=c96fa468-ca1b-46f0-8974-546f23f9ee6f&redirect_uri=https%3A%2F%2Fokta-oidc-fun.herokuapp.com%2Fflow_result
在這種情況下,我們將一些聲明編碼為id_token
:
{
"sub": "00u2yulup4eWbOttd1t7",
"name": "Okta OIDC Fun",
"email": "okta_oidc_fun@okta.com",
"ver": 1,
"iss": "https://micah.okta.com/oauth2/aus2yrcz7aMrmDAKZ1t7",
"aud": "0oa2yrbf35Vcbom491t7",
"iat": 1501532304,
"exp": 1501535904,
"jti": "ID.1C2NQext2hM0iJy55cLc_Ryc45urVYC1wJ0S-KebkpI",
"amr": [
"pwd"
],
"idp": "00o1zyyqo9bpRehCw1t7",
"nonce": "c96fa468-ca1b-46f0-8974-546f23f9ee6f",
"preferred_username": "okta_oidc_fun@okta.com",
"auth_time": 1501528157,
"at_hash": "GB5O9CpSSOUSfVZ9CRekRg",
"c_hash": "mRNStYQm-QU4rwcfv88VKA"
}
如果我們使用結果access_token
來到達/userinfo
端點,在這種情況下,我們會得到:
http https://micah.okta.com/oauth2/aus2yrcz7aMrmDAKZ1t7/v1/userinfo Authorization:"Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6Ik93bFNJS3p3Mmt1Wk8zSmpnMW5Dc2RNelJhOEV1elY5emgyREl6X3RVRUkifQ..."
HTTP/1.1 200 OK
...
{
"sub": "00u2yulup4eWbOttd1t7",
"name": "Okta OIDC Fun",
"locale": "en-US",
"email": "okta_oidc_fun@okta.com",
"preferred_username": "okta_oidc_fun@okta.com",
"given_name": "Okta OIDC",
"family_name": "Fun",
"zoneinfo": "America/Los_Angeles",
"updated_at": 1499922371,
"email_verified": true
}
這將完善范圍內所請求的所有身份信息。
OIDC 規范可容納自定義范圍和聲明。在令牌(可通過加密驗證)中包含自定義聲明的能力是身份提供者的一項重要功能。Okta 的實現為此提供了支持。
下面的屏幕截圖顯示了我的授權服務器的聲明選項卡:
點擊“添加聲明”按鈕將彈出一個對話框:
在上面的屏幕截圖中,自定義聲明是使用 Okta 的表達式語言定義的。表達式語言是 Okta 獨有的,它是一種靈活的方式來描述構建屬性以包含(或不包含)在自定義聲明中的規則。
response_type=id_token
使用帶有和的隱式流scope=openid+profile
,我們現在返回一個id_token
包含以下編碼聲明的:
{
"sub": "00u2yulup4eWbOttd1t7",
"ver": 1,
"iss": "https://micah.okta.com/oauth2/aus2yrcz7aMrmDAKZ1t7",
"aud": "0oa2yrbf35Vcbom491t7",
"iat": 1501533536,
"exp": 1501537136,
"jti": "ID.TsKlBQfGmiJcl2X3EuhzyyLfmzqi0OCd66rJ3Onk7FI",
"amr": [
"pwd"
],
"idp": "00o1zyyqo9bpRehCw1t7",
"nonce": "c96fa468-ca1b-46f0-8974-546f23f9ee6f",
"auth_time": 1501528157,
"at_hash": "hEjyn3mbKjuWanuSAF-z4Q",
"full_name": "Okta OIDC Fun"
}
請注意full_name
中的聲明id_token
。
可以通過訪問端點來驗證訪問令牌/introspect
。對于active
令牌,您將獲得如下響應:
http --auth <OIDC Client ID>:<OIDC Client Secret> -f POST \
https://micah.okta.com/oauth2/aus2yrcz7aMrmDAKZ1t7/v1/introspect \
token=eyJhbGciOiJSUzI1NiIsImtpZCI6Ik93bFNJS3p3Mmt1Wk8zSmpnMW5Dc2RNelJhOEV1elY5emgyREl6X3RVRUkifQ...
HTTP/1.1 200 OK
...
{
"active": true,
"aud": "https://afitnerd.com/test",
"client_id": "xdgqP32nYN148gn3gJsW",
"exp": 1498517509,
"fullName": "Micah Silverman",
"iat": 1498513909,
"iss": "https://micah.oktapreview.com/oauth2/aus9vmork8ww5twZg0h7",
"jti": "AT.JdXQPAuh-JTqhspCL8nLe2WgbfjcK_-jmlp7zwaYttE",
"scope": "openid profile",
"sub": "micah+okta@afitnerd.com",
"token_type": "Bearer",
"uid": "00u9vme99nxudvxZA0h7",
"username": "micah+okta@afitnerd.com"
}
由于它需要 OIDC 客戶端 ID 和密鑰,因此此操作通常在可以安全獲取這些憑據的應用服務器中完成。您不會希望最終用戶 Web 或移動應用等能夠訪問 OIDC 客戶端密鑰。
如果token
參數無效或者已過期,/introspect
端點將返回以下內容:
http --auth <OIDC Client ID>:<OIDC Client Secret> -f POST \
https://micah.okta.com/oauth2/aus2yrcz7aMrmDAKZ1t7/v1/introspect \
token=bogus
HTTP/1.1 200 OK
...
{
"active": false
}
可以使用JWK端點驗證 ID 令牌。JWK 是一種表示加密密鑰的 JSON 數據結構。JWK 端點從用于 API 發現的 OIDC“眾所周知”端點公開。這會返回大量信息。以下是摘錄:
http https://micah.okta.com/oauth2/aus2yrcz7aMrmDAKZ1t7/.well-known/openid-configuration
HTTP/1.1 200 OK
...
{
"authorization_endpoint": "https://micah.okta.com/oauth2/aus2yrcz7aMrmDAKZ1t7/v1/authorize",
...
"introspection_endpoint": "https://micah.okta.com/oauth2/aus2yrcz7aMrmDAKZ1t7/v1/introspect",
...
"issuer": "https://micah.okta.com/oauth2/aus2yrcz7aMrmDAKZ1t7",
"jwks_uri": "https://micah.okta.com/oauth2/aus2yrcz7aMrmDAKZ1t7/v1/keys",
...
"userinfo_endpoint": "https://micah.okta.com/oauth2/aus2yrcz7aMrmDAKZ1t7/v1/userinfo"
}
其中一些端點(例如/userinfo
和/authorize
)現在看起來應該很熟悉。我們感興趣的是/keys
中顯示的端點jwks_uri
。
http https://micah.okta.com/oauth2/aus2yrcz7aMrmDAKZ1t7/v1/keys
HTTP/1.1 200 OK
...
{
"keys": [
{
"alg": "RS256",
"e": "AQAB",
"kid": "cbkhWG0YmFsGiNO1LEkWSEszDCTNfwvJPpXxuVf_kX0",
"kty": "RSA",
"n": "g2XQgdyc5P6F4K26ioKiUzrdgfy90eBgIbcrKkspKZmzRJ3CIssv69f1ClJvT784J-...",
"use": "sig"
}
]
}
注意kid
聲明。它kid
與我們標題中的聲明相匹配id_token
:
{
"typ": "JWT",
"alg": "RS256",
"kid": "cbkhWG0YmFsGiNO1LEkWSEszDCTNfwvJPpXxuVf_kX0"
}
我們還可以看到使用的算法是RS256
。使用聲明中找到的公鑰n
以及安全庫,我們可以確認 ID 令牌未被篡改。所有這些都可以在最終用戶 SPA、移動應用程序等上安全地完成。
下面是一個 Java 示例,它使用上述聲明jwks_uri
來驗證id_token
: https: //github.com/dogeared/JWKTokenVerifier
java -jar target/jwk-token-verifier-0.0.1-SNAPSHOT-spring-boot.jar \
eyJhbGciOiJSUzI1NiIsImtpZCI6Ik93bFNJS3p3Mmt1Wk8zSmpnMW5Dc2RNel... \
g2XQgdyc5P6F4K26ioKiUzrdgfy90eBgIbcrKkspKZmzRJ3CIssv69f1ClJvT784J-... \
AQAB
Verified Access Token
{
"header" : {
"alg" : "RS256",
"kid" : "cbkhWG0YmFsGiNO1LEkWSEszDCTNfwvJPpXxuVf_kX0"
},
"body" : {
"ver" : 1,
"jti" : "AT.LT9cRL_Kzd3T8Izw_ONZxHJ5xGBPD0m13iiEIDK_Nbw",
"iss" : "https://micah.okta.com/oauth2/aus2yrcz7aMrmDAKZ1t7",
"aud" : "test",
"iat" : 1501533536,
"exp" : 1501537136,
"cid" : "0oa2yrbf35Vcbom491t7",
"uid" : "00u2yulup4eWbOttd1t7",
"scp" : [ "openid" ],
"sub" : "okta_oidc_fun@okta.com"
},
"signature" : "ZV_9tYxt4v4bp9WEEDu038b7v_OHsbMZw13daR1s5_tI56oayBgJlnqf-..."
}
如果 JWT 的任何部分id_token
被篡改,你會看到以下信息:
io.jsonwebtoken.SignatureException: JWT signature does not match locally computed signature. JWT validity cannot be asserted and should not be trusted.
使用終端和 JWK 驗證 JWT/introspect
是 OIDC 的一個強大組件。它能夠高度確保令牌未以任何方式被篡改。因此,令牌中包含的信息(例如有效期)可以安全地執行。