【问题标题】:Should connect/userinfo endpoint work after token revocation?令牌撤销后连接/用户信息端点应该工作吗?
【发布时间】:2023-03-09 08:31:01
【问题描述】:

我正在使用 IdentityServer4 进行基于令牌的用户登录

用户登录后,我得到access_tokenrefresh_token

当用户尝试注销时,我调用 IdentityServer4 的 /connect/revocation 端点

但即使在令牌被撤销后,当我调用/connect/userinfo 端点时,我也能够获取用户信息。

我应该不会出错吗?

这是我如何实现RevokeToken

    protected async Task RevokeRefreshToken(string refreshToken)
    {
        var httpClient = new HttpClient();
        var refreshTokenRevokeResponse = await httpClient.RevokeTokenAsync(new TokenRevocationRequest
        {
            Address = identityServer.IdpRevocationEndPoint,
            ClientId = identityServer.IdpClientId,
            ClientSecret = identityServer.IdpClientSecret,
            Token = refreshToken,
            TokenTypeHint = "refresh_token"
        }).ConfigureAwait(false);
        if (refreshTokenRevokeResponse.IsError)
        {
            Utils.Log(refreshTokenRevokeResponse.Error, refreshTokenRevokeResponse.Exception);
        }
    }

    protected async Task RevokeAccessToken(string accessToken)
    {
        var httpClient = new HttpClient();
        var accessTokenRevokeResponse = await httpClient.RevokeTokenAsync(new TokenRevocationRequest
        {
            Address = identityServer.IdpRevocationEndPoint,
            ClientId = identityServer.IdpClientId,
            ClientSecret = identityServer.IdpClientSecret,
            Token = accessToken,               
        }).ConfigureAwait(false);
        if (accessTokenRevokeResponse.IsError)
        {
            Utils.Log(accessTokenRevokeResponse.Error, accessTokenRevokeResponse.Exception);
        }
    }

先调用撤销的函数调用RevokeRefreshToken(string refreshToken)然后调用RevokeAccessToken(string accessToken) 这是identityserver中的客户端配置

       new Client
        {
            ClientId = "client_foo",
            ClientName = "foo client",
            ClientSecrets =
            {
                new Secret("secret".ToSha256())
            },
            AllowedGrantTypes = GrantTypes.ResourceOwnerPasswordAndClientCredentials,
            AccessTokenLifetime = 3600,
            IdentityTokenLifetime = 3600,
            AllowOfflineAccess = true,
            RequirePkce = false,
            RequireClientSecret = false,
            RequireConsent = false,
            Enabled = true,
            RedirectUris = redirectUris,
            PostLogoutRedirectUris = postLogoutRedirectUris,
            AllowedCorsOrigins = allowedCorsOrigins,
            AlwaysIncludeUserClaimsInIdToken = true,
            UserSsoLifetime = 3600,
            RefreshTokenExpiration = TokenExpiration.Absolute,
            RefreshTokenUsage = TokenUsage.ReUse,
            AllowedScopes =
            {
                IdentityServerConstants.StandardScopes.OpenId,
                IdentityServerConstants.StandardScopes.Profile,
                IdentityServerConstants.StandardScopes.OfflineAccess,
                "firstName",
                "lastName",
                "emailId",
            }
        }

这是来自IdentityServer的日志文件

2019-05-14 12:25:28.950 +02:00 [DBG] Start discovery request
2019-05-14 12:26:19.370 +02:00 [DBG] Request path /connect/revocation matched to endpoint type Revocation
2019-05-14 12:26:19.383 +02:00 [DBG] Endpoint enabled: Revocation, successfully created handler: IdentityServer4.Endpoints.TokenRevocationEndpoint
2019-05-14 12:26:19.383 +02:00 [INF] Invoking IdentityServer endpoint: IdentityServer4.Endpoints.TokenRevocationEndpoint for /connect/revocation
2019-05-14 12:26:19.389 +02:00 [DBG] Start revocation request.
2019-05-14 12:26:19.389 +02:00 [DBG] Start client validation
2019-05-14 12:26:19.389 +02:00 [DBG] Start parsing Basic Authentication secret
2019-05-14 12:26:19.390 +02:00 [DBG] Start parsing for secret in post body
2019-05-14 12:26:19.391 +02:00 [DBG] Parser found secret: PostBodySecretParser
2019-05-14 12:26:19.391 +02:00 [DBG] Secret id found: client_foo
2019-05-14 12:26:19.392 +02:00 [DBG] client configuration validation for client client_foo succeeded.
2019-05-14 12:26:19.392 +02:00 [DBG] Public Client - skipping secret validation success
2019-05-14 12:26:19.405 +02:00 [DBG] Client validation success
2019-05-14 12:26:19.410 +02:00 [DBG] Token type hint found in request: refresh_token
2019-05-14 12:26:19.410 +02:00 [DBG] ValidateRequestAsync result: IdentityServer4.Validation.TokenRevocationRequestValidationResult
2019-05-14 12:26:19.426 +02:00 [DBG] Refresh token revoked
2019-05-14 12:26:19.432 +02:00 [INF] Token successfully revoked
2019-05-14 12:27:06.311 +02:00 [DBG] Request path /connect/revocation matched to endpoint type Revocation
2019-05-14 12:27:06.324 +02:00 [DBG] Endpoint enabled: Revocation, successfully created handler: IdentityServer4.Endpoints.TokenRevocationEndpoint
2019-05-14 12:27:06.325 +02:00 [INF] Invoking IdentityServer endpoint: IdentityServer4.Endpoints.TokenRevocationEndpoint for /connect/revocation
2019-05-14 12:27:06.325 +02:00 [DBG] Start revocation request.
2019-05-14 12:27:06.325 +02:00 [DBG] Start client validation
2019-05-14 12:27:06.325 +02:00 [DBG] Start parsing Basic Authentication secret
2019-05-14 12:27:06.325 +02:00 [DBG] Start parsing for secret in post body
2019-05-14 12:27:06.350 +02:00 [DBG] Parser found secret: PostBodySecretParser
2019-05-14 12:27:06.350 +02:00 [DBG] Secret id found: client_foo
2019-05-14 12:27:06.350 +02:00 [DBG] client configuration validation for client client_foo succeeded.
2019-05-14 12:27:06.350 +02:00 [DBG] Public Client - skipping secret validation success
2019-05-14 12:27:06.350 +02:00 [DBG] Client validation success
2019-05-14 12:27:06.350 +02:00 [DBG] ValidateRequestAsync result: IdentityServer4.Validation.TokenRevocationRequestValidationResult
2019-05-14 12:27:06.353 +02:00 [DBG] reference_token grant with value: eyJhbGciOiJSUzI1NiIsImtpZCI6ImY2YWM5MmZiOTlhNzRhYzcyMTFjYTE5ZjE3YTNlMjQyIiwidHlwIjoiSldUIn0.eyJuYmYiOjE1NTc4Mjk1MjMsImV4cCI6MTU1NzgzMzEyMywiaXNzIjoiaHR0cHM6Ly9sb2NhbGhvc3Q6NDQzNjciLCJhdWQiOiJodHRwczovL2xvY2FsaG9zdDo0NDM2Ny9yZXNvdXJjZXMiLCJjbGllbnRfaWQiOiJlcEliZUNsaWVudFNlcnZlclNpZGUiLCJzdWIiOiIxNzI5IiwiYXV0aF90aW1lIjoxNTU3ODI5NTIxLCJpZHAiOiJsb2NhbCIsImVtYWlsIjoiYmtjQG9ubS5kZSIsImZpcnN0TmFtZSI6IkJpcGxvdiIsImxhc3ROYW1lIjoiS0MiLCJsYW5ndWFnZSI6IkUiLCJsZXR0ZXJHcmVldGluZyI6Ik1vbnNpZXVyIiwic2NvcGUiOlsib3BlbmlkIiwicHJvZmlsZSIsIm9mZmxpbmVfYWNjZXNzIl0sImFtciI6WyJwd2QiXX0.ZkJU1rYHJYOjgK3_NKcKHfcoDjT1E8iOft1qXw2_1xpHYB2oKM9CosXNuAJ52VmSDXbXIKoLTDvPKl5UXs4ZwO5rGwwFYGyel-tXiAz_mLwSEZuO3tq2c5b7tehhDndQBu7Pu--JeQdkruhMx9NenCwqLXK5dewTNnH7WpnolHvzyLWhbioBg5WTAEgiIQYLOi_G5pBHUwo_RlpihLwPHYzkocv4vIjKTTV26pcLryP3sKQ7btsv7H6htFrx42Nppi28cRIbcxc_jTLqbriB_HNgyBD_W7qRZ6CDnmYWTJdAqJiYGbUcu0SYXN0-2HrI71lXJhsoJ4lMxki8RQDsqA not found in store.
2019-05-14 12:27:06.353 +02:00 [DBG] refresh_token grant with value: eyJhbGciOiJSUzI1NiIsImtpZCI6ImY2YWM5MmZiOTlhNzRhYzcyMTFjYTE5ZjE3YTNlMjQyIiwidHlwIjoiSldUIn0.eyJuYmYiOjE1NTc4Mjk1MjMsImV4cCI6MTU1NzgzMzEyMywiaXNzIjoiaHR0cHM6Ly9sb2NhbGhvc3Q6NDQzNjciLCJhdWQiOiJodHRwczovL2xvY2FsaG9zdDo0NDM2Ny9yZXNvdXJjZXMiLCJjbGllbnRfaWQiOiJlcEliZUNsaWVudFNlcnZlclNpZGUiLCJzdWIiOiIxNzI5IiwiYXV0aF90aW1lIjoxNTU3ODI5NTIxLCJpZHAiOiJsb2NhbCIsImVtYWlsIjoiYmtjQG9ubS5kZSIsImZpcnN0TmFtZSI6IkJpcGxvdiIsImxhc3ROYW1lIjoiS0MiLCJsYW5ndWFnZSI6IkUiLCJsZXR0ZXJHcmVldGluZyI6Ik1vbnNpZXVyIiwic2NvcGUiOlsib3BlbmlkIiwicHJvZmlsZSIsIm9mZmxpbmVfYWNjZXNzIl0sImFtciI6WyJwd2QiXX0.ZkJU1rYHJYOjgK3_NKcKHfcoDjT1E8iOft1qXw2_1xpHYB2oKM9CosXNuAJ52VmSDXbXIKoLTDvPKl5UXs4ZwO5rGwwFYGyel-tXiAz_mLwSEZuO3tq2c5b7tehhDndQBu7Pu--JeQdkruhMx9NenCwqLXK5dewTNnH7WpnolHvzyLWhbioBg5WTAEgiIQYLOi_G5pBHUwo_RlpihLwPHYzkocv4vIjKTTV26pcLryP3sKQ7btsv7H6htFrx42Nppi28cRIbcxc_jTLqbriB_HNgyBD_W7qRZ6CDnmYWTJdAqJiYGbUcu0SYXN0-2HrI71lXJhsoJ4lMxki8RQDsqA not found in store.
2019-05-14 12:27:06.353 +02:00 [INF] No matching token found
2019-05-14 12:28:49.765 +02:00 [DBG] CORS request made for path: /.well-known/openid-configuration from origin: https://localhost
2019-05-14 12:28:49.765 +02:00 [DBG] Client list checked and origin: https://localhost is allowed
2019-05-14 12:28:49.765 +02:00 [DBG] CorsPolicyService allowed origin: https://localhost
2019-05-14 12:28:49.765 +02:00 [DBG] Request path /.well-known/openid-configuration matched to endpoint type Discovery
2019-05-14 12:28:49.765 +02:00 [DBG] Endpoint enabled: Discovery, successfully created handler: IdentityServer4.Endpoints.DiscoveryEndpoint
2019-05-14 12:28:49.765 +02:00 [INF] Invoking IdentityServer endpoint: IdentityServer4.Endpoints.DiscoveryEndpoint for /.well-known/openid-configuration
2019-05-14 12:28:49.765 +02:00 [DBG] Start discovery request
2019-05-14 12:29:23.761 +02:00 [DBG] Request path /connect/userinfo matched to endpoint type Userinfo
2019-05-14 12:29:23.775 +02:00 [DBG] Endpoint enabled: Userinfo, successfully created handler: IdentityServer4.Endpoints.UserInfoEndpoint
2019-05-14 12:29:23.775 +02:00 [INF] Invoking IdentityServer endpoint: IdentityServer4.Endpoints.UserInfoEndpoint for /connect/userinfo
2019-05-14 12:29:23.780 +02:00 [DBG] Start userinfo request
2019-05-14 12:29:23.785 +02:00 [DBG] Bearer token found in header
2019-05-14 12:29:23.811 +02:00 [DBG] client configuration validation for client client_foo succeeded.
2019-05-14 12:29:23.814 +02:00 [DBG] client configuration validation for client client_foo succeeded.
2019-05-14 12:29:24.722 +02:00 [DBG] Calling into custom token validator: IdentityServer4.Validation.DefaultCustomTokenValidator
2019-05-14 12:29:24.723 +02:00 [DBG] Token validation success
{"ClientId":null,"ClientName":null,"ValidateLifetime":true,"AccessTokenType":"Jwt","ExpectedScope":"openid","TokenHandle":null,"JwtId":null,"Claims":{"nbf":1557829523,"exp":1557833123,"iss":"https://localhost:44367","aud":"https://localhost:44367/resources","client_id":"client_foo","sub":"1729","auth_time":1557829521,"idp":"local","email":"foo@foo.com","firstName":"foo","lastName":"bar","language":"E","letterGreeting":"Monsieur","scope":["openid","profile","offline_access"],"amr":"pwd"},"$type":"TokenValidationLog"}
2019-05-14 12:29:25.622 +02:00 [DBG] Creating userinfo response
2019-05-14 12:29:25.626 +02:00 [DBG] Scopes in access token: openid profile offline_access
2019-05-14 12:29:25.628 +02:00 [DBG] Scopes in access token: openid profile offline_access
2019-05-14 12:29:25.630 +02:00 [DBG] Requested claim types: sub name family_name given_name middle_name nickname preferred_username profile picture website gender birthdate zoneinfo locale updated_at offline_access
2019-05-14 12:29:25.630 +02:00 [DBG] Scopes in access token: openid profile offline_access
2019-05-14 12:29:26.484 +02:00 [INF] Profile service returned the following claim types: sub email firstName lastName language letterGreeting

【问题讨论】:

    标签: c# jwt identityserver4 access-token


    【解决方案1】:

    这里有几点需要注意:

    1. JWT 无法撤销
    2. 只能撤销参考令牌

    考虑一下,您有 两个客户:“A”和“B”。

    现在,客户端“A”调用 /connect/token 端点以获取 access_token

    /connect/revocation 可以被两个客户端调用。

    例如(client_id=B):

    [POST]
    https://localhost:44367/connect/revocation
    [Header]
    Content-Type: application/x-www-form-urlencoded
    [Request Body]//line break for easy readability purpose only
    token=aac0ebb65a999067beda705d478879e915c9f0699582afbcdb5f94fae9210a81
    &token_type_hint=access_token
    &client_id=B
    

    这将给出 200OK 响应

    现在,如果您调用 /connect/userinfo 端点,即使在从撤销端点获得 200OK 响应后,您仍然可以获取用户信息。

    但是,如果您使用 client_id 作为 A 调用撤销端点

    例如(client_id=A):

    [POST]
    https://localhost:44367/connect/revocation
    [Header]
    Content-Type: application/x-www-form-urlencoded
    [Request Body]//line break for easy readability purpose only
    token=aac0ebb65a999067beda705d478879e915c9f0699582afbcdb5f94fae9210a81
    &token_type_hint=access_token
    &client_id=A
    

    这也会给出 200OK 响应。

    如果您在撤销后致电/connect/userinfo。现在这将给出unauthorized 响应。这似乎是正确的。

    我不知道这个设计选择是故意的还是IdentityServer4中的一个错误

    【讨论】:

    • 是的,如果在 PersistedGrants 表中找到令牌,端点总是返回 200 ok。此外,客户端 A 和客户端 B 的问题是因为您无法从其他客户端撤消访问令牌。在日志中,您会发现类似“客户端 B 拒绝撤销属于客户端 A 的访问令牌”的内容
    猜你喜欢
    • 2015-12-08
    • 2018-09-21
    • 2019-04-12
    • 1970-01-01
    • 1970-01-01
    • 2015-07-08
    • 2017-06-15
    • 2014-03-29
    • 2014-06-06
    相关资源
    最近更新 更多