2017-01-24 5 views
1

Я в тупике от этого поведения. В принципе, когда запрашивается обновление токена, WebAPI проходит через логику удаления старого refresh_token из данных, и я не вижу ошибок в отладчике. Однако API возвращает HTTP 400 с «invalid_grant» в качестве ошибки.C# WebAPI Owin jwt RefreshToken invalid_grant

Startup.cs

// OAuth Server configuration 
OAuthAuthorizationServerOptions OAuthServerOptions = new OAuthAuthorizationServerOptions() 
{ 
    AllowInsecureHttp = false, 
    TokenEndpointPath = new PathString("/oauth2/token"), 
    AccessTokenExpireTimeSpan = TimeSpan.FromMinutes(1), 
    AccessTokenFormat = new AccessTokenJwtFormat(issuer), 
    RefreshTokenProvider = new SimpleRefreshTokenProvider(), 
    RefreshTokenFormat = new RefreshTokenJwtFormat(issuer), 
    Provider = new CustomOAuthProvider() 
}; 

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

SimpleRefreshTokenProvider.cs

public class SimpleRefreshTokenProvider : IAuthenticationTokenProvider 
{ 

    public async Task CreateAsync(AuthenticationTokenCreateContext context) 
    { 
     var clientid = context.Ticket.Properties.Dictionary["as:client_id"]; 

     if (string.IsNullOrEmpty(clientid)) 
     { 
      return; 
     } 

     var refreshTokenId = Guid.NewGuid().ToString("n"); 
     var refreshTokenLifeTime = context.OwinContext.Get<string>("as:clientRefreshTokenLifeTime"); 

     var _repo = ClientDbProvider.GetInstance(); 
     var token = new RefreshToken() 
     { 
      Id = ClientHelper.GetHash(refreshTokenId), 
      ClientId = clientid, 
      Subject = context.Ticket.Identity.Name, 
      IssuedUtc = DateTime.UtcNow, 
      ExpiresUtc = DateTime.UtcNow.AddMinutes(Convert.ToDouble(refreshTokenLifeTime)) 
     }; 

     context.Ticket.Properties.IssuedUtc = new DateTimeOffset(token.IssuedUtc); 
     context.Ticket.Properties.ExpiresUtc = new DateTimeOffset(token.ExpiresUtc); 
     context.Ticket.Properties.Dictionary.Add("refreshTokenId", refreshTokenId); 
     context.Ticket.Identity.AddClaim(new Claim(ClaimTypes.Role, "refreshToken")); 
     token.ProtectedTicket = context.SerializeTicket(); 
     var result = await _repo.AddRefreshToken(token); 
     if (result) 
     { 
      context.SetToken(token.ProtectedTicket); 
     } 

     return; 
    } 

    public async Task ReceiveAsync(AuthenticationTokenReceiveContext context) 
    { 

     var allowedOrigin = context.OwinContext.Get<string>("as:clientAllowedOrigin"); 
     context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { allowedOrigin }); 

     var _repo = ClientDbProvider.GetInstance(); 
     string hashedTokenId = ClientHelper.GetHash(context.Token); 
     var refreshToken = await _repo.FindRefreshToken(hashedTokenId); 

     if (refreshToken != null) 
     { 
      //Get protectedTicket from refreshToken class 
      context.DeserializeTicket(refreshToken.ProtectedTicket); 
      var result = await _repo.RemoveRefreshToken(hashedTokenId); 
     } 
    } 

    public void Create(AuthenticationTokenCreateContext context) 
    { 
     throw new NotImplementedException(); 
    } 

    public void Receive(AuthenticationTokenReceiveContext context) 
    { 
     throw new NotImplementedException(); 
    } 
} 

RefreshTokenJwtFormat.cs

public class RefreshTokenJwtFormat : ISecureDataFormat<AuthenticationTicket> 
    { 
     private const string AudiencePropertyKey = "audience"; 

     private readonly string _issuer = string.Empty; 

     public RefreshTokenJwtFormat(string issuer) 
     { 
      _issuer = issuer; 
      audStore = new AudiencesStore(); 
     } 

     public string Protect(AuthenticationTicket data) 
     { 
      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"); 

      var audience = GetAudience(audienceId); 
      string symmetricKeyAsBase64 = audience.Base64Secret; 

      var keyByteArray = TextEncodings.Base64Url.Decode(symmetricKeyAsBase64); 
      var signingCredentials = new SigningCredentials(
        new InMemorySymmetricSecurityKey(keyByteArray), 
        SecurityAlgorithms.HmacSha256Signature, 
        SecurityAlgorithms.Sha256Digest); 

      var issued = data.Properties.IssuedUtc; 
      var expires = data.Properties.ExpiresUtc; 
      var payload = new JwtPayload(_issuer, audienceId, data.Identity.Claims, issued.Value.UtcDateTime, expires.Value.UtcDateTime); 
      if (data.Properties.Dictionary.ContainsKey("refreshTokenId")) 
      { 
       payload.Add("refreshTokenId", data.Properties.Dictionary["refreshTokenId"]); 
      } 

      var header = new JwtHeader(signingCredentials); 
      var token = new JwtSecurityToken(header, payload); 

      var handler = new JwtSecurityTokenHandler(); 

      var jwt = handler.WriteToken(token); 

      return jwt; 
     } 

     public AuthenticationTicket Unprotect(string protectedText) 
     { 
      var handler = new JwtSecurityTokenHandler(); 
      SecurityToken securityToken = handler.ReadToken(protectedText); 
      var audienceId = ((JwtSecurityToken)securityToken).Claims.First(x => x.Type == "aud").Value; 
      var audience = GetAudience(audienceId); 

      string symmetricKeyAsBase64 = audience.Base64Secret; 
      var keyByteArray = TextEncodings.Base64Url.Decode(symmetricKeyAsBase64); 
      var securityKey = new InMemorySymmetricSecurityKey(keyByteArray); 

      var validationParameters = new TokenValidationParameters() 
      { 
       ValidateIssuerSigningKey = true, 
       IssuerSigningKey = securityKey, 
       ValidateLifetime = true, 
       ClockSkew = TimeSpan.Zero, 
       ValidateAudience = true, 
       ValidAudience = audienceId, 
       ValidateIssuer = true, 
       ValidIssuer = _issuer 
      }; 

      SecurityToken validatedToken; 
      ClaimsPrincipal principal = null; 
      try 
      { 
       principal = handler.ValidateToken(protectedText, validationParameters, out validatedToken); 
      } 
      catch(Exception ex) 
      { 
       return null; 
      } 

      return new AuthenticationTicket(principal.Identities.First(), new AuthenticationProperties()); 
     } 

     private Models.Audience GetAudience(string audienceId) 
     { 
      var findAudTask = Task.Run(() => audStore.FindAudienceAsync(audienceId)); 
      findAudTask.Wait(); 
      var audience = findAudTask.Result; 
      return audience; 
     } 

     private AudiencesStore audStore; 
    } 
+0

вы нашли какие-либо решения? тоже самое –

ответ

1

у меня есть аналогичный (рабочий) код. Я не специалист по этому вопросу, но я по сравнению мой код немного и заметил:

в SimpleRefreshToken.cs:

в receiveAsync, мой код заканчивается:

context.SetTicket(context.Ticket); 
    return Task.FromResult<object>(null); 

который устанавливает билет и вернуть значение.

То же самое для createAsync, мой код заканчивается:

 return Task.FromResult<object>(null); 

Allthough, не знаю, если это поможет

1

Сначала вам нужно реализовать GrantRefreshToken (контекст OAuthGrantRefreshTokenContext) в вашем CustomOAuthProvider что-то вроде

public override Task GrantRefreshToken(OAuthGrantRefreshTokenContext context) 
     { 

      // Change auth ticket for refresh token requests 
      var newIdentity = new ClaimsIdentity(context.Ticket.Identity); 
      //newIdentity.AddClaim(new Claim("newClaim", "newValue")); 

      var newTicket = new AuthenticationTicket(newIdentity, context.Ticket.Properties); 
      context.Validated(newTicket); 

      return Task.FromResult<object>(null); 
     } 

Во-вторых, вам необходимо изменить свой метод Снять защиту в RefreshTokenJwtFormat.

заменить

return new AuthenticationTicket(principal.Identities.First(), new AuthenticationProperties()); 

с

return new AuthenticationTicket(principal.Identities.First(), new AuthenticationProperties 
       { 
        IssuedUtc = validatedToken.ValidFrom, 
        ExpiresUtc = validatedToken.ValidTo 
       });