2016-04-14 117 views
2

我想驗證使用oAuth 2.0中間件的智威湯遜。我想在我的Startup.cs類使用自定義提供者:如何在oAuth 2.0/owin中自定義JWT令牌驗證?

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過期時間的任何內容)。

請評論,無論你的想法/建議/良好的不良練習選項/鏈接你會覺得有幫助。 謝謝。

回答

3

其實我們可以做JWT的自定義驗證。我創建了一個沒有到期時間的JWT,並通過它的簽名對其進行了驗證,當我們在Jwt中保持到期時間時,也可以做到這一點。 現在前面一樣,而是採用JWTBearerAuthentication,我們可以使用OAuthBearerAuthentication象下面這樣:

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類創建您的智威湯遜通過撤消方法,該方法是驗證你的智威湯遜在ISecureDataFormat接口中聲明:

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 
    } 

}