2016-03-20 6 views
2

は私が記事のようにほぼ正確にそれを実装しました記事OAuthAuthorizationServerProviderはどこでRefresh Token Expiryをチェックしますか?

http://bitoftech.net/2014/07/16/enable-oauth-refresh-tokens-angularjs-app-using-asp-net-web-api-2-owin/

次認証サーバを実装しているが、私は、認証サーバは、リフレッシュトークンの有効期限が切れている知っている方法を見てはいけません。確かに私はテストしたが、サーバーは期限切れのリフレッシュトークンが表示されたときにアクセストークンを許可しませんが、私の認証サーバーではこのロジックが表示されません。さらに、期限切れリフレッシュトークンを使用してアクセストークンを要求すると、私のOAuthAuthorizationServerProviderサブクラスは決して呼び出されません。期限切れの新しいアクセストークンを要求すると、実際にOAuthAuthorizationServerProvider派生クラスまたはIAuthenticationTokenProvider実装のメソッドは呼び出されませんリフレッシュトークン。どんな助けもありがとうございます。私は要求

request with expired refresh とリフレッシュトークンの有効期限が切れている、\方法が打撃を受ける上記のクラスのNONEを作る際にここで、私は再び

public class SmartCardOAuthAuthenticationTokenProvider : IAuthenticationTokenProvider 
{ 
    private IDataAccessFactoryFactory _producesFactoryThatProducesIAuthenticateDataAccess; 
    public SmartCardOAuthAuthenticationTokenProvider(IDataAccessFactoryFactory producesFactoryThatProducesIAuthenticateDataAccess) 
    { 
     _producesFactoryThatProducesIAuthenticateDataAccess = producesFactoryThatProducesIAuthenticateDataAccess; 

    } 
    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(IDataAccessFactory producesIAuthenticateDataAccess = _producesFactoryThatProducesIAuthenticateDataAccess.GetDataAccessFactory())//using (IAuthorizationDataAccess _repo = new AuthRepository()) 
     { 
      IAuthorizationDataAccess _repo = producesIAuthenticateDataAccess.GetDataAccess<IAuthorizationDataAccess>(); 

      var refreshTokenLifeTime = context.OwinContext.Get<string>("as:clientRefreshTokenLifeTime"); 

      var token = new RefreshToken() 
      { 
       RefreshTokenId = 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.AddRefreshTokenAsync(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 (IAuthorizationDataAccess _repo = new AuthRepository()) 
     //{ 
     using (IDataAccessFactory producesIAuthenticateDataAccess = _producesFactoryThatProducesIAuthenticateDataAccess.GetDataAccessFactory()) //using (AuthRepository _repo = new AuthRepository()) 
     { 
      IAuthorizationDataAccess _repo = producesIAuthenticateDataAccess.GetDataAccess<IAuthorizationDataAccess>(); 

      var refreshToken = await _repo.FindRefreshTokenAsync(hashedTokenId); 

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

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

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

public class SmartCardOAuthAuthorizationProvider : OAuthAuthorizationServerProvider 
{ 
    private IDataAccessFactoryFactory _producesFactoryThatProducesIAuthenticateDataAccess; 
    public SmartCardOAuthAuthorizationProvider(IDataAccessFactoryFactory producesFactoryThatProducesIAuthenticateDataAccess) 
    { 
     _producesFactoryThatProducesIAuthenticateDataAccess = producesFactoryThatProducesIAuthenticateDataAccess; 

    } 
    public override System.Threading.Tasks.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 }); 

     if (context.UserName != "onlyOneHardCodedUserForSakeOfExploration" && context.Password!="thePassword") 
     { 
      context.SetError("invalid_grant", "the user name or password is incorrect"); 
      return Task.FromResult<object>(null); ; 
     } 
     ClaimsIdentity identity = new ClaimsIdentity(context.Options.AuthenticationType); 
     identity.AddClaim(new Claim(ClaimTypes.Name, context.UserName)); 
     identity.AddClaim(new Claim("sub", context.UserName)); 
     identity.AddClaim(new Claim(ClaimTypes.Role, "PostVSDebugBreakModeEnterEventArgs")); 
     identity.AddClaim(new Claim(DatawareClaimTypes.SmartCardUserId.ToString(), 1.ToString())); 

     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); 
     //ticket.Properties.ExpiresUtc = new DateTimeOffset(DateTime.Now.AddDays(2)); 
     context.Validated(ticket); 

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

    public override System.Threading.Tasks.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); 
     } 

     string[] clientIdClientUnique = context.ClientId.Split(':'); 

     if (clientIdClientUnique == null || clientIdClientUnique.Length <= 1) 
     { 
      context.SetError("invalid_client_unique"); 
      return Task.FromResult<object>(null); 
     } 

     clientId = clientIdClientUnique[0]; 
     string clientUnique = clientIdClientUnique[1]; 

     using (IDataAccessFactory producesIAuthenticateDataAccess = _producesFactoryThatProducesIAuthenticateDataAccess.GetDataAccessFactory()) //using (AuthRepository _repo = new AuthRepository()) 
     { 
      IAuthorizationDataAccess _repo = producesIAuthenticateDataAccess.GetDataAccess<IAuthorizationDataAccess>(); 


      client = _repo.FindClient(clientId);//new Client { Active = true, AllowedOrigin = "*", ApplicationType = ApplicationTypes.DesktopClient, ClientId = context.ClientId, Name = "Visual Studio Event Source", RefreshTokenLifeTimeInMinutes = 14400, Secret = Helper.GetHash(clientSecret) };//_repo.FindClient(context.ClientId); 
     } 

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

     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.RefreshTokenLifeTimeInMinutes.ToString()); 

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

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

} 

を持っているものです。さらに、APIに渡されたリフレッシュトークンは、SmartCardOAuthAuthenticationTokenProvider.CreateAsyncで生成したGUIDだけであり、有効期限に関する情報は含まれていません。リフレッシュによってアクセスを要求するときに上記の方法のどれもヒットしない場合、リフレッシュによって新しいアクセストークンを要求するときに何も表示されません(何も表示されません)。

私には魔法のようです。

UPDATE 1 -

public static class OwinStartUpConfig 
{ 
    public static void Configure(HttpConfiguration configFromOwinStartup) 
    { 
     configFromOwinStartup.MapHttpAttributeRoutes(); 
     configFromOwinStartup.Routes.MapHttpRoute("Default", "{controller}/{id}", new { id = RouteParameter.Optional }); 

     var jsonFormatter = configFromOwinStartup.Formatters.OfType<JsonMediaTypeFormatter>().First(); 
     jsonFormatter.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver(); 

     RegiserDependencies(configFromOwinStartup); 

    } 

    public static void RegiserDependencies(HttpConfiguration configFromOwinStartup) 
    { 
     string connectionStringForSmartCardDbCntx = System.Configuration.ConfigurationManager.ConnectionStrings["SmartCardDataContext"].ConnectionString; 
     string projectNameWhenNewProjectCreatedDueToNoMatch = System.Configuration.ConfigurationManager.AppSettings["ProjectNameWhenNewProjectCreatedDueToNoMatch"]; 

     Autofac.ContainerBuilder builderUsedToRegisterDependencies = new Autofac.ContainerBuilder(); 

     builderUsedToRegisterDependencies.RegisterType<DataAccessFactoryFactoryEf>() 
      .As<IDataAccessFactoryFactory>() 
      .WithParameter(new TypedParameter(typeof(string), connectionStringForSmartCardDbCntx)); 

     builderUsedToRegisterDependencies.Register(
      c => 
        new List<IProjectActivityMatch<VSDebugBreakModeEnterActivity>> 
      { 
       new MatchVSProjectWithMostRecentActivity<VSDebugBreakModeEnterActivity>(), 
       new MatchVSSolutionWithMostRecentActivityActivityMatch<VSDebugBreakModeEnterActivity>(), 
       new MatchMostRecentActivityMatch<VSDebugBreakModeEnterActivity>(), 
       new MatchToNewProjectActivityMatch<VSDebugBreakModeEnterActivity>(projectNameWhenNewProjectCreatedDueToNoMatch) 
      } 
     ).As<IEnumerable<IProjectActivityMatch<VSDebugBreakModeEnterActivity>>>(); 

     builderUsedToRegisterDependencies 
      .RegisterType<MatchDontGiveUpActivityMatch<VSDebugBreakModeEnterActivity>>() 
      //.WithParameter(Autofac.Core.ResolvedParameter.ForNamed<IEnumerable<IProjectActivityMatch<VSDebugBreakModeEnterActivity>>>("VSDebugBreakModeEnterActivityMatchers")) 
      .As<IProjectActivityMatch<VSDebugBreakModeEnterActivity>>(); 

     builderUsedToRegisterDependencies 
      .RegisterType<VSDebugBreakModeEnterEventArgsEventSaver>() 
      .Named<ISaveVisualStudioEvents>("VSDebugBreakModeEnterSaver"); 

     builderUsedToRegisterDependencies 
      .RegisterType<VSDebugBreakModeEnterEventArgsController>() 
      .WithParameter(Autofac.Core.ResolvedParameter.ForNamed<ISaveVisualStudioEvents>("VSDebugBreakModeEnterSaver")); 

     var container = builderUsedToRegisterDependencies.Build(); 

     configFromOwinStartup.DependencyResolver = new AutofacWebApiDependencyResolver(container); 

    } 
} 
    public static class OAuthStartupConfig 
{ 
    internal static void Configure(IAppBuilder app) 
    { 
     OAuthAuthorizationServerOptions oAuthServerOptions = new OAuthAuthorizationServerOptions 
     { 
      AllowInsecureHttp = false, 
      TokenEndpointPath = new PathString("/token"), 
      AccessTokenExpireTimeSpan = TimeSpan.FromDays(1), //TimeSpan.FromDays(1), 
      Provider = new SmartCardOAuthAuthorizationProvider(new AuthorizationDataAccessFactoryFactory()), 
      RefreshTokenProvider = new SmartCardOAuthAuthenticationTokenProvider(new AuthorizationDataAccessFactoryFactory()) 
     }; 
     app.UseOAuthAuthorizationServer(oAuthServerOptions); 
     app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions()); 
    } 
} 

UPDATEスタートアップコードを追加 - コメント

request for access token by pass returns access and refresh token

に応答して上記応答内のアクセストークンは、アクセストークンのクレーム情報及び有効期限の情報が含まれてい。これらの情報はすべて、サーバーによって上記のアクセストークンにシリアル化されます。そこで、アクセストークンの有効期限がどのようにチェックされているかを確認できます。しかし、リフレッシュトークンはどうですか?リフレッシュトークンは良いですので、リフレッシュトークンによるアクセストークンの要求が許可されていますが、リフレッシュ場合は、上記の要求に

enter image description here

:アクセストークンの要求は、リフレッシュトークンを使用して作るされている場合トークンが期限切れになったOAuthAuthorizationServerProviderがリフレッシュトークンの有効期限を確認する方法

繰り返しますが、私は上記のコードが実際に期限切れのリフレッシュトークンをチェックしないとリフレッシュトークンの有効期限が切れている場合、アクセストークンを許可していないことを確認しているが、それはどのように知っているのですか?私はOAuthAuthorizationServerProvider派生クラスにリフレッシュトークンの有効期限をチェックするために何も書きませんでした。そう?

UPDATE

魔法はIAuthenticationTokenProvider実装のReceiveAsync方法で行われます。

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 (IAuthorizationDataAccess _repo = new AuthRepository()) 
     //{ 
     using (IDataAccessFactory producesIAuthenticateDataAccess = _producesFactoryThatProducesIAuthenticateDataAccess.GetDataAccessFactory()) //using (AuthRepository _repo = new AuthRepository()) 
     { 
      IAuthorizationDataAccess _repo = producesIAuthenticateDataAccess.GetDataAccess<IAuthorizationDataAccess>(); 

      var refreshToken = await _repo.FindRefreshTokenAsync(hashedTokenId); 

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

特に、context.DeserializeTicket(refreshToken.ProtectedTicket);という行が魔法です。それはcontext.Ticketプロパティを設定します。 ReceiveAsyncメソッドが完了したら、完了しました。手動で何かを確認する必要はありません.OWINは、チケットの有効期限が切れていることを舞台裏で知ることができます。

+0

プロバイダをOAuthServerOptionsに接続するコードを表示できますか?サーバーオプションを作成するときにリフレッシュトークンプロバイダを指していないと思われます。 –

答えて

1

私は記事とほとんど同じように実装しましたが、認証サーバーがリフレッシュトークンが期限切れになっていることがわかりません。

この情報は、たとえばSQLテーブルなどのどこかに保存する必要があります。リフレッシュトークンを使用して新しいトークンを発行する要求を受け取ると、ローカルの真理値源に関する情報に基づいて、送信されたリフレッシュトークンが有効かどうかを確認する必要があります。

例えば、これは私がそれを実現している。

public override async Task ReceiveAsync(AuthenticationTokenReceiveContext context) 
    { 
     var allowedOrigin = context.OwinContext.Get<string>(Constants.PublicAuth.CLIENT_ALLOWED_ORIGIN); 
     context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] {allowedOrigin}); 

     RefreshTokenModel refreshToken = await _mediator.SendAsync(new VerifyRefreshToken(context.Token)); 

     if (refreshToken != null) 
     { 
      context.DeserializeTicket(refreshToken.ProtectedTicket); 
     } 
    } 

そして、これは、クエリハンドラです:存在しない場合は、見ることができるように

public async Task<RefreshTokenModel> Handle(VerifyRefreshToken query) 
     { 
      RefreshToken refreshToken = await Context.RefreshTokens 
       .Where(rt => rt.Token == query.Token) 
       .FirstOrDefaultAsync(); 

      if (refreshToken == null || refreshToken.ExpiresUtc < DateTimeOffset.UtcNow) return null; 

      User user = await Context.Users.Where(u => u.Email == refreshToken.Subject).SingleOrDefaultAsync(); 
      if (user == null || (user.LockoutEnabled && user.LockoutEndUtc > DateTimeOffset.UtcNow)) return null; 

      return new RefreshTokenModel 
      { 
       ProtectedTicket = new UTF8Encoding(true).GetString(refreshToken.ProtectedTicket) 
      }; 
     } 

、またはトークン有効期限が過ぎた、私はnullを返します。 nullはトークンを意味しないため、ReceiveAsync(AuthenticationTokenReceiveContext context)は何も返しません。401 Unauthorizedが送出されます。

+0

あなたの答えをありがとう。しかし、私はあなたが私のReceiveAsyncメソッドで提案したロジックを実装しておらず、トークンの有効期限はまだ動作しています。私が投稿したコードは期限切れのトークンを検出しますが、私はそのようなコードを実装していないので、どのように把握できません。これは私が理解していないものです。 – andrewramka

+0

あなたが「トークン」と言うときは、ベアラを意味しますか?そのトークンには内部に有効期限が設定されているため(コードAccessTokenExpireTimeSpan = TimeSpan.FromDays(1)の略語)の略です。もう1つは「個人的な」コード(ここではGUID)なので、期限切れにならず、キーのようです。 –

+0

アクセストークンには有効期限がありますが、更新トークンはどうですか?新しいスクリーンショットを追加し、両方のトークンを認証サーバーから送り返して表示します。アクセストークンがあります(はい、この文字列にはクレーム、有効期限などの情報が含まれています)。リフレッシュトークンもあります(この文字列は、データベース内のリフレッシュトークンレコードを指すGUIDです)。トークンに正しい情報が含まれているため、サーバはアクセストークンを簡単に検証できますが、リフレッシュトークンを使用して新しいアクセストークンが要求された場合、サーバはリフレッシュトークンをどのように検証しますか?これが問題です。 – andrewramka

0

この魔法は、IAuthenticationTokenProvider実装のReceiveAsyncメソッドで発生します。

特に、コンテキストcontext.DeserializeTicket(refreshToken.ProtectedTicket);私が行方不明だった魔法です。それはcontext.Ticketプロパティを設定します。 ReceiveAsyncメソッドが完了したら、完了しました。手動で何かを確認する必要はありません.OWINは、チケットの有効期限が切れていることを舞台裏で知ることができます。

関連する問題