如何自定义oAuth 2.0 / owin中的JWT令牌验证?

4

我正在尝试使用oAuth 2.0中间件验证JWT。我尝试在Startup.cs类中使用自定义提供程序:

 public class Startup
{
    public void Configuration(IAppBuilder app)
    {
        HttpConfiguration config = new HttpConfiguration();

        // Web API routes
        config.MapHttpAttributeRoutes();

        ConfigureOAuth(app);

        app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);

        app.UseWebApi(config);

    }

    public void ConfigureOAuth(IAppBuilder app)
    {

        OAuthAuthorizationServerOptions OAuthServerOptions = new OAuthAuthorizationServerOptions()
        {
            //For Dev enviroment only (on production should be AllowInsecureHttp = false)
            AllowInsecureHttp = true,
            TokenEndpointPath = new PathString("/oauth2/token"),
            AccessTokenExpireTimeSpan = TimeSpan.FromMinutes(5),
            Provider = new CustomOAuthProvider(),
            AccessTokenFormat = new RMAJwtAuthenticator.CustomJwtFormat("www.abc.com")
        };

        // OAuth 2.0 Bearer Access Token Generation
        app.UseOAuthAuthorizationServer(OAuthServerOptions);

        // start : Code for Validating JWT
        var issuer = "www.abc.com";
        var audience = "www.xyz.com";
        var secret = TextEncodings.Base64Url.Decode("Yuer534553HDS&dsa");

        // Api controllers with an [Authorize] attribute will be validated with JWT
        app.UseJwtBearerAuthentication(
            new JwtBearerAuthenticationOptions
            {
                AuthenticationMode = AuthenticationMode.Active,
                AllowedAudiences = new[] { audience },
                IssuerSecurityTokenProviders = new IIssuerSecurityTokenProvider[]
                {
                    new SymmetricKeyIssuerSecurityTokenProvider(issuer, secret)
                },
                Provider = new CustomOAuthBearerProvider()


            });

        //End: Code for Validating JWT

    }
}

在我的CustomOAuthBearerProvider中,它继承了IOAuthBearerAuthenticationProvider,我提供了ApplyChallenge()、RequestToken()和ValidateIdentity()的定义:

 public class CustomOAuthBearerProvider : IOAuthBearerAuthenticationProvider
{
    public Task ApplyChallenge(OAuthChallengeContext context)
    {            
        return Task.FromResult<object>(null);
    }

    public Task RequestToken(OAuthRequestTokenContext context)
    {            
        return Task.FromResult<object>(null);
    }

    public Task ValidateIdentity(OAuthValidateIdentityContext context)
    {            
        return Task.FromResult<object>(null);
    }
}

现在,当我尝试获取授权资源时,首先会触发RequestToken()方法,然后我不知道JWT是如何验证的并且控制权被传递给ValidateIdentity()方法。

我想自定义验证过程的原因是为了在数据库中保存和扩展我的JWT的过期时间(您也可以建议任何增加JWT过期时间而不更改原始令牌的方法)。

请评论任何有用的思路/建议/良好-不良做法选项/链接,谢谢。

2个回答

4
实际上,我们可以自定义验证JWT。我创建了一个没有过期时间的JWT,并通过其签名进行了验证,当我们在Jwt中保留过期时间时,也可以执行相同的操作。 现在,与以前不同的是,我们可以使用OAuthBearerAuthentication而不是JWTBearerAuthentication,如下所示:
 public void ConfigureOAuth(IAppBuilder app)
    {
        OAuthAuthorizationServerOptions OAuthServerOptions = new OAuthAuthorizationServerOptions()
        {
            //For Dev enviroment only (on production should be AllowInsecureHttp = false)
            AllowInsecureHttp = true,
            TokenEndpointPath = new PathString("/api/token"),
            //provide Expire Time
            //AccessTokenExpireTimeSpan = TimeSpan.FromMinutes(5),
            Provider = new CustomOAuthProvider(),
            //provide issuer name/url
            //
            AccessTokenFormat = new RMAJwtAuthenticator.CustomJwtFormat("www.abc.com")
        };

        // OAuth 2.0 Bearer Access Token Generation
        app.UseOAuthAuthorizationServer(OAuthServerOptions);

        //// start : Code for Validating JWT

        OAuthBearerAuthenticationOptions OAuthBearerOptions = new OAuthBearerAuthenticationOptions()
        {
            AccessTokenFormat = OAuthServerOptions.AccessTokenFormat,
            AccessTokenProvider = OAuthServerOptions.AccessTokenProvider,
            AuthenticationMode = OAuthServerOptions.AuthenticationMode,
            AuthenticationType = OAuthServerOptions.AuthenticationType,
            Description = OAuthServerOptions.Description,
            Provider = new CustomOAuthBearerProvider()
        };
        app.UseOAuthBearerAuthentication(OAuthBearerOptions);

        //////End: Code for Validating JWT

    }

您可以使用同一 CustomJwtFormat 类来创建JWT,并通过ISecureDataFormat接口中声明的 UnProtect 方法验证您的JWT:

 public class CustomJwtFormat : ISecureDataFormat<AuthenticationTicket>
{
    //Needs to be configured in Configuration file
    const string AudiencePropertyKey = "audience";
    const string signatureAlgorithm = "www.w3.org/2001/04/xmldsig-more#hmac-sha256";
    const string digestAlgorithm = "www.w3.org/2001/04/xmlenc#sha256";

    private readonly string _issuer = string.Empty;

    public CustomJwtFormat(string issuer)
    {
        _issuer = issuer;
    }

    /// <summary>
    /// Creates JWT Token here, using AuthenticationTicket
    /// </summary>
    /// <param name="data"></param>
    /// <returns></returns>
    public string Protect(AuthenticationTicket data)
    {
        JwtAuthHelper objJwtAuthHelper = new JwtAuthHelper();
        try
        {
            if (data == null)
            {
                throw new ArgumentNullException("data");
            }

            string audienceId = data.Properties.Dictionary.ContainsKey(AudiencePropertyKey) ? data.Properties.Dictionary[AudiencePropertyKey] : null;

            if (string.IsNullOrWhiteSpace(audienceId)) throw new InvalidOperationException("AuthenticationTicket.Properties does not include audience");

            //check if audience is valid (in case of audience is stored in DB or some list)
            Audience audience = AudiencesStore.FindAudience(audienceId);

            //In case , if each audience has separate secretKey
            //Right now we have a common secret key
            if (audience != null)
            {
                var symmetricKey = TextEncodings.Base64Url.Decode(audience.EncryptedSecret);//any encrypted (or simple) key from 3rd party client

                //***added refernce of System.IdenityModel to get SigningCredentials class refernce
                // instead of using ThinkTecture nugget packaged dlls
                var SigningCredentials = new SigningCredentials(new InMemorySymmetricSecurityKey(symmetricKey), signatureAlgorithm, digestAlgorithm);

                var issued = data.Properties.IssuedUtc;
                var expires = data.Properties.ExpiresUtc;

                //Modified to keep issued and expirey time as NULL
                var token = new JwtSecurityToken(_issuer, audienceId, data.Identity.Claims, null, null, SigningCredentials);
               //var token = new JwtSecurityToken(_issuer, audienceId, data.Identity.Claims, issued.Value.UtcDateTime, expires.Value.UtcDateTime, SigningCredentials);

                var handler = new JwtSecurityTokenHandler();

                var jwt = handler.WriteToken(token);

            }

            return string.Empty;
        }
        catch (Exception)
        {
            throw;
        }
    }

    /// <summary>
    /// UnProtect ticket : Validates JWT
    /// </summary>
    /// <param name="protectedText"></param>
    /// <returns></returns>
    public AuthenticationTicket Unprotect(string protectedText)
    {
        // start : Code for Validating JWT                       

        //JwtSecurityTokenHandler
        System.IdentityModel.Tokens.JwtSecurityTokenHandler tokenHandler = new System.IdentityModel.Tokens.JwtSecurityTokenHandler();
        System.Security.Claims.ClaimsPrincipal claimsPrincipal;

        try
        {
            System.IdentityModel.Tokens.JwtSecurityToken tokenReceived = new System.IdentityModel.Tokens.JwtSecurityToken(protectedText);

            //Configure Validation parameters// Now its Generalized//token must have issuer and audience
            var issuer = tokenReceived.Issuer; 
            List<string> strAudience = (List<string>)tokenReceived.Audiences;
            var audience = strAudience.Count > 0 ? strAudience[0].ToString(): string.Empty;
            Audience audForContext = AudiencesStore.FindAudience(audience);
            var symmetricKey = Microsoft.Owin.Security.DataHandler.Encoder.TextEncodings.Base64Url.Decode(audForContext.EncryptedSecret);

            var validationParameters = new System.IdentityModel.Tokens.TokenValidationParameters()
            {
                ValidAudience = audience,
                IssuerSigningKey = new System.IdentityModel.Tokens.InMemorySymmetricSecurityKey(symmetricKey),
                ValidIssuer = issuer,
                RequireExpirationTime = false
            };

            System.IdentityModel.Tokens.SecurityToken validatedToken;                
            //if token gets validated claimsPrincipal has value otherwise it throws exception                
            claimsPrincipal = tokenHandler.ValidateToken(protectedText, validationParameters, out validatedToken);

            var props = new AuthenticationProperties(new Dictionary<string, string> { { "audience", audience } });
            var ticket = new AuthenticationTicket((System.Security.Claims.ClaimsIdentity)claimsPrincipal.Identity, props);
            return ticket;
        }
        catch (Exception)
        {

            throw;
        }

        ////End: Custom code to handle Validate Token
    }

}

0

如果您正在使用外部发行的令牌并且只需要验证,则只需要具有签名密钥解析器。

这已经包含在Microsoft.IdentityModel.Tokens中(请参见此处的示例:https://github.com/auth0/auth0-aspnet-owin/blob/master/src/Auth0.Owin.OpenIdConnectSigningKeyResolver/OpenIdConnectSigningKeyResolver.cs)。

如果使用System.IdentityModel.Tokens.Jwt,则需要更多工作(请参见此处的示例:https://github.com/NaosProject/Naos.Auth)。我编写了一段代码来完成这个任务,可以直接复制(MIT许可证)或使用软件包“Naos.Auth.Recipes.Jwt”安装为混合。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接