2015-11-29 4 views
5

После Taiseer Джудеха превосходной статьи
Включить OAuth Обновить лексемы в AngularJS App с помощью ASP .NET Web API 2 и Owin (http://bitoftech.net/2014/07/16/enable-oauth-refresh-tokens-angularjs-app-using-asp-net-web-api-2-owin/) в настоящее время я создаю проверку подлинность на основе маркеров с обновлением маркеров опции.OAuth 2 обновления маркеров invalid_grant

Мой Startup код класса выглядит следующим образом:

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

      ConfigureOAuth(app); 

      WebApiConfig.Register(config); 
      app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll); 
      app.UseWebApi(config); 
     } 

     public void ConfigureOAuth(IAppBuilder app) 
     { 

      OAuthAuthorizationServerOptions oAuthServerOptions = new OAuthAuthorizationServerOptions() 
      { 

       AllowInsecureHttp = true, 
       TokenEndpointPath = new PathString("/token"), 
       AccessTokenExpireTimeSpan = TimeSpan.FromMinutes(30), 
       Provider = new SimpleAuthorizationServerProvider(), 
       RefreshTokenProvider = new SimpleRefreshTokenProvider() 
      }; 

      // Token Generation 
      app.UseOAuthAuthorizationServer(oAuthServerOptions); 
      app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions()); 

     } 
    } 

Мой SimpleAuthorizationServerProvider код класса выглядит следующим образом:

public class SimpleAuthorizationServerProvider : OAuthAuthorizationServerProvider 
    { 
     public override Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context) 
     { 

      string clientId = string.Empty; 
      string clientSecret = string.Empty; 
      Client client = null; 

      if (!context.TryGetBasicCredentials(out clientId, out clientSecret)) 
      { 
       context.TryGetFormCredentials(out clientId, out clientSecret); 
      } 

      if (context.ClientId == null) 
      { 
       //Remove the comments from the below line context.SetError, and invalidate context 
       //if you want to force sending clientId/secrects once obtain access tokens. 
       context.Validated(); 
       //context.SetError("invalid_clientId", "ClientId should be sent."); 
       return Task.FromResult<object>(null); 
      } 

      using (AuthRepository _repo = new AuthRepository()) 
      { 
       client = _repo.FindClient(context.ClientId); 
      } 

      if (client == null) 
      { 
       context.SetError("invalid_clientId", string.Format("Client '{0}' is not registered in the system.", context.ClientId)); 
       return Task.FromResult<object>(null); 
      } 

      if (client.ApplicationType == Models.ApplicationTypes.NativeConfidential) 
      { 
       if (string.IsNullOrWhiteSpace(clientSecret)) 
       { 
        context.SetError("invalid_clientId", "Client secret should be sent."); 
        return Task.FromResult<object>(null); 
       } 
       else 
       { 
        if (client.Secret != Helper.GetHash(clientSecret)) 
        { 
         context.SetError("invalid_clientId", "Client secret is invalid."); 
         return Task.FromResult<object>(null); 
        } 
       } 
      } 

      if (!client.Active) 
      { 
       context.SetError("invalid_clientId", "Client is inactive."); 
       return Task.FromResult<object>(null); 
      } 

      context.OwinContext.Set<string>("as:clientAllowedOrigin", client.AllowedOrigin); 
      context.OwinContext.Set<string>("as:clientRefreshTokenLifeTime", client.RefreshTokenLifeTime.ToString()); 

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

     public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context) 
     { 

      var allowedOrigin = context.OwinContext.Get<string>("as:clientAllowedOrigin"); 

      if (allowedOrigin == null) allowedOrigin = "*"; 

      context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { allowedOrigin }); 
      //var id = ""; 
      using (AuthRepository _repo = new AuthRepository()) 
      { 
       IdentityUser user = await _repo.FindUser(context.UserName, context.Password); 

       if (user == null) 
       { 
        context.SetError("invalid_grant", "The user name or password is incorrect."); 
        return; 
       } 
       //Here set User.Identity.Id = RavenUserId, So rest of the user will be able to get it 
       //id = (user == null ? "0" : user.RavenUserId.ToString()); 
      } 

      var identity = new ClaimsIdentity(context.Options.AuthenticationType); 
      identity.AddClaim(new Claim(ClaimTypes.Name, context.UserName)); 
      //So when we will call User.Identity.Id we will be able to get Raven User Id 
      // identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, id)); 
      identity.AddClaim(new Claim("sub", context.UserName)); 
      identity.AddClaim(new Claim("role", "user")); 

      var props = new AuthenticationProperties(new Dictionary<string, string> 
       { 
        { 
         "as:client_id", (context.ClientId == null) ? string.Empty : context.ClientId 
        }, 
        { 
         "userName", context.UserName 
        } 
       }); 

      var ticket = new AuthenticationTicket(identity, props); 
      context.Validated(ticket); 

     } 

     public override Task GrantRefreshToken(OAuthGrantRefreshTokenContext context) 
     { 
      var originalClient = context.Ticket.Properties.Dictionary["as:client_id"]; 
      var currentClient = context.ClientId; 

      if (originalClient != currentClient) 
      { 
       context.SetError("invalid_clientId", "Refresh token is issued to a different clientId."); 
       return Task.FromResult<object>(null); 
      } 

      // Change auth ticket for refresh token requests 
      var newIdentity = new ClaimsIdentity(context.Ticket.Identity); 

      var newClaim = newIdentity.Claims.Where(c => c.Type == "newClaim").FirstOrDefault(); 
      if (newClaim != null) 
      { 
       newIdentity.RemoveClaim(newClaim); 
      } 
      newIdentity.AddClaim(new Claim("newClaim", "newValue")); 

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

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


     public override Task TokenEndpoint(OAuthTokenEndpointContext context) 
     { 
      foreach (KeyValuePair<string, string> property in context.Properties.Dictionary) 
      { 
       context.AdditionalResponseParameters.Add(property.Key, property.Value); 
      } 

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

Мой SimpleRefreshTokenProvider код класса выглядит следующим образом:

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"); 

      using (var _repo = new AuthRepository()) 
      { 
       var refreshTokenLifeTime = context.OwinContext.Get<string>("as:clientRefreshTokenLifeTime"); 

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

       context.Ticket.Properties.IssuedUtc = token.IssuedUtc; 
       context.Ticket.Properties.ExpiresUtc = token.ExpiresUtc; 

       token.ProtectedTicket = context.SerializeTicket(); 

       var result = await _repo.AddRefreshToken(token); 

       if (result) 
       { 
        context.SetToken(refreshTokenId); 
       } 

      } 
     } 

     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 }); 

      string hashedTokenId = Helper.GetHash(context.Token); 

      using (var _repo = new AuthRepository()) 
      { 
       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(); 
     } 
    } 

Код класса AuthRepository выглядит следующим образом:

public class AuthRepository : IDisposable 
    { 
     private AuthContext _ctx; 

     private UserManager<IdentityUser> _userManager; 

     public AuthRepository() 
     { 
      _ctx = new AuthContext(); 
      _userManager = new UserManager<IdentityUser>(new UserStore<IdentityUser>(_ctx)); 
     } 

     public async Task<IdentityResult> RegisterUser(UserModel userModel) 
     { 
      IdentityUser user = new IdentityUser 
      { 
       UserName = userModel.UserName 
      }; 

      var result = await _userManager.CreateAsync(user, userModel.Password); 

      return result; 
     } 

     public async Task<IdentityUser> FindUser(string userName, string password) 
     { 
      IdentityUser user = await _userManager.FindAsync(userName, password); 

      return user; 
     } 


     public Client FindClient(string clientId) 
     { 
      var client = _ctx.Clients.Find(clientId); 
      //var clients = _ctx.Clients; 
      //var client = _ctx.Clients.FirstOrDefault(x => x.Id==clientId); 
      return client; 
     } 

     public async Task<bool> AddRefreshToken(RefreshToken token) 
     { 

      var existingToken = _ctx.RefreshTokens.Where(r => r.Subject == token.Subject && r.ClientId == token.ClientId).SingleOrDefault(); 

      if (existingToken != null) 
      { 
       var result = await RemoveRefreshToken(existingToken); 
      } 

      _ctx.RefreshTokens.Add(token); 

      return await _ctx.SaveChangesAsync() > 0; 
     } 

     public async Task<bool> RemoveRefreshToken(string refreshTokenId) 
     { 
      var refreshToken = await _ctx.RefreshTokens.FindAsync(refreshTokenId); 

      if (refreshToken != null) 
      { 
       _ctx.RefreshTokens.Remove(refreshToken); 
       return await _ctx.SaveChangesAsync() > 0; 
      } 

      return false; 
     } 

     public async Task<bool> RemoveRefreshToken(RefreshToken refreshToken) 
     { 
      _ctx.RefreshTokens.Remove(refreshToken); 
      return await _ctx.SaveChangesAsync() > 0; 
     } 

     public async Task<RefreshToken> FindRefreshToken(string refreshTokenId) 
     { 
      var refreshToken = await _ctx.RefreshTokens.FindAsync(refreshTokenId); 

      return refreshToken; 
     } 

     public List<RefreshToken> GetAllRefreshTokens() 
     { 
      return _ctx.RefreshTokens.ToList(); 
     } 


     public void Dispose() 
     { 
      _ctx.Dispose(); 
      _userManager.Dispose(); 

     } 
    } 

И Аякса код:

$("#refresh").click(function() { 
       var token = sessionStorage.getItem(tokenKey); 
       var refresh = sessionStorage.getItem('isRefreshToken'); 
       var refreshToken = sessionStorage.getItem('refreshToken'); 

       if (refresh) { 
        var refreshdata = "grant_type=refresh_token&refresh_token=" + refreshToken + "&client_id=TokenBasedAuthentication"; 
        console.log(refreshdata); 

        sessionStorage.setItem(tokenKey, ''); 
        sessionStorage.setItem(isRefreshToken, ''); 
        sessionStorage.setItem(refreshToken, ''); 

        $.ajax({ 
         url: '/token', 
         type: 'POST', 
         data: refreshdata, 
         headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, 
         success: function (data) { 

          sessionStorage.setItem(tokenKey, data.access_token); 
          sessionStorage.setItem(isRefreshToken, true); 
          sessionStorage.setItem(refreshToken, data.refresh_token); 

         }, 
         error: function (xhr) { 
          alert(xhr.status + ': ' + xhr.statusText); 
         } 

        }); 
       } 

      }); 

Наконец, когда я нажимаю на Refresh возвращает меня следующая ошибка ошибку: «invalid_grant»

Последние два дня я пытался выяснить, но не удалось.

+0

Я застрял в той же проблеме. Получили ли вы решение? –

ответ

1

Попробуйте это. Удалить эту строку кода newIdentity.AddClaim(new Claim("newClaim", "newValue")); с вашего GrantRefreshToken функции SimpleAuthorizationServerProvider класс. Поскольку эта линия бесполезна. Он дублирует заявку, когда вы запрашиваете новый токен обновления. Так что это противостоит вам.

+0

Это не сработало. –

3

У меня была проблема, когда я всегда получал ошибку invalid_grant, хотя знал, что refresh_token действителен. Конечно, есть много причин, по которым может быть ошибка invalid_grant, но после отладки кода я обнаружил, что моя проблема связана с методом CreateAsync. Переменная refreshTokenLifetime была равна нулю. Таким образом, когда создается RefreshToken, значение ExpiresUtc уже истекло, вызывая ошибку invalid_grant. Чтобы решить эту проблему, я подтвердил, что у меня есть допустимое значение для переменной refreshTokenLifetime.

var refreshTokenLifetime = context.OwinContext.Get<string>("as:RefreshTokenLifetime") ?? "60"; 
+0

Я думаю, что может быть любое количество проблем, которые приводят к беспричинно неопределенной ошибке «invalid_grant», но у меня была эта проблема, и оказалось, что ваш ответ здесь был прав на деньги! В моем случае у меня была разница между установкой «..Lifetime» в словаре и извлечением «LifeTime» !! Поскольку я следил за учебником, я думал: «* Не используйте константы для этих ключей, спрашивая о проблемах *« ... и вот и вот! :) Моя реализация теперь работает отлично! Спасибо !! – Deltics

Смежные вопросы