授权码 + PKCE 模式| OIDC & OAuth2.0 认证协议最佳实践系列 [03] - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
Authing
V2EX    OAuth

授权码 + PKCE 模式| OIDC & OAuth2.0 认证协议最佳实践系列 [03]

  •  
  •   Authing 2023-05-04 17:10:45 +08:00 1442 次点击
    这是一个创建于 891 天前的主题,其中的信息可能已经有所发展或是发生改变。

    图片 在上一篇文章中,我们介绍了OIDC授权码模式,本次我们将重点围绕 授权码 + PKCE 模式( Authorization Code With PKCE )进行介绍 ,从而让你的系统快速具备接入用户认证的标准体系。OIDC & OAuth2.0 认证协议最佳实践系列 02 - 授权码模式( Authorization Code )接入 Authing 图片

    为什么会有 PKCE 模式:

    PKCE是 Proof Key for Code Exchange 的缩写,PKCE 是一种用于增强授权码模式安全性的方法,它可以防止恶意应用程序通过截获授权码和重定向 URI 来获得访问令牌。PKCE 通过将随机字符串( code_verifier )和其 SHA-256 哈希值( code_challenge )与授权请求一起发送,确保访问令牌只能由具有相应 code_verifier 的应用程序使用,保障用户的安全性。

    [ OAuth 2.0 协议扩展] PKCE 扩展协议:为了解决公开客户端的授权安全问题

    「面向对象」 public 客户端,其本身没有能力保存密钥信息(恶意攻击者可以通过反编译等手段查看到客户端的密钥 client_secret , 也就可以通过授权码 code 换取 access_token , 到这一步,恶意应用就可以拿着 token 请求资源服务器了)

    「原理」 PKCE 协议本身是对OAuth 2.0的扩展, 它和之前的授权码流程大体上是一致的, 区别在于在向授权服务器的 authorize endpoint 请求时,需要额外的 code_challenge 和 code_challenge_method 参数;向 token endpoint 请求时, 需要额外的 code_verifier 参数。最后授权服务器会对这三个参数进行对比验证, 通过后颁发令牌。

    01.授权码 + PKCE 模式( Authorization Code With PKCE )

    如果你的应用是一个 SPA 前端应用或移动端 App ,建议使用授权码 + PKCE 模式来完成用户的认证和授权。授权码 + PKCE 模式适合不能安全存储密钥的场景(例如前端浏览器)

    我们解释下 code_verifier 和 code_challenge 对于每一个 OAuth/OIDC 请求,客户端会先创建一个代码验证器 code_verifier

    code_verifier:在 [A-Z] / [a-z] / [0-9] / "-" / "." / "_" / "~" 范围内,生成 43-128 位的随机字符串。

    code_challenge:则是对 code_verifier 通过 code_challenge_method 例如 sha256 转换得来的。

    用大白话讲下就是在认证是用户携带的是加密后的 code_challenge ,在用户认证成功通过 code 获取 Token 时,客户端证明自己的方式则是把 code_verifier 原文发送,认证中心收到获取 Token 请求时通过 code_verifier + code_challenge_method 进行转换,发现最终结果与 code_challenge 匹配则返回 Token ,否则拒绝。

    1.1 整体流程

    整体上,有以下流程:

    1.用户点击登录。 2.在你的应用中,生成 code_verifier 和 code_challenge 。 3.拼接登录链接(包含 code_challenge ) 跳转到 Authing 请求认证。 4.Authing 发现用户没有登录,重定向到认证页面,要求用户完成认证。 5.用户在浏览器完成认证。 6.Authing 服务器通过浏览器通过重定向将授权码( code )发送到你的应用前端。 7.你的应用将授权码 (code) 和 code_verifier 发送到 Authing 请求获取 Token. 8.Authing 校验 code 、code_verifier 和 code_challenge 。 9.校验通过,Authing 则返回 AccessToken 和 IdToken 以及可选的 RefreshToken 。 10.你的应用现在知道了用户的身份,后续使用 AccessToken 换取用户信息,调用资源方的 API 等

    图片

    1.2 准备接入

    1.2.1 整体流程

    需要先在 Authing 创建应用: 图片 配置登录回调和登出回调,配置为你实际项目的地址,我们在这里配置 localhost 用于测试。 若你想匹配多个登录 /登出回调 可以使用 ‘*’ 号进行通配,登录 /登出回调可以是如下格式 图片

    图片 在协议配置中,我们勾选 authorization_code 并且使用 code 作为返回类型,如下图所示:PKCE 模式使用的是 code_verifier 来换取 Token ,所以需要配置获取 Token 的方式为 null 图片

    1.3 接入测试

    1.3.1 所需调用接口列表

    GET${host}/oidc/auth 发起登录(拼接你的发起登录地址) POST${host}/oidc/token 获取 TokenGET${host}/oidc/me 获取用户信息 POST${host}/oidc/token/introspection 校验 TokenPOST${host}/oidc/token 刷新 TokenPOST${host}/oidc/revocation 吊销 TokenGET${host}/session/end 登出 

    1.3.2 Run in Postman 所需调用接口列表

    https://app.getpostman.com/run-collection/24730905-5d29e488-719e-4ffe-af21-a7c18298d328?action=collection%2Ffork&collection-url=entityId%3D24730905-5d29e488-719e-4ffe-af21-a7c18298d328%26entityType%3Dcollection%26workspaceId%3D13ff793c-024c-459d-b1f6-87e91c4769ed#env%5BAuthing%20OIDC%5D=W3sia2V5IjoiaG9zdCIsInZhbHVlIjoiaHR0cHM6Ly9kZWVwbGFuZy5hdXRoaW5nLmNuIiwiZW5hYmxlZCI6dHJ1ZSwidHlwZSI6ImRlZmF1bHQifSx7ImtleSI6ImNsaWVudF9pZCIsInZhbHVlIjoiNjM4MmNmNDg2ZTVhNjk0NDNhZjI5NzFiIiwiZW5hYmxlZCI6dHJ1ZSwidHlwZSI6ImRlZmF1bHQifSx7ImtleSI6ImNsaWVudF9zZWNyZXQiLCJ2YWx1ZSI6Ijc3NWMyM2NlMjkwYzkwZDQwNDUxNGU3MDgyMDkzZWIzIiwiZW5hYmxlZCI6dHJ1ZSwidHlwZSI6ImRlZmF1bHQifSx7ImtleSI6ImFjY2Vzc190b2tlbiIsInZhbHVlIjoiIiwiZW5hYmxlZCI6dHJ1ZSwidHlwZSI6ImRlZmF1bHQifSx7ImtleSI6ImlkX3Rva2VuIiwidmFsdWUiOiIiLCJlbmFibGVkIjp0cnVlLCJ0eXBlIjoiZGVmYXVsdCJ9LHsia2V5IjoicmVmcmVzaF90b2tlbiIsInZhbHVlIjoiIiwiZW5hYmxlZCI6dHJ1ZSwidHlwZSI6ImRlZmF1bHQifV0=

    1.3.3 发起登录

    GET${host}/oidc/auth

    这是基于浏览器的 OIDC 的起点,请求对用户进行身份验证,并会在验证成功后返回授权码到您所指定的 redirect_uri 。

    生成 code_challenge 和 code_verifier

    在线生成 https://tonyxu-io.github.io/pkce-generator/

    离线生成 首先,我们要生成一个 code_challenge 和 code_verifier,以下是使用 Javascript 语言生成 PKCE 所需要的 code_verifier 和 code_challenge 的脚本:

    // 生成随机字符串 function generateRandomString(length) { var result = ''; var characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; var charactersLength = characters.length; for (var i = 0; i < length; i++) { result += characters.charAt(Math.floor(Math.random() * charactersLength)); } return result; } // 生成 code_verifier var codeVerifier = generateRandomString(128); // 对 code_verifier 进行 SHA-256 编码,并将其转换为 base64url 格式的 code_challenge var sha256 = new jsSHA("SHA-256", "TEXT"); sha256.update(codeVerifier); var codeChallenge = btoa(String.fromCharCode.apply(null, new Uint8Array(sha256.getHash("ARRAYBUFFER")))) .replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, ''); // 将 code_verifier 和 code_challenge 用对象形式返回 var pkce = { codeVerifier: codeVerifier, codeChallenge: codeChallenge }; 

    以上代码使用 jsSHA 库计算 SHA-256 哈希值,使用 base64url 编码将哈希值转换为 code_challenge 。你可以将以上代码复制到你的 Javascript 代码中,并使用 pkce.codeVerifier 和 pkce.codeChallenge 调用 OAuth 2.0 授权请求。

    举例

    code_verifier:4aHg5fN1AGdbnBAfVKMf9ZMK4PUOBTwQSKKk9V8wYXOFYDZklMl7dzDUhnQi4sYhzGb6PWCkNQqLP70K1DNOneEDq8iyASepAdGjGBBmCs4BGCDDJNwLrGpnJEfmrI66 

    code_verifier 的长度为 43 ~ 128 ,我们生成的是 128 位

    code_challenge:OhMk95M9qWkKd06--utVtRzQh8Y0Qtqo4cPqqzMJyMw 

    发起登录地址(浏览器中打开) https://{host}/oidc/auth?scope=openid+profile+offline_access+username+email+phone&redirect_uri=http://localhost:8080/&response_type=code&prompt=consent&nOnce=6e187def-1a19-4067-8875-653f024d5a9f&client_id={client_id}&state=1676881862&code_challenge={code_challenge}&code_challenge_method=S256

    体验地址 https://oidc-authorizationcode-withpkce.authing.cn/oidc/auth?scope=openid+profile+offline_access+username+email+phone&redirect_uri=http://localhost:8080/&response_type=code&prompt=consent&nOnce=6e187def-1a19-4067-8875-653f024d5a9f&client_id=63f30f5bf629268cc27d93c6&state=1676881862&code_challenge=OhMk95M9qWkKd06--utVtRzQh8Y0Qtqo4cPqqzMJyMw&code_challenge_method=S256

    参数说明 图片

    1.3.4 获取 Token

    POST${host}/oidc/token 用户在 Authing 侧完成登录操作后,Authing 会将生成的 code 作为参数回调到 redirect_uri 地址,此时通过 code 换 token 接口即可拿到对应的访问令牌 access_token

    请求参数 图片 请求示例

    curl --location --request POST 'https://{host}/oidc/token' \ --header 'Content-Type: application/x-www-form-urlencoded' \ --data-urlencode 'client_id={应用 ID}' \ --data-urlencode 'client_secret={应用密钥}' \ --data-urlencode 'grant_type=authorization_code' \ --data-urlencode 'redirect_uri={发起登录时指定的 redirect_uri}' \ --data-urlencode 'code={/oidc/auth 返回的 code}' \ --data-urlencode 'code_verifier={code_verifier}' 

    响应示例(成功)

    { "scope": "openid username email phone offline_access profile", "token_type": "Bearer", "access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6ImVtSzBGbVRIa0xlQWFjeS1YWEpVT3J6SzkxV243TkdoNGFlYUVlSjVQOUUifQ.eyJzdWIiOiI2M2ViNTNjNDQxYTVjMmYwNWYyNGJiMDMiLCJhdWQiOiI2M2ViNDU4NTE1NmQ5NzcxMDFkZDM3NTAiLCJzY29wZSI6Im9wZW5pZCB1c2VybmFtZSBlbWFpbCBwaG9uZSBvZmZsaW5lX2FjY2VzcyBwcm9maWxlIiwiaWF0IjoxNjc2MzY2OTE0LCJleHAiOjE2Nzc1NzY1MTQsImp0aSI6ImVmVU04enNrbl92LXYzeXZfbDVHRV9fQ2JEY0NNZDhEVDFnYVI0bHRqcHAiLCJpc3MiOiJodHRwczovL29pZGMtYXV0aG9yaXphdGlvbi1jb2RlLmF1dGhpbmcuY24vb2lkYyJ9.E3gAYzCQbJmrtM5zl91OPHm2YPnDxzRejw75oVMF1tLqCS0trj6CSBxyxP3Z9t6Eb_oAu1f_3I6XC3KC-l0DTM6q7_R2rnW4LWlik2rDCLuGpG0FqFScLZhwafmrPsVn93yaBQfEEoaLviqKhj3DgOymKqHZzFG3taaz2k_pWsxt4z97DtKjRTiqyMvcSfHsVrjSKELaC-5S_PHPWcQ70iX85IwUb6i5ldZGxYmODCvChNC9p4D4IOT3atvyEHgBTmjA9ZKI-T7hCVHSO91WZY3l1p4iWdi6KdP1oMGTy8WbmUHG9SiWO1Efh_9I5ZpRzVNWXINLv-lZ0d2aZKjg2w", "expires_in": 1209600, "id_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiI2M2ViNTNjNDQxYTVjMmYwNWYyNGJiMDMiLCJhdWQiOiI2M2ViNDU4NTE1NmQ5NzcxMDFkZDM3NTAiLCJpYXQiOjE2NzYzNjY5MTQsImV4cCI6MTY3NzU3NjUxNCwiaXNzIjoiaHR0cHM6Ly9vaWRjLWF1dGhvcml6YXRpb24tY29kZS5hdXRoaW5nLmNuL29pZGMiLCJub25jZSI6IjhiYjg3MjdhLWU1MGUtNDUzOC05ZmZmLWZhOTFlNWQ0Y2MwYSIsIm5hbWUiOm51bGwsImdpdmVuX25hbWUiOm51bGwsIm1pZGRsZV9uYW1lIjpudWxsLCJmYW1pbHlfbmFtZSI6bnVsbCwibmlja25hbWUiOm51bGwsInByZWZlcnJlZF91c2VybmFtZSI6bnVsbCwicHJvZmlsZSI6bnVsbCwicGljdHVyZSI6Imh0dHBzOi8vZmlsZXMuYXV0aGluZy5jby9hdXRoaW5nLWNvbnNvbGUvZGVmYXVsdC11c2VyLWF2YXRhci5wbmciLCJ3ZWJzaXRlIjpudWxsLCJiaXJ0aGRhdGUiOm51bGwsImdlbmRlciI6IlUiLCJ6b25laW5mbyI6bnVsbCwibG9jYWxlIjpudWxsLCJ1cGRhdGVkX2F0IjoiMjAyMy0wMi0xNFQwOToyNjoyOC4wNjhaIiwiZW1haWwiOm51bGwsImVtYWlsX3ZlcmlmaWVkIjpmYWxzZSwicGhvbmVfbnVtYmVyIjoiMTg1MTY4Mjk5OTUiLCJwaG9uZV9udW1iZXJfdmVyaWZpZWQiOnRydWUsInVzZXJuYW1lIjpudWxsfQ.GweoWBCEyHQGP6G9ohbfBMUMALlbZMM9hRAes1De7BM", "refresh_token": "KanvCEmonS_FgCRdFftOCwka2f8Qjj4tcsIfJF-VC1W" } 

    响应示例(失败)

    { "error": "invalid_grant", "error_description": "授权码无效或已过期" } 

    1.3.5 所需调用接口列表

    GET${host}/oidc/me 获取用户信息 此端点是 OIDC 获取用户端点,可以通过 AccessToken 获取有关当前登录用户的信息。

    请求参数 图片 请求示例

    curl --location --request GET 'https://{host}/oidc/me?access_token={access_token}' 

    响应示例(成功)

    { "name": null, "given_name": null, "middle_name": null, "family_name": null, "nickname": null, "preferred_username": null, "profile": null, "picture": "https://files.authing.co/authing-console/default-user-avatar.png", "website": null, "birthdate": null, "gender": "U", "zoneinfo": null, "locale": null, "updated_at": "2023-02-14T09:26:28.068Z", "email": "[email protected]", "email_verified": true, "phone_number": "185xxxx9995", "phone_number_verified": true, "username": "neo", "sub": "63eb53c441a5c2f05f24bb03" } 

    响应示例(失败)

    { "error": "invalid_grant", "error_description": "Access Token 无效" } 

    1.3.6 校验 Token

    POST${host}/oidc/auth 此端点接受 access_token 、id_token 、refresh_token ,并返回一个布尔值,指示它是否处于活动状态。如果令牌处于活动状态,还将返回有关令牌的其他数据。如果 token 无效、过期或被吊销,则认为它处于非活动状态。

    access_token 可以使用 RS256 签名算法或 HS256 签名算法进行签名。下面是这两种签名算法的区别:

    RS256 是使用 RSA 算法的一种数字签名算法,它使用公钥 /私钥对来加密和验证信息。 RS256 签名生成的令牌比 HS256 签名生成的令牌更加安全,因为使用 RSA 密钥对进行签名可以提供更高的保护级别。使用 RS256 签名算法的令牌可以使用公钥进行验证,公钥可以通过 JWK 端点获取。

    HS256 是使用对称密钥的一种数字签名算法。它使用同一个密钥进行签名和验证。 HS256 签名算法在性能方面比 RS256 签名算法更快,因为它使用的是对称密钥,而不是使用 RSA 公钥 /私钥对来签名和验证。使用 HS256 签名算法的令牌可以通过 shared secret (应用密钥)进行验证。

    在实际应用中,RS256 算法更加安全,但同时也更加消耗资源,如果系统需要高性能,可以选择 HS256 签名算法。

    验证 Token 分为两种方式

    本地验证与使用 Authing 在线验证。我们建议在本地验证 JWT Token ,因为可以节省你的服务器带宽并加快验证速度。你也可以选择将 Token 发送到 Authing 的验证接口由 Authing 进行验证并返回结果,但这样会造成网络延迟,而且在网络拥塞时可能会有慢速请求。

    以下是本地验证和在线验证的优劣对比: 图片

    在线校验

    需要注意的是,id_token 目前无法在线校验,因为 id_token 只是一个标识,若需要校验 id_token 则需要您在离线自行校验

    请求参数 图片

    请求示例

    curl --location --request POST 'https://{host}/oidc/token/introspection' \ --header 'Content-Type: application/x-www-form-urlencoded' \ --data-urlencode 'client_id={应用 ID}' \ --data-urlencode 'client_secret={应用密钥}' \ --data-urlencode 'token={ token }' \ --data-urlencode 'token_type_hint={token_type_hint}' 

    校验 access_token 响应示例(校验通过)

    { "active": true, "sub": "63eb53c441a5c2f05f24bb03", "client_id": "63eb4585156d977101dd3750", "exp": 1677648467, "iat": 1676438867, "iss": "https://oidc-authorization-code.authing.cn/oidc", "jti": "ObgavGBUocr1wsrUvtDLHmuFSgoebxsiOY4JNRqIhaQ", "scope": "offline_access username profile openid phone email", "token_type": "Bearer" } 

    校验 access_token 响应示例(校验未通过)

    { "active": false } 

    校验 refresh_token 响应示例 (校验通过)

    { "active": true, "sub": "63eb53c441a5c2f05f24bb03", "client_id": "63eb4585156d977101dd3750", "exp": 1679030867, "iat": 1676438867, "iss": "https://oidc-authorization-code.authing.cn/oidc", "jti": "6F2TO1v1YZ1_N7I3jXYHjK-vZzKtlD0IiP5KPoUFUCQ", "scope": "offline_access username profile openid phone email" } 

    校验 refresh_token 响应示例(校验未通过)

    { "active": false } 

    离线校验 可参考文档( Authing 开发者文档): https://docs.authing.cn/v2/guides/faqs/how-to-validate-user-token.html#%E6%9C%AC%E5%9C%B0%E9%AA%8C%E8%AF%81

    我们简单说下,若您使用离线校验应该对 token 进行如下规则的校验 1.格式校验- 校验 token 格式是否是 JWT 格式 2.类型校验- 校验 token 是否是目标 token 类型,比如 access_token 、id_token 、refresh_token 3.issuer 校验- 校验 token 是否为信赖的 issuer 颁发 4.签名校验- 校验 token 签名是否由 issuer 签发,防止伪造 5.有效期校验- 校验 token 是否在有效期内 6.claims 校验- 是否符合与预期的一致

    示例代码 下面是一个示例 Java 代码,可以用于在本地校验 OIDC RS256 和 HS256 签发的 access_token

    import com.nimbusds.jose.JWSObject; import com.nimbusds.jwt.JWTClaimsSet; import com.nimbusds.jwt.SignedJWT; import java.net.URL; import java.text.ParseException; import java.util.Date; public class OIDCValidator { private static final String ISSUER = "https://your-issuer.com"; private static final String AUDIENCE = "your-client-id"; private final URL jwkUrl; public OIDCValidator(final URL jwkUrl) { this.jwkUrl = jwkUrl; } public JWTClaimsSet validateToken(final String accessToken) throws ParseException { final SignedJWT signedJWT = SignedJWT.parse(accessToken); if (signedJWT == null) { throw new RuntimeException("Access token is null or empty"); } final JWTClaimsSet claims = signedJWT.getJWTClaimsSet(); if (claims == null) { throw new RuntimeException("No claims present in the access token"); } if (!claims.getIssuer().equals(ISSUER)) { throw new RuntimeException("Invalid issuer in access token"); } if (!claims.getAudience().contains(AUDIENCE)) { throw new RuntimeException("Invalid audience in access token"); } final JWSObject jwsObject = signedJWT.getJWSObject(); if (jwsObject == null) { throw new RuntimeException("No JWS object found in the access token"); } // Fetch the JWKs from the JWK set URL final JWKSet jwkSet = JWKSet.load(jwkUrl); final JWK jwk = jwkSet.getKeyByKeyId(jwsObject.getHeader().getKeyID()); if (jwk == null) { throw new RuntimeException("No JWK found for the access token"); } if (!jwsObject.verify(jwk.getKey())) { throw new RuntimeException("Invalid signature in access token"); } if (claims.getExpirationTime() == null || claims.getExpirationTime().before(new Date())) { throw new RuntimeException("Expired access token"); } return claims; } } 

    这段代码使用 Nimbus JOSE+JWT 库来解析和验证 JWT token 。它使用指定的 issuer 和 audience 值对 access_token 进行验证,并验证 JWT 中 claims 的格式、类型、签名、有效期和 issuer 。如果发生任何验证错误,则将抛出 RuntimeException 。使用时需要传入对应的 JWK URL 和 access_token 进行调用,例如:

    final URL jwkUrl = new URL("https://your-issuer.com/.well-known/jwks.json"); final OIDCValidator validator = new OIDCValidator(jwkUrl); final String accessToken = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyLCJleHAiOjE1MTYyMzkwMjJ9.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c"; final JWTClaimsSet claims = validator.validateToken(accessToken); 

    这个示例只校验了 RS256 和 HS256 签名算法。

    1.3.7 刷新 Token

    POST${host}/oidc/token 此功能用于用户 token 的刷新操作,在 token 获取阶段需要先获取到 refresh_token 。

    请求参数 图片 请求参数

    curl --location --request POST 'https://{host}/oidc/token' \ --header 'Content-Type: application/x-www-form-urlencoded' \ --data-urlencode 'client_id={应用 ID}' \ --data-urlencode 'client_secret={应用密钥}' \ --data-urlencode 'refresh_token={刷新令牌}' \ --data-urlencode 'grant_type=refresh_token' 

    响应示例(成功)

    { "refresh_token": "6F2TO1v1YZ1_N7I3jXYHjK-vZzKtlD0IiP5KPoUFUCQ", "scope": "offline_access username profile openid phone email", "token_type": "Bearer", "access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6ImVtSzBGbVRIa0xlQWFjeS1YWEpVT3J6SzkxV243TkdoNGFlYUVlSjVQOUUifQ.eyJzdWIiOiI2M2ViNTNjNDQxYTVjMmYwNWYyNGJiMDMiLCJhdWQiOiI2M2ViNDU4NTE1NmQ5NzcxMDFkZDM3NTAiLCJzY29wZSI6Im9mZmxpbmVfYWNjZXNzIHVzZXJuYW1lIHByb2ZpbGUgb3BlbmlkIHBob25lIGVtYWlsIiwiaWF0IjoxNjc2NDQ0MjY4LCJleHAiOjE2Nzc2NTM4NjgsImp0aSI6IkEtZUlQYkJ5N3lJLTliUmp1RnJHeXNCSXdjbWtOUl9WalpYODB2aU05VFkiLCJpc3MiOiJodHRwczovL29pZGMtYXV0aG9yaXphdGlvbi1jb2RlLmF1dGhpbmcuY24vb2lkYyJ9.Kk3jSK5BSUEDVTQMdMAdG5cBCxZt31vQiD-XYHNA84Gd3Mo8eDLcQpjMEzQ8HJ4_b9IgMOz5ydXz0zAQ6AjLMW3Rl49qhTGDB7Kq7tHTFmDO8itoO2LQTCLPCPtP3TkoOgptlFD_sd32nefH-HojNhuqwKw469Byw3xnW5xEs3wSuOoUdHwR2n9j1T1Zgp3e90xmBjbtbofQE1z0IWtCnrfJ9ujWsKXoN_7OAXbCTa-Ak_DhgLHU7xutQaaBOgD28lLLT5xclgBWfv7Leyx_kBnVGT5Jvo1tfA6AUEp6wJO4GUBzsijLefI04VDzBGypNuFJlw_jOhSp-SWxJjQSwQ", "expires_in": 1209600, "id_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiI2M2ViNTNjNDQxYTVjMmYwNWYyNGJiMDMiLCJhdWQiOiI2M2ViNDU4NTE1NmQ5NzcxMDFkZDM3NTAiLCJpYXQiOjE2NzY0NDQyNjgsImV4cCI6MTY3NzY1Mzg2OCwiaXNzIjoiaHR0cHM6Ly9vaWRjLWF1dGhvcml6YXRpb24tY29kZS5hdXRoaW5nLmNuL29pZGMiLCJuYW1lIjpudWxsLCJnaXZlbl9uYW1lIjpudWxsLCJtaWRkbGVfbmFtZSI6bnVsbCwiZmFtaWx5X25hbWUiOm51bGwsIm5pY2tuYW1lIjpudWxsLCJwcmVmZXJyZWRfdXNlcm5hbWUiOm51bGwsInByb2ZpbGUiOm51bGwsInBpY3R1cmUiOiJodHRwczovL2ZpbGVzLmF1dGhpbmcuY28vYXV0aGluZy1jb25zb2xlL2RlZmF1bHQtdXNlci1hdmF0YXIucG5nIiwid2Vic2l0ZSI6bnVsbCwiYmlydGhkYXRlIjpudWxsLCJnZW5kZXIiOiJVIiwiem9uZWluZm8iOm51bGwsImxvY2FsZSI6bnVsbCwidXBkYXRlZF9hdCI6IjIwMjMtMDItMTRUMDk6MjY6MjguMDY4WiIsImVtYWlsIjpudWxsLCJlbWFpbF92ZXJpZmllZCI6ZmFsc2UsInBob25lX251bWJlciI6IjE4NTE2ODI5OTk1IiwicGhvbmVfbnVtYmVyX3ZlcmlmaWVkIjp0cnVlLCJ1c2VybmFtZSI6bnVsbH0.DGoJrzkgti44zw-MotVM1KpLxbJTzc5pfh-xYun_xDQ" } 

    响应示例(失败)

    { "error": "invalid_grant", "error_description": "Refresh Token 无效或已过期" } 

    1.3.8 撤回 Token

    POST${host}/oidc/auth

    撤销 access_token / refresh_token 。

    请求参数 图片 请求示例

    curl --location --request POST 'https://{host}/oidc/token/revocation' \ --header 'Content-Type: application/x-www-form-urlencoded' \ --data-urlencode 'client_id={应用 ID}' \ --data-urlencode 'client_secret={应用密钥}' \ --data-urlencode 'token= {token}' \ --data-urlencode 'token_type_hint={token_type_hint}' 

    响应示例(成功)

    HTTP 200 OK

    响应示例(失败)

    { "error": "xxxx", "error_description": "xxxx" } 

    1.3.9 用户登出

    GET${host}/oidc/session/end 使用此操作通过删除用户的浏览器会话来注销用户。 post_logout_redirect_uri 可以指定在执行注销后重定向的地址。否则,浏览器将重定向到默认页面 请求参数 图片 请求示例(浏览器访问)

    GET https://oidc-authorization-code.authing.cn/oidc/session/end?id_token_hint={id_token}&post_logout_redirect_uri=http://localhost:8080/&state=1676452381 

    02.本章总结

    本章我们介绍了 OIDC 授权码模式的接入流程以及相关接口的调用方式,对于小白来说可能需要整体跑一遍流程才能熟悉,我们也建议你 fork 我们的 postman collection 跑一遍流程,对 PKCE 模式你就基本掌握啦。 接下来我们还会介绍 OIDC 的授权码+PKCE 流程,以及接入Authing的方式,需要你对授权码模式的流程有一定了解哦。

    往期精彩内容 什么是事件驱动( EDA )?为什么它是技术领域的主要驱动力? 图片

    目前尚无回复
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     2630 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 117ms UTC 06:02 PVG 14:02 LAX 23:02 JFK 02:02
    Do have faith in what you're doing.
    ubao snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86