"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_tokenid_token請稍等,稍后我們將更深入地討論令牌。)當架構中包含“中間件”時,此流程非常有用。中間件具有 和client idclient secret這是code通過點擊/token端點將 交換為令牌所必需的。然后可以將這些令牌返回給最終用戶應用程序(例如瀏覽器),而無需瀏覽器知道client secret。此流程允許通過使用 實現長壽命會話refresh tokens。 的唯一目的refresh tokens是獲取新的access tokens以延長用戶會話。

隱式流程使用response_type=id_token tokenresponse_type=id_token。身份驗證成功后,響應將在第一種情況下包含id_token和,在第二種情況下僅包含 。當您的應用程序直接與后端對話以獲取沒有中間件的令牌時,此流程很有用。它不支持長壽命會話。access_tokenid_token

混合流將上述兩種流以不同的組合方式組合在一起 – 以適合用例的方式。例如response_type=code id_token。此方法可實現一種場景,您可以在應用程序中擁有一個長壽命會話,并立即從/authorization端點獲取令牌。

關于Token

有了范圍、聲明和響應類型的基礎,我們現在可以討論令牌了!OIDC 中有三種類型的令牌:id_tokenaccess_tokenrefresh_token

ID Token

根據OIDC 規范, Anid_tokenJWT。這意味著:

規范中有一組規則id_token用于驗證。 在 中編碼的聲明中id_token有一個到期日期 ( exp),必須在驗證過程中遵守。 此外,JWT 的簽名部分與密鑰一起使用,以驗證整個 JWT 未以任何方式被篡改。

訪問Token

訪問令牌用作不記名令牌。不記名令牌意味著不記名令牌持有者無需進一步識別即可訪問授權資源。因此,保護不記名令牌非常重要。如果我能以某種方式獲得并“持有”您的訪問令牌,我就可以偽裝成您。

為了提高安全性,這些令牌通常具有較短的使用壽命(由其到期時間決定)。也就是說,當訪問令牌到期時,用戶必須再次進行身份驗證才能獲得新的訪問令牌,從而限制其為不記名令牌這一事實的暴露。

盡管 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

刷新令牌用于獲取新的訪問令牌。通常,刷新令牌是長期有效的,而訪問令牌是短期有效的。這允許長期存在的會話在必要時可以被終止。以下是一個典型的場景:

  1. 用戶登錄并獲取訪問令牌和刷新令牌
  2. 應用程序檢測到訪問令牌已過期
  3. 應用程序使用刷新令牌獲取新的訪問令牌
  4. 重復 2 和 3,直到刷新令牌過期
  5. 刷新令牌過期后,用戶必須重新進行身份驗證

你可能會問:為什么要這樣做?這種方法在用戶體驗和安全性之間取得了平衡。想象一下,如果用戶以某種方式受到威脅。或者,他們的訂閱到期了。或者,他們被解雇了。在任何時候,管理員都可以撤銷刷新令牌。然后,上面的第三步將失敗,用戶將被迫(嘗試)通過身份驗證建立新會話。如果他們的帳戶已被暫停,他們將無法進行身份驗證。

識別類型

區分不同的 token 類型有時會令人困惑。以下是快速參考:

  1. ID 令牌攜帶在令牌本身中編碼的身份信息,該令牌必須是 JWT
  2. 訪問令牌用于通過將資源用作承載令牌來獲取對資源的訪問權限
  3. 刷新令牌僅用于獲取更多訪問令牌

OIDC 實際應用

您從 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

讓我們詳細分析一下:

鑰匙價值描述
組織 URLhttps://micah.okta.comOkta 租戶
授權網址/oauth2/aus2yrcz7aMrmDAKZ1t7/v1/授權貴組織的默認授權端點
客戶端 ID0oa2yrbf35Vcbom491t7Okta 中定義的 OIDC 應用程序的客戶端 ID
響應類型代碼指示代碼流的響應類型
范圍開放標識openid 范圍是必需的
狀態小房間油膩餡餅流程結束時返回隨機值
隨機數b1e7b75d-6248-4fc7-bad0-ac5ae0f2e581編碼到 id_token 中的隨機值,用于稍后驗證
重定向 urihttps%3A%2F%2Fokta-oidc-fun.herokuapp.com%2Fflow_resultOP 重定向到的 URL 編碼 URL

瀏覽器中的情況如下:

請注意,在新屏幕上,您將被重定向回redirect_uri最初指定的頁面:

在后臺,會使用固定的用戶名和密碼建立會話。則當您單擊鏈接時,您將被重定向到登錄,然后重定向回同一頁面。

在上面的屏幕截圖中,您可以看到返回的代碼和原始的state

現在,中間層(在本例中為 Spring Boot 應用程序)可以將該代碼交換為id_token和。這個中間層將驗證我們之前在授權請求中發送的狀態,并使用客戶端密鑰發出請求,為用戶創建和。access_token/tokenaccess_tokenid_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_typeid_tokentoken下面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_tokentoken

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
}

Token中有什么?

根據 OIDC 規范,與身份相關的信息有兩個主要來源。一個來源是編碼到id_token JWT中的信息。另一個來源是來自端點的響應/userinfo,可以使用access_token不記名令牌訪問。在 Okta,我們也選擇將訪問令牌設為 JWT,這提供了第三個信息來源。(您會在許多 OIDC 實現中看到這一點。)

請求中有很多查詢參數組合/authorization,它們決定了哪些信息將被編碼到 中。影響最終在返回的令牌和端點id_token中找到的內容的兩個查詢參數是和。/userinforesponse_typescope

OIDC 響應類型

目前,我們將把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
}

我們返回了subemailemail_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
}

請注意,我們將subemail聲明直接編碼在 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

OIDC 范圍

將所有可用范圍與所有可能的響應類型相結合,可呈現大量信息:確切地說是 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 的一個強大組件。它能夠高度確保令牌未以任何方式被篡改。因此,令牌中包含的信息(例如有效期)可以安全地執行。

上一篇:

增強API安全性:使用OPA和Kong Gateway進行細粒度訪問控制

下一篇:

確保OAuth 2.0訪問令牌安全,使用持有者憑證證明
#你可能也喜歡這些API文章!

我們有何不同?

API服務商零注冊

多API并行試用

數據驅動選型,提升決策效率

查看全部API→
??

熱門場景實測,選對API

#AI文本生成大模型API

對比大模型API的內容創意新穎性、情感共鳴力、商業轉化潛力

25個渠道
一鍵對比試用API 限時免費

#AI深度推理大模型API

對比大模型API的邏輯推理準確性、分析深度、可視化建議合理性

10個渠道
一鍵對比試用API 限時免費