V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
Authing
V2EX  ›  OAuth

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

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

    图片 在上一篇文章中,我们介绍了 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   ·   实用小工具   ·   2754 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 33ms · UTC 08:09 · PVG 16:09 · LAX 00:09 · JFK 03:09
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.