Ручная проверка токена от поставщика OIDC без » хорошо известных` метаданных в Asp Net Core 3.1

#asp.net-core #oauth-2.0 #openid-connect #openid

Вопрос:

Я использую поток с Openid, где я перенаправляю своего пользователя к другому поставщику, чтобы позаботиться о входе в систему, и после этого входа я получаю code URL-адрес в моем /логине/обратном вызове?код=xxxx.

Итак, JWT генерируется с использованием code , но я не могу его проверить. У меня нет well-known конечной точки в моей STS, мне нужно настроить ее вручную таким образом:

     services.AddAuthorization(cfg =>
        {
            cfg.AddPolicy("MyPolicy", cfgPolicy =>
            {
                cfgPolicy.AddRequirements().RequireAuthenticatedUser();
                cfgPolicy.AddAuthenticationSchemes(OpenIdConnectDefaults.AuthenticationScheme);
            });
        }).AddAuthentication(cfg =>
        {
            cfg.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
            cfg.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
        })
        .AddCookie()
        .AddOpenIdConnect(cfg =>
        {
            cfg.ClientId = authenticationConfig.ClientId;
            cfg.ClientSecret = authenticationConfig.ClientSecret;
            cfg.ResponseType = "code";
            cfg.CallbackPath = "/login/callback";
            cfg.Scope.Clear();
            cfg.Scope.Add("openid");
            
            cfg.TokenValidationParameters = new TokenValidationParameters
            {
                ValidIssuer = "https://myissuer"
            };

            cfg.Configuration = new OpenIdConnectConfiguration
            {
                AuthorizationEndpoint = "https://mysts/api/oauth/authorize",
                TokenEndpoint = "https://mysts/api/oauth/token",
                UserInfoEndpoint = "https://mysts/api/oauth/token_info"
            };
        });
 

Некоторые важные моменты:

  1. У меня есть конечная точка опроса токена для проверки моего токена (конечная точка token_info).
  2. У меня нет конечной точки по умолчанию для возврата открытых ключей (jwks). Моя конечная точка всегда связана с некоторыми значениями, что-то в этом роде … > https://mysts/offline/jwks/{kid}/{clientid}, так что это динамично и зависит от токена.
  3. У меня нет хорошо известной конечной точки.

Комментарии:

1. Не могли бы вы, пожалуйста, сказать мне, в чем ваш вопрос? Вы хотите, чтобы мы помогли вам в чем ?

2. «у меня есть конечная точка самоанализа токенов», вы также можете отправить каждый токен в конечную точку самоанализа для проверки, и это, вероятно, будет проще реализовать, но в конечном итоге будет медленнее с точки зрения выполнения (учитывая, что это синхронизация по асинхронности и все такое).

Ответ №1:

Вы не можете подтвердить это, потому что, ASP.NET Ядро пытается проверить подпись JWT по умолчанию. Однако, поскольку вы сами настроили метаданные, а не дали a JwksUri , у него нет возможности получить открытый ключ поставщика OIDC для проверки подписи.

Вы можете указать JwksUri или отключить проверку подписи (НЕБЕЗОПАСНО), или проверить ее самостоятельно. Если у вас есть доступ к открытому ключу, каким-то образом используйте options.SecurityTokenValidator и реализуйте a ISecurityTokenValidator для пользовательской проверки.

Вот (не проверенная) реализация, которая динамически извлекает JWKS и проверяет токен:

 public void ConfigureServices(IServiceCollection services)
{
    services.AddAuthentication()
        .AddOpenIdConnect(options => {
            options.SecurityTokenValidator = new MyJwtValidator(Configuration);
        });
}

class MyJwtValidator: ISecurityTokenValidator
{
    private readonly IConfiguration _configuration;
    private readonly HttpClient _httpClient;
    private readonly JwtSecurityTokenHandler _tokenHandler;

    public MyJwtValidator(IConfiguration configuration)
    {
        _configuration = configuration;
        _tokenHandler = new JwtSecurityTokenHandler();
        _httpClient = new HttpClient
        {
            BaseAddress = new Uri(_configuration.GetSection("OidcProvider").Get<string>())
        };
    }

    public bool CanReadToken(string securityToken) => true;

    public ClaimsPrincipal ValidateToken(
        string securityToken,
        TokenValidationParameters validationParameters,
        out SecurityToken validatedToken
    )
    {
        // parse the token (without validating) to extract a value
        var parsedToken = new JwtSecurityToken(securityToken);
        var keyId = parsedToken.Claims.First(c => c.Type == "kid").Value;
        
        // fetch JWKS and validate the token
        var clientId = _configuration.GetSection("OidcProvider:ClientId").Get<string>();
        var jwks = _httpClient.GetStringAsync($"url/to/jwks/{keyId}/{clientId}").Resu<
        // jwks == "{ keys: [..."
        var signingKeys = new JsonWebKeySet(jwks).GetSigningKeys();

        return _tokenHandler.ValidateToken(securityToken, new TokenValidationParameters
        {
            IssuerSigningKeys = signingKeys
        }, out validatedToken);
    }

    public bool CanValidateToken { get; } = true;
    public int MaximumTokenSizeInBytes { get; set; } = int.MaxValue;
}
 

Он выполняет синхронизацию по асинхронности, что не одобряется, но вы можете кэшировать ключи, чтобы избежать уплаты штрафа.

Комментарии:

1. Спасибо, я буду использовать это решение, чтобы сделать свой собственный токен проверки.