如何在后端验证身份提供者的身份令牌

How to validate id token from identity provider on backend

我正在开发应用程序,它由 angular 前端和 ASP 网络 API 后端(.net 4.5)组成。对于身份验证,我使用 OpenIdConnect。我已成功将前端连接到身份提供者,但现在我需要在后端验证 ID 令牌,因此我可以确定,只有经过验证的用户才能调用后端。

此id令牌使用rs256算法进行签名。所以在后端,我需要做两件事:

  1. 从身份提供者那里获取 JWKs URL - 我在这里有点迷路,我应该通过普通的 HttpClient 获取它,还是有一些库或辅助函数可以做到这一点?

  2. 从 JWK 生成 RSA public 密钥并验证令牌 - 为此 iam 使用此函数:

         string token="xyz..";
         RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
    
         rsa.ImportParameters(
           new RSAParameters()
           {
               Modulus = FromBase64Url("xyz.."),
               Exponent = FromBase64Url("xyz..")
           });
    
         var validationParameters = new TokenValidationParameters
         {
             RequireExpirationTime = true,
             RequireSignedTokens = true,
             ValidateAudience = false,
             ValidateIssuer = false,
             ValidateLifetime = true,
             IssuerSigningKey = new RsaSecurityKey(rsa)
         };
    
         SecurityToken validatedSecurityToken = null;
         var handler = new JwtSecurityTokenHandler();
         handler.ValidateToken(tokenStr, validationParameters, out validatedSecurityToken);
         JwtSecurityToken validatedJwt = validatedSecurityToken as JwtSecurityToken;
    

它可以工作,但现在我需要以某种方式将它与加载的 JWK 连接起来,并注册它以便为每个出现的请求使用它。任何建议或简单的例子都会对我有帮助。谢谢

下面这段代码摘自我的一个培训类,它会自动下载并验证提供的令牌,希望您可以作为参考。您通常会使用 ConfigurationManager 为您下载 IdentityServer 配置和 JWKS。它还将在内部缓存并定期(每 24 次)读取配置。

using Microsoft.IdentityModel.Protocols;
using Microsoft.IdentityModel.Protocols.OpenIdConnect;
using Microsoft.IdentityModel.Tokens;
using OpenID_Connect_client.Models;
using System;
using System.IdentityModel.Tokens.Jwt;
using System.Linq;
using System.Security.Claims;
using System.Threading;

namespace OpenID_Connect_client
{
    public class TokenValidator
    {
        private readonly IOpenIDSettings openIDSettings;

        public TokenValidator(IOpenIDSettings openIDSettings)
        {
            this.openIDSettings = openIDSettings;
        }

        public string ValidateToken(string token, string clientId)
        {
            try
            {
                string issuer = openIDSettings.Issuer;

                var configurationManager = new ConfigurationManager<OpenIdConnectConfiguration>($"{issuer}/.well-known/openid-configuration", new OpenIdConnectConfigurationRetriever());
                var openIdConfig = configurationManager.GetConfigurationAsync(CancellationToken.None).Result;

                // Configure the TokenValidationParameters. Assign the SigningKeys which were downloaded from Auth0. 
                // Also set the Issuer and Audience(s) to validate
                //https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/blob/dev/src/Microsoft.IdentityModel.Tokens/TokenValidationParameters.cs
                var validationParameters =
                    new TokenValidationParameters
                    {
                        IssuerSigningKeys = openIdConfig.SigningKeys,

                        ValidAudiences = new[] { clientId },
                        ValidIssuer = issuer,
                        ValidateLifetime = true,
                        ValidateAudience = true,
                        ValidateIssuer = true,
                        ValidateIssuerSigningKey = true,
                        ValidateTokenReplay = true
                    };

                // Now validate the token. If the token is not valid for any reason, an exception will be thrown by the method
                SecurityToken validatedToken;
                JwtSecurityTokenHandler handler = new JwtSecurityTokenHandler();
                var user = handler.ValidateToken(token, validationParameters, out validatedToken);

                // The ValidateToken method above will return a ClaimsPrincipal. Get the user ID from the NameIdentifier claim
                // (The sub claim from the JWT will be translated to the NameIdentifier claim)
                return $"Token is validated. User Id {user.Claims.FirstOrDefault(c => c.Type == ClaimTypes.NameIdentifier)?.Value}";
            }
            catch (Exception exc)
            {
                return "Invalid token: " + exc.Message;
            }
        }


    }
}