用户密码更改的OAuth访问和刷新令牌控制/管理

时间:2015-04-09 09:00:41

标签: oauth-2.0 authorization asp.net-web-api2 access-token asp.net-identity-2

我们正在开发内部移动应用程序和web api。 我们正在使用asp.net web api 2和asp.net Identy 2 OAuth。

我已经启动并运行了api并给了我一个持票人令牌。但是,我想稍微修改一下这样的流程:

  1. App用户使用用户名和密码登录api。
  2. 应用程序收到刷新令牌,该令牌有效期为30天。
  3. App然后请求一个访问令牌,为api提供刷新令牌。 (如果用户更改了密码或帐户已被锁定,我希望能够使请求无效。)
  4. App获取一个有效30分钟的访问令牌,如果密码检查失败则获取401.
  5. App可以在接下来的29分钟内使用给定的访问令牌访问api。之后,应用程序必须使用刷新令牌获取新的访问令牌。
  6. 我想这样做的原因是为了阻止用户设备在更改密码后获得对api的访问权限。如果他们的手机被盗,他们需要能够登录网站并更改密码,以便手机的新主人无法访问我们公司的服务。

    我建议的解决方案能否做到,如果是,这是一个明智的解决方案吗?我还没有忘记任何关键因素吗?

    我愿意在令牌刷新时进行数据库访问,但不是每次API调用都会进行。

    总结一下我的问题是:

    1. 我的计划方法合情合理吗?
    2. 如何安全地检查密码是否已更改或帐户是否在刷新令牌过程中被锁定。
    3. 请在下面找到我当前的OAuth设置和类:(我尝试添加刷新令牌功能,但尚未尝试添加任何密码验证)

      Startup.Auth.cs

      public partial class Startup
      {
          public static OAuthAuthorizationServerOptions OAuthOptions { get; private set; }
      
          public static string PublicClientId { get; private set; }
      
          // For more information on configuring authentication, please visit http://go.microsoft.com/fwlink/?LinkId=301864
          public void ConfigureAuth(IAppBuilder app)
          {
              // Configure the db context and user manager to use a single instance per request
              app.CreatePerOwinContext(IdentityDbContext.Create);
              app.CreatePerOwinContext<FskUserManager>(FskUserManager.Create);
      
              // Enable the application to use a cookie to store information for the signed in user
              // and to use a cookie to temporarily store information about a user logging in with a third party login provider
              app.UseCookieAuthentication(new CookieAuthenticationOptions());
              app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
      
              // Configure the application for OAuth based flow
              PublicClientId = "self";
              OAuthOptions = new OAuthAuthorizationServerOptions
              {
                  TokenEndpointPath = new PathString("/Token"),
                  Provider = new ApplicationOAuthProvider(PublicClientId),
                  AuthorizeEndpointPath = new PathString("/api/Account/ExternalLogin"),
                  AccessTokenExpireTimeSpan = TimeSpan.FromDays(14),
                  RefreshTokenProvider = new ApplicationRefreshTokenProvider(),
                  AllowInsecureHttp = true
              };
      
              // Enable the application to use bearer tokens to authenticate users
              app.UseOAuthBearerTokens(OAuthOptions);
      }
      

      ApplicationRefreshTokenProvider.cs

      public class ApplicationRefreshTokenProvider : AuthenticationTokenProvider
              {
                  public override void Create(AuthenticationTokenCreateContext context)
                  {
                      // Expiration time in minutes
                      int refreshTokenExpiration = Convert.ToInt32(System.Configuration.ConfigurationManager.AppSettings["ApiRefreshTokenExpiry"]);
                      context.Ticket.Properties.ExpiresUtc = new DateTimeOffset(DateTime.Now.AddMinutes(refreshTokenExpiration));
                      context.SetToken(context.SerializeTicket());
                  }
      
                  public override void Receive(AuthenticationTokenReceiveContext context)
                  {
                      context.DeserializeTicket(context.Token);
                  }
              }   
      

      ApplicationOAuthProvider.cs

      public class ApplicationOAuthProvider : OAuthAuthorizationServerProvider
      {
          private readonly string _publicClientId;
      
          public ApplicationOAuthProvider(string publicClientId)
          {
              if (publicClientId == null)
              {
                  throw new ArgumentNullException("publicClientId");
              }
      
              _publicClientId = publicClientId;
          }
      
          public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
          {
              var userManager = context.OwinContext.GetUserManager<FskUserManager>();
      
              FskUser user = await userManager.FindAsync(context.UserName, context.Password);
      
              if (user == null)
              {
                  context.SetError("invalid_grant", "The user name or password is incorrect.");
                  return;
              }
      
              ClaimsIdentity oAuthIdentity = await user.GenerateUserIdentityAsync(userManager,
                 OAuthDefaults.AuthenticationType);
              ClaimsIdentity cookiesIdentity = await user.GenerateUserIdentityAsync(userManager,
                  CookieAuthenticationDefaults.AuthenticationType);
      
              AuthenticationProperties properties = CreateProperties(user.UserName);
              AuthenticationTicket ticket = new AuthenticationTicket(oAuthIdentity, properties);
              context.Validated(ticket);
              context.Request.Context.Authentication.SignIn(cookiesIdentity);
          }
      
          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);
          }
      
          public override Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
          {
              // Resource owner password credentials does not provide a client ID.
              if (context.ClientId == null)
              {
                  context.Validated();
              }
      
              return Task.FromResult<object>(null);
          }
      
          public override Task ValidateClientRedirectUri(OAuthValidateClientRedirectUriContext context)
          {
              if (context.ClientId == _publicClientId)
              {
                  Uri expectedRootUri = new Uri(context.Request.Uri, "/");
      
                  if (expectedRootUri.AbsoluteUri == context.RedirectUri)
                  {
                      context.Validated();
                  }
              }
      
              return Task.FromResult<object>(null);
          }
      
          public static AuthenticationProperties CreateProperties(string userName)
          {
              IDictionary<string, string> data = new Dictionary<string, string>
              {
                  { "userName", userName }
              };
              return new AuthenticationProperties(data);
          }
      }
      

1 个答案:

答案 0 :(得分:0)

如果我理解你的任务是正确的,这是一个想法。

在创建访问令牌事件上,您可以检查密码是否已从网站更改,如果是,则撤消刷新令牌。 (您可以创建一些密码已更改的标志或其他内容)

创建访问令牌时不应该经常这样,因此db访问应该没有问题。

现在的问题是如何撤销刷新令牌。除非有构建方式,否则您必须实现自定义构建。这里的想法是检查刷新令牌创建日期和更改密码操作的日期。如果在创建刷新令牌后完成更改密码操作,则不会对用户进行身份验证。

让我知道你对此的看法。