2015-11-29 17 views
5

Theo dõi bài viết tuyệt vời của Taiseer Joudeh
Bật thẻ làm mới OAuth trong ứng dụng AngularJS sử dụng ASP .NET Web API 2 và Owin (http://bitoftech.net/2014/07/16/enable-oauth-refresh-tokens-angularjs-app-using-asp-net-web-api-2-owin/) hiện tại tôi đang tạo xác thực dựa trên mã thông báo.Mã thông báo làm mới OAuth 2 invalid__rant

mã lớp Startup của tôi là như sau:

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

     } 
    } 

mã lớp SimpleAuthorizationServerProvider của tôi là như sau:

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

mã lớp SimpleRefreshTokenProvider của tôi là như sau:

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

Các Mã lớp AuthRepository như sau:

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

     } 
    } 

Và mã ajax là:

$("#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); 
         } 

        }); 
       } 

      }); 

Cuối cùng khi tôi nhấp vào Refresh nó trả tôi sau lỗi lỗi: "invalid_grant"

cuối hai ngày tôi đã cố gắng để tìm ra nhưng không thành công.

+0

Tôi bị kẹt với cùng một vấn đề. Bạn đã nhận được bất kỳ giải pháp cho điều này? –

Trả lời

1

Hãy thử điều này. Xóa dòng mã này newIdentity.AddClaim(new Claim("newClaim", "newValue")); từ hàm GrantRefreshToken của lớp SimpleAuthorizationServerProvider. Vì dòng này không được sử dụng. Đang sao chép xác nhận quyền sở hữu khi bạn yêu cầu mã thông báo làm mới mới. Vì vậy, nó là đối lập với bạn.

+0

Điều này không hoạt động. –

3

Tôi gặp sự cố khi tôi luôn nhận được lỗi invalid_grant mặc dù tôi biết tệp refresh_token hợp lệ. Cấp có nhiều lý do tại sao có thể có lỗi invalid_grant, nhưng sau khi gỡ lỗi thông qua mã, tôi đã phát hiện ra rằng sự cố của tôi nằm trong phương thức CreateAsync. Biến refreshTokenLifetime là null. Do đó, khi hàm RefreshToken được tạo, giá trị ExpiresUtc đã hết hạn, gây ra lỗi invalid_grant. Để giải quyết vấn đề này, tôi đã xác minh rằng tôi đã có giá trị hợp lệ cho biến refreshTokenLifetime.

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

Tôi nghĩ rằng có thể có bất kỳ số lượng vấn đề nào dẫn đến lỗi bắt lỗi "invalid_grant" mơ hồ, nhưng tôi đã gặp phải vấn đề này và hóa ra câu trả lời của bạn ở đây đúng về tiền !! Trong trường hợp của tôi, tôi đã có một sự khác biệt trường hợp giữa các thiết lập của '..Lifetime' trong từ điển và lấy' LifeTime' !! Như tôi đã làm theo hướng dẫn tôi đã suy nghĩ "* không sử dụng hằng số cho các phím này là yêu cầu cho sự cố *" ... và lo và nhìn !! :) Việc triển khai của tôi hiện đang hoạt động hoàn hảo !! Cảm ơn bạn !! – Deltics

Các vấn đề liên quan