簡體   English   中英

如何在oAuth 2.0 / owin中自定義JWT令牌驗證?

[英]How to Customize JWT token validation in oAuth 2.0 / owin?

我正在嘗試使用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

    }
}

在繼承IOAuthBearerAuthenticationProvider的CustomOAuthBearerProvider中,我提供了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的過期時間而無需更改原始令牌)。

請發表評論,無論您認為什么想法/建議/好/不好的做法/鏈接都將有所幫助。 謝謝。

實際上,我們可以進行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
    }

}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM