Openiddict multiple refresh tokens
如何在Asp.net核心中为openiddict创建自定义提供程序,以允许多个刷新令牌?这样,如果用户从自己的计算机登录然后回家然后在手机上登录,则不必在每次登录其他设备时都进行登录。 app.UseOAuthValidation()在调用授权控制器之前在后台运行,因此没有句柄可以验证是否有多个刷新令牌匹配。另一个问题是我正在使用此:
1 2 3 4 | services.AddDbContext<ApplicationDbContext>(options => { options.UseMySql(Configuration.GetConnectionString("DefaultConnection")) .UseOpenIddict(); }); |
因此,我无法通过DbContext访问openiddict表来手动执行此操作。
Startup.cs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 | using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Microsoft.EntityFrameworkCore; using DPInventoryPOAPI.Models; using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Identity.EntityFrameworkCore; using OpenIddict.Core; using OpenIddict.Models; using System.Threading; using System.Linq; namespace DPInventoryPOAPI { public class Startup { public Startup(IHostingEnvironment env) { var builder = new ConfigurationBuilder() .SetBasePath(env.ContentRootPath) .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true) .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true) .AddEnvironmentVariables(); Configuration = builder.Build(); } public IConfigurationRoot Configuration { get; } // This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) { services.AddCors(options => { options.AddPolicy("CorsPolicy", builder => builder.AllowAnyOrigin() .AllowAnyMethod() .AllowAnyHeader() .AllowCredentials() ); }); services.AddMvc(); services.AddDbContext<ApplicationDbContext>(options => { options.UseMySql(Configuration.GetConnectionString("DefaultConnection")) .UseOpenIddict(); }); services.AddIdentity<ApplicationUser, IdentityRole>() .AddEntityFrameworkStores<ApplicationDbContext>() .AddDefaultTokenProviders(); services.AddOpenIddict() .AddEntityFrameworkCoreStores<ApplicationDbContext>() .AddMvcBinders() .EnableTokenEndpoint("/token") .AllowPasswordFlow() .AllowRefreshTokenFlow() .DisableHttpsRequirement() .AddEphemeralSigningKey(); } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. public void Configure(IApplicationBuilder app, IHostingEnvironment env, IApplicationLifetime applicationLifetime, ILoggerFactory loggerFactory) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); app.UseDatabaseErrorPage(); //app.UseBrowserLink(); } else { app.UseExceptionHandler("/Home/Error"); } app.UseCors("CorsPolicy"); app.UseIdentity(); app.UseOpenIddict(); app.UseOAuthValidation(); app.UseMvcWithDefaultRoute(); //SeedDatabase(app); } } } |
并授权控制器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 | using System.Diagnostics; using System.Linq; using System.Threading.Tasks; using AspNet.Security.OpenIdConnect.Extensions; using AspNet.Security.OpenIdConnect.Primitives; using AspNet.Security.OpenIdConnect.Server; using AuthorizationServer.Models; using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Http.Authentication; using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Mvc; using OpenIddict.Core; using OpenIddict.Models; // For more information on enabling MVC for empty projects, visit http://go.microsoft.com/fwlink/?LinkID=397860 namespace AuthorizationServer.Controllers { public class AuthorizationController : Controller { private readonly OpenIddictApplicationManager<OpenIddictApplication> _applicationManager; private readonly SignInManager<ApplicationUser> _signInManager; private readonly UserManager<ApplicationUser> _userManager; public AuthorizationController( OpenIddictApplicationManager<OpenIddictApplication> applicationManager, SignInManager<ApplicationUser> signInManager, UserManager<ApplicationUser> userManager) { _applicationManager = applicationManager; _signInManager = signInManager; _userManager = userManager; } [HttpPost("~/connect/token"), Produces("application/json")] public async Task<IActionResult> Exchange(OpenIdConnectRequest request) { Debug.Assert(request.IsTokenRequest(), "The OpenIddict binder for ASP.NET Core MVC is not registered." + "Make sure services.AddOpenIddict().AddMvcBinders() is correctly called."); if (request.IsPasswordGrantType()) { var user = await _userManager.FindByNameAsync(request.Username); if (user == null) { return BadRequest(new OpenIdConnectResponse { Error = OpenIdConnectConstants.Errors.InvalidGrant, ErrorDescription ="The username/password couple is invalid." }); } // Ensure the user is allowed to sign in. if (!await _signInManager.CanSignInAsync(user)) { return BadRequest(new OpenIdConnectResponse { Error = OpenIdConnectConstants.Errors.InvalidGrant, ErrorDescription ="The specified user is not allowed to sign in." }); } // Reject the token request if two-factor authentication has been enabled by the user. if (_userManager.SupportsUserTwoFactor && await _userManager.GetTwoFactorEnabledAsync(user)) { return BadRequest(new OpenIdConnectResponse { Error = OpenIdConnectConstants.Errors.InvalidGrant, ErrorDescription ="The specified user is not allowed to sign in." }); } // Ensure the user is not already locked out. if (_userManager.SupportsUserLockout && await _userManager.IsLockedOutAsync(user)) { return BadRequest(new OpenIdConnectResponse { Error = OpenIdConnectConstants.Errors.InvalidGrant, ErrorDescription ="The username/password couple is invalid." }); } // Ensure the password is valid. if (!await _userManager.CheckPasswordAsync(user, request.Password)) { if (_userManager.SupportsUserLockout) { await _userManager.AccessFailedAsync(user); } return BadRequest(new OpenIdConnectResponse { Error = OpenIdConnectConstants.Errors.InvalidGrant, ErrorDescription ="The username/password couple is invalid." }); } if (_userManager.SupportsUserLockout) { await _userManager.ResetAccessFailedCountAsync(user); } // Create a new authentication ticket. var ticket = await CreateTicketAsync(request, user); return SignIn(ticket.Principal, ticket.Properties, ticket.AuthenticationScheme); } else if (request.IsRefreshTokenGrantType()) { // Retrieve the claims principal stored in the refresh token. var info = await HttpContext.Authentication.GetAuthenticateInfoAsync( OpenIdConnectServerDefaults.AuthenticationScheme); // Retrieve the user profile corresponding to the refresh token. var user = await _userManager.GetUserAsync(info.Principal); if (user == null) { return BadRequest(new OpenIdConnectResponse { Error = OpenIdConnectConstants.Errors.InvalidGrant, ErrorDescription ="The refresh token is no longer valid." }); } // Ensure the user is still allowed to sign in. if (!await _signInManager.CanSignInAsync(user)) { return BadRequest(new OpenIdConnectResponse { Error = OpenIdConnectConstants.Errors.InvalidGrant, ErrorDescription ="The user is no longer allowed to sign in." }); } // Create a new authentication ticket, but reuse the properties stored // in the refresh token, including the scopes originally granted. var ticket = await CreateTicketAsync(request, user, info.Properties); return SignIn(ticket.Principal, ticket.Properties, ticket.AuthenticationScheme); } return BadRequest(new OpenIdConnectResponse { Error = OpenIdConnectConstants.Errors.UnsupportedGrantType, ErrorDescription ="The specified grant type is not supported." }); } private async Task<AuthenticationTicket> CreateTicketAsync( OpenIdConnectRequest request, ApplicationUser user, AuthenticationProperties properties = null) { // Create a new ClaimsPrincipal containing the claims that // will be used to create an id_token, a token or a code. var principal = await _signInManager.CreateUserPrincipalAsync(user); // Note: by default, claims are NOT automatically included in the access and identity tokens. // To allow OpenIddict to serialize them, you must attach them a destination, that specifies // whether they should be included in access tokens, in identity tokens or in both. foreach (var claim in principal.Claims) { // In this sample, every claim is serialized in both the access and the identity tokens. // In a real world application, you'd probably want to exclude confidential claims // or apply a claims policy based on the scopes requested by the client application. claim.SetDestinations(OpenIdConnectConstants.Destinations.AccessToken, OpenIdConnectConstants.Destinations.IdentityToken); } // Create a new authentication ticket holding the user identity. var ticket = new AuthenticationTicket(principal, properties, OpenIdConnectServerDefaults.AuthenticationScheme); if (!request.IsRefreshTokenGrantType()) { // Set the list of scopes granted to the client application. // Note: the offline_access scope must be granted // to allow OpenIddict to return a refresh token. ticket.SetScopes(new[] { OpenIdConnectConstants.Scopes.OpenId, OpenIdConnectConstants.Scopes.Email, OpenIdConnectConstants.Scopes.Profile, OpenIdConnectConstants.Scopes.OfflineAccess, OpenIddictConstants.Scopes.Roles }.Intersect(request.GetScopes())); } return ticket; } } } |
How do you create custom provider for openiddict in Asp.net core to allow multiple refresh tokens? This way if the user logs in from their computer and then goes home and logs in on their phone, they don't have to login each time they get on to a different device.
OTB,OpenIddict允许您检索多个(独立的)刷新令牌,只要它们是使用不同的
The app.UseOAuthValidation() runs in the background before the authorize controller ever gets called so there is no handle to verify if more than 1 refresh token matches.
验证中间件从不处理刷新令牌,因为它仅负责验证访问令牌。
So I do not have access to the openiddict tables via DbContext to do this manually.
您可以在