2016-12-23 76 views
0

如何在Asp.net核心中爲openiddict創建自定義提供程序以允許多個刷新標記?這樣,如果用戶從他們的計算機登錄然後回家登錄到他們的電話上,他們每次登錄不同的設備時都不必登錄。 app.UseOAuthValidation()在授權控制器被調用之前在後臺運行,所以沒有句柄來驗證是否有超過1個刷新令牌匹配。另一個問題是,我正在使用此:Openiddict多刷新標記

services.AddDbContext<ApplicationDbContext>(options => { 
      options.UseMySql(Configuration.GetConnectionString("DefaultConnection")) 
        .UseOpenIddict(); 
     }); 

所以我沒有通過DbContext訪問openiddict表來手動執行此操作。

Startup.cs

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

,並授權控制器

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

回答

1

如何創建在Asp.net核心openiddict允許多個刷新令牌定製的供應商?這樣,如果用戶從他們的計算機登錄然後回家登錄到他們的電話上,他們每次登錄不同的設備時都不必登錄。

OTB,OpenIddict允許您檢索多個(獨立的)刷新令牌,只要請求使用不同的grant_type=password請求。在您的情況下,如果移動應用程序檢索到的令牌被吊銷(例如手動或因爲它已被使用),則桌面應用程序使用的刷新令牌仍可用於檢索新的訪問/刷新令牌。

app.UseOAuthValidation()在授權控制器被調用之前在後臺運行,因此沒有句柄來驗證是否有多個刷新令牌匹配。

驗證中間件從不處理刷新令牌,因爲它只負責驗證訪問令牌。

因此,我無法通過DbContext訪問openiddict表來手動執行此操作。

您可以在DbContext添加DbSet<OpenIddictToken>財產或檢索通過context.Set<OpenIddictToken>()DbSet<OpenIddictToken>