2017-09-15 4 views
1

J'ai été bloqué pendant quelques jours en essayant de configurer l'exemple PasswordFlow/Refresh et j'ai besoin d'aide pour savoir comment dépanner mon problème.openiddict mot de passe/actualiser le flux Microsoft.AspNetCore.Authorization.DefaultAuthorizationService [2] L'autorisation a échoué pour l'utilisateur: (null)

 info: Microsoft.AspNetCore.Authorization.DefaultAuthorizationService[2] 
     Authorization failed for user: (null). 

J'ai essayé la suggestion faite par Kevin Chalet le 19 Juillet à utiliser OAuthValidationDefaults.AuthenticationScheme

public class Startup 
{ 
    public IConfiguration Configuration { get; set; } 
    private string _environmentName; 
    private string _userAuthConnectionString; 


    public Startup(IHostingEnvironment env) 
    { 
     Debug.WriteLine($"EnvironmentName:{env.EnvironmentName}"); 
     _environmentName = env.EnvironmentName; 

     var builder = new ConfigurationBuilder() 
      .SetBasePath(Directory.GetCurrentDirectory()) 
      .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true) 
      .AddJsonFile($"appsettings.{_environmentName.ToLower()}.json", optional: true); 
     Configuration = builder.Build(); 
    } 

    // This method gets called by the runtime. Use this method to add services to the container. 
    // For more information on how to configure your application, visit http://go.microsoft.com/fwlink/?LinkID=398940 
    public void ConfigureServices(IServiceCollection services) 
    { 
     //services.AddCors(); 
     services.AddMvc();   // Add framework services. 
     ConfigureEntityFramework(services); 
     ConfigureSettings(services); 
     services.AddOptions(); 
     ConfigureCustomServices(services); 
     services.AddAuthorization(auth => ConfigureAuthorization(auth)); 
    } 

    // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. 
    public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) 
    { 

     loggerFactory.AddConsole(Configuration.GetSection("Logging")); 
     loggerFactory.AddDebug(); 

     // Reference: http://benjii.me/2016/01/angular2-routing-with-asp-net-core-1/ 
     // Route all unknown requests to app root 
     app.Use(async (context, next) => 
     { 
      await next(); 

      // If there's no available file and the request doesn't contain an extension, we're probably trying to access a page. 
      // Rewrite request to use app root 
      if (context.Response.StatusCode == 404 && !Path.HasExtension(context.Request.Path.Value)) 
      { 
       context.Request.Path = "/index.html"; // Put your Angular root page here 
       await next(); 
      } 
     }); 

     app.UseAuthentication(); 
     // Reference: http://www.mithunvp.com/angular-2-in-asp-net-5-typescript-visual-studio-2015/ 
     // For ASP.NET 5 to serve static files, we need to add StaticFiles middle ware in Configure method of StartUp.cs page. 
     app.UseDefaultFiles(); 
     app.UseStaticFiles(); 
     app.UseMvc(); 

    } 



    private void ConfigureSettings(IServiceCollection services) 
    { 
     services.Configure<PortalAuthenticationSettings>(Configuration.GetSection("PortalAuthentication")); 
    } 

    private void ConfigureEntityFramework(IServiceCollection services) 
    { 
     //Configure with our Settings object 
     var portalConnectionString = Configuration.GetSection("Data:DefaultConnection:ConnectionString").Value; 
     _userAuthConnectionString = Configuration.GetSection("Data:UserAuthConnection:ConnectionString").Value; 

     services.AddDbContext<PortalContext>(options => { 
      options.UseSqlServer(portalConnectionString); 
     }); 
     services.AddDbContext<AuthPortalDbContext>(options => { 
      options.UseSqlServer(_userAuthConnectionString); 

      // Register the entity sets needed by OpenIddict. 
      // Note: use the generic overload if you need 
      // to replace the default OpenIddict entities. 
      options.UseOpenIddict(); 
     }); 


     // Register the Identity services. 
     services.AddIdentity<ApplicationUser, IdentityRole>() 
      .AddEntityFrameworkStores<AuthPortalDbContext>() 
      .AddDefaultTokenProviders(); 

     #region may not need 
     //services.ConfigureApplicationCookie(config => 
     //{ 
     // config.Events = new CookieAuthenticationEvents 
     // { 
     //  OnRedirectToLogin = ctx => 
     //  { 
     //   if (ctx.Request.Path.StartsWithSegments("/api")) 
     //   { 
     //    ctx.RedirectUri = null; 
     //    ctx.Response.WriteAsync("{\"error\": " + ctx.Response.StatusCode + "}"); 
     //   } 
     //   else 
     //   { 
     //    ctx.Response.Redirect(ctx.RedirectUri); 
     //   } 
     //   return Task.FromResult(0); 
     //  } 
     // }; 
     //}); 
     #endregion 


     //https://github.com/openiddict/openiddict-samples/blob/dev/samples/PasswordFlow/AuthorizationServer/Startup.cs 
     // Configure Identity to use the same JWT claims as OpenIddict instead 
     // of the legacy WS-Federation claims it uses by default (ClaimTypes), 
     // which saves you from doing the mapping in your authorization controller. 
     services.Configure<IdentityOptions>(options => 
     { 
      options.ClaimsIdentity.UserNameClaimType = OpenIdConnectConstants.Claims.Name; 
      options.ClaimsIdentity.UserIdClaimType = OpenIdConnectConstants.Claims.Subject; 
      options.ClaimsIdentity.RoleClaimType = OpenIdConnectConstants.Claims.Role; 
     }); 

     // Register the OpenIddict services. 
     services.AddOpenIddict(options => { 

      // Register the Entity Framework stores. 
      options.AddEntityFrameworkCoreStores<AuthPortalDbContext>(); 

      // Register the ASP.NET Core MVC binder used by OpenIddict. 
      // Note: if you don't call this method, you won't be able to 
      // bind OpenIdConnectRequest or OpenIdConnectResponse parameters. 
      options.AddMvcBinders(); 

      // Enable the token endpoint. 
      options.EnableTokenEndpoint("/connect/token"); 

      // Enable the password and the refresh token flows. 
      options.AllowPasswordFlow() 
        .AllowRefreshTokenFlow(); 

      // During development, you can disable the HTTPS requirement. 
      options.DisableHttpsRequirement(); 

      // Note: to use JWT access tokens instead of the default 
      // encrypted format, the following lines are required: 
      options.UseJsonWebTokens(); 
      options.AddEphemeralSigningKey(); 
     }); 

     services.AddAuthentication(options => 
     { 
      //options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme; 
      options.DefaultScheme = OAuthValidationDefaults.AuthenticationScheme; 
      //options.DefaultChallengeScheme = OAuthValidation; 
     }); 

     #region not part of refresh token sample, nor password flow sample 
     // this is not part of refresh token sample 
     // use jwt bearer authentication 
     var Events = new JwtBearerEvents 
     { 
      OnAuthenticationFailed = context => { return Task.FromResult(0); }, 
      OnTokenValidated = context => { return Task.FromResult(0); } 
     }; 
     #endregion 

     var portalURL = Configuration.GetSection("PortalURL").Value; 
     // If you prefer using JWT, don't forget to disable the automatic 
     // JWT -> WS-Federation claims mapping used by the JWT middleware: 
     JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear(); 
     JwtSecurityTokenHandler.DefaultOutboundClaimTypeMap.Clear(); 


     services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) 

      .AddJwtBearer(options => 
      { 

       options.Authority = portalURL; 
       options.Audience = portalURL; 
       options.RequireHttpsMetadata = false; 
       options.Events = Events; 
       options.TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters 
       { 
        NameClaimType = OpenIdConnectConstants.Claims.Subject, 
        RoleClaimType = OpenIdConnectConstants.Claims.Role 
       }; 
      }); 


     services.AddScoped(typeof(Microsoft.AspNetCore.Identity.IUserClaimsPrincipalFactory<ApplicationUser>), typeof(Portal.UserClaimsPrincipalFactory<ApplicationUser, IdentityRole>)); 
     //services.AddScoped<UserManager<ApplicationUser>, PortalUserManager>(); // shiro 
     services.AddScoped<IAuthorizationHandler, PermissionHandler>(); 


    } 


    private void ConfigureAuthorization(Microsoft.AspNetCore.Authorization.AuthorizationOptions auth) 
    { 
     // Add Inline Authorization Policies 
     var optionsBuilder = new DbContextOptionsBuilder<AuthPortalDbContext>(); 
     optionsBuilder.UseSqlServer(_userAuthConnectionString); 
     var ctx = new AuthPortalDbContext(optionsBuilder.Options); 
     foreach (var permission in ctx.Permission) 
     { 
      // Try to convert string to PermissionEnum type 
      PermissionEnum permEnum; 
      if (Enum.TryParse(permission.PermissionName, out permEnum)) 
      { 
       auth.AddPolicy(permission.PermissionName, 
       policy => policy.Requirements.Add(new PermissionRequirement(permEnum))); 
      } 


     } 


    } 

    private static void ConfigureCustomServices(IServiceCollection services) 
    { 
     // Inject caching helper 
     //services.AddTransient<IDatabaseInitializer, DatabaseInitializer>(); 
     services.AddSingleton<IResearchRepository, ResearchRepository>(); 
     services.AddSingleton<IResearchService, ResearchService>(); 
     services.AddSingleton<ICompanyRepository, CompanyRepository>(); 
     services.AddSingleton<ICompanyService, CompanyService>(); 
     services.AddSingleton<IUserRepository, UserRepository>(); 
     services.AddSingleton<IUserService, UserService>(); 
     services.AddSingleton<IAuthorizationHandler, EvaluationAuthorizationHandler>(); 
    } 
} 

j'ai pu ouvrir une session avec succès mais quand je suis en train de faire un appel GET/api/entreprise et obtenir cette autorisation a échoué pour l'utilisateur. J'ai mis à niveau vers .NET Core 2.0 et en utilisant Angular 2.0 (avec angular-jwt). Je reçois le jeton d'accès et le jeton d'actualisation. Je suis capable de décoder le jeton sur le navigateur. J'apprécierais grandement l'aide!

J'ai aussi ajouté les UserClaimsPrincipalFactory.cs

public class UserClaimsPrincipalFactory<TUser, TRole> : IUserClaimsPrincipalFactory<TUser> 
    where TUser : class 
    where TRole : class 
{ 
    /// <summary> 
    /// Initializes a new instance of the <see cref="UserClaimsPrincipalFactory{TUser, TRole}"/> class. 
    /// </summary> 
    /// <param name="userManager">The <see cref="UserManager{TUser}"/> to retrieve user information from.</param> 
    /// <param name="roleManager">The <see cref="RoleManager{TRole}"/> to retrieve a user's roles from.</param> 
    /// <param name="optionsAccessor">The configured <see cref="IdentityOptions"/>.</param> 
    public UserClaimsPrincipalFactory(
     UserManager<TUser> userManager, 
     RoleManager<TRole> roleManager, 
     IOptions<IdentityOptions> optionsAccessor) 
    { 
     if (optionsAccessor == null || optionsAccessor.Value == null) 
     { 
      throw new ArgumentNullException(nameof(optionsAccessor)); 
     } 
     UserManager = userManager ?? throw new ArgumentNullException(nameof(userManager)); 
     RoleManager = roleManager ?? throw new ArgumentNullException(nameof(roleManager)); 
     Options = optionsAccessor.Value; 
    } 

    /// <summary> 
    /// Gets the <see cref="UserManager{TUser}"/> for this factory. 
    /// </summary> 
    /// <value> 
    /// The current <see cref="UserManager{TUser}"/> for this factory instance. 
    /// </value> 
    public UserManager<TUser> UserManager { get; private set; } 

    /// <summary> 
    /// Gets the <see cref="RoleManager{TRole}"/> for this factory. 
    /// </summary> 
    /// <value> 
    /// The current <see cref="RoleManager{TRole}"/> for this factory instance. 
    /// </value> 
    public RoleManager<TRole> RoleManager { get; private set; } 

    /// <summary> 
    /// Gets the <see cref="IdentityOptions"/> for this factory. 
    /// </summary> 
    /// <value> 
    /// The current <see cref="IdentityOptions"/> for this factory instance. 
    /// </value> 
    public IdentityOptions Options { get; private set; } 

    /// <summary> 
    /// Creates a <see cref="ClaimsPrincipal"/> from an user asynchronously. 
    /// </summary> 
    /// <param name="user">The user to create a <see cref="ClaimsPrincipal"/> from.</param> 
    /// <returns>The <see cref="Task"/> that represents the asynchronous creation operation, containing the created <see cref="ClaimsPrincipal"/>.</returns> 
    public virtual async Task<ClaimsPrincipal> CreateAsync(TUser user) 
    { 
     if (user == null) 
     { 
      throw new ArgumentNullException(nameof(user)); 
     } 
     var userId = await UserManager.GetUserIdAsync(user); 
     var userName = await UserManager.GetUserNameAsync(user); 
     var id = new ClaimsIdentity(CookieAuthenticationDefaults.AuthenticationScheme, 
      Options.ClaimsIdentity.UserNameClaimType, 
      Options.ClaimsIdentity.RoleClaimType); 
     id.AddClaim(new Claim(Options.ClaimsIdentity.UserIdClaimType, userId)); 
     id.AddClaim(new Claim(Options.ClaimsIdentity.UserNameClaimType, userName)); 
     //if (UserManager.SupportsUserSecurityStamp) 
     //{ 
     // id.AddClaim(new Claim(Options.ClaimsIdentity.SecurityStampClaimType, 
     //  await UserManager.GetSecurityStampAsync(user))); 
     //} 
     if (UserManager.SupportsUserRole) 
     { 
      var roles = await UserManager.GetRolesAsync(user); 
      foreach (var roleName in roles) 
      { 
       id.AddClaim(new Claim(Options.ClaimsIdentity.RoleClaimType, roleName)); 
       if (RoleManager.SupportsRoleClaims) 
       { 
        var role = await RoleManager.FindByNameAsync(roleName); 
        if (role != null) 
        { 
         id.AddClaims(await RoleManager.GetClaimsAsync(role)); 
        } 
       } 
      } 
     } 
     if (UserManager.SupportsUserClaim) 
     { 
      id.AddClaims(await UserManager.GetClaimsAsync(user)); 
     } 
     return new ClaimsPrincipal(id); 
    } 

} 

Voici le api que je vous appelle. (rien de spécial)

[Authorize] 
// Get the account profile information for the current user 
[Route("api/[controller]")] 
public class UserController : Controller 
{ 
    ILogger _logger; 
    private readonly UserManager<ApplicationUser> _userManager; 
    private ICompanyService _companyService; 
    private IUserService _userService; 
    public UserController(ILoggerFactory loggerFactory, UserManager<ApplicationUser> userManager, 
     ICompanyService companyService, IUserService userService) 
    { 
     _logger = loggerFactory.CreateLogger(this.GetType().FullName); 
     _userManager = userManager; 
     _companyService = companyService; 
     _userService = userService; 

    } 

    // GET: api/values 
    [HttpGet] 
    public async Task<User> Get() 
    { 
     User _acct = null; 
     var user = await _userManager.GetUserAsync(this.User); 

     if (user != null) 
     { 
      var company = await _companyService.GetCompany(user.CompanyId); 
      _acct = new User() 
      { 
       //AccountId = this.User. 
       FirstName = user.FirstName, 
       LastName = user.LastName, 
       JoinDate = user.JoinDate, 
       TermsOfAgreementDate = user.TermsOfAgreementDate, 
      }; 
      if (company != null) 
      { 
       _acct.CompanyId = company.CompanyId; 
       _acct.CompanyName = company.CompanyName; 
      } 
     } 

     return _acct; 

    } 


    // PUT api/values/5 
    //  [HttpPut("{id}")] 
    //public async Task Put(int id, [FromBody]DateTime agreedDate) 
    [HttpPut] 
    public async Task<bool> Put([FromBody]User acct) 
    { 
     //var user = await _userManager.GetUserAsync(this.User); 
     ClaimsPrincipal currentUser = this.User; 
     var user = await _userManager.FindByNameAsync(currentUser.Identity.Name); 
     var currentUserId = currentUser.FindFirst(ClaimTypes.NameIdentifier).Value; 

     User _acct = new User 
     { 
      UserId = currentUserId, 
      FirstName = user.FirstName, 
      LastName = user.LastName, 
      JoinDate = user.JoinDate, 
      CompanyId = user.CompanyId, 
      TermsOfAgreementDate = acct.TermsOfAgreementDate 
     }; 

     try { 
      var result = await _userService.UpdateUser(_acct); 
     } 
     catch (Exception e) 
     { 
      _logger.LogError("Error found {0}", e.Message); 
     } 
     return true; 

    } 

} 

Se connecte lorsque mon/api/utilisateur est appelé.

Portal> info: OpenIddict.OpenIddictHandler[0] 
    Portal>  The token response was successfully returned: { 
    Portal>   "token_type": "Bearer", 
    Portal>   "access_token": "[removed for security reasons]", 
    Portal>   "expires_in": 3600, 
    Portal>   "refresh_token": "[removed for security reasons]" 
    Portal>  }. 
    Portal> info: OpenIddict.OpenIddictHandler[0] 
    Portal>  The token response was successfully returned: { 
    Portal>   "token_type": "Bearer", 
    Portal>   "access_token": "[removed for security reasons]", 
    Portal>   "expires_in": 3600, 
    Portal>   "refresh_token": "[removed for security reasons]" 
    Portal>  }. 
    Portal> info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2] 
    Portal>  Executed action Portal.Controllers.Api.AuthorizationController.Exchange (Portal) in 15839.2511ms 
    Portal> info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2] 
    Portal>  Executed action Portal.Controllers.Api.AuthorizationController.Exchange (Portal) in 15839.2511ms 
    Portal> info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2] 
    Portal>  Request finished in 16289.7179ms 200 application/json;charset=UTF-8 
    Portal> info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2] 
    Portal>  Request finished in 16289.7179ms 200 application/json;charset=UTF-8 
    Portal> info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1] 
    Portal>  Request starting HTTP/1.1 GET http://localhost:49142/api/user/ application/json 
    Portal> info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1] 
    Portal>  Request starting HTTP/1.1 GET http://localhost:49142/api/user/ application/json 
    Portal> info: Microsoft.AspNetCore.Authorization.DefaultAuthorizationService[2] 
    Portal>  Authorization failed for user: (null). 
    Portal> info: Microsoft.AspNetCore.Authorization.DefaultAuthorizationService[2] 
    Portal>  Authorization failed for user: (null). 
    Portal> info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[3] 
    Portal>  Authorization failed for the request at filter 'Microsoft.AspNetCore.Mvc.Authorization.AuthorizeFilter'. 
    Portal> info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[3] 
    Portal>  Authorization failed for the request at filter 'Microsoft.AspNetCore.Mvc.Authorization.AuthorizeFilter'. 
    Portal> info: Microsoft.AspNetCore.Mvc.ChallengeResult[1] 
    Portal>  Executing ChallengeResult with authentication schemes(). 
    Portal> info: Microsoft.AspNetCore.Mvc.ChallengeResult[1] 
    Portal>  Executing ChallengeResult with authentication schemes(). 
    Portal> info: Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationHandler[12] 
    Portal>  AuthenticationScheme: Identity.Application was challenged. 
    Portal> info: Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationHandler[12] 
    Portal>  AuthenticationScheme: Identity.Application was challenged. 
    Portal> info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2] 
    Portal>  Executed action Portal.Controllers.Api.UserController.Get (Portal) in 5947.5521ms 
    Portal> info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2] 
    Portal>  Executed action Portal.Controllers.Api.UserController.Get (Portal) in 5947.5521ms 
    Portal> info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2] 
    Portal>  Request finished in 5979.075ms 302 
    Portal> info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2] 
    Portal>  Request finished in 5979.075ms 302 

Répondre

2

Mettez à jour vos options d'authentification pour remplacer les modèles par défaut fixés par services.AddIdentity():

services.AddAuthentication(options => 
{ 
    options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; 
    options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; 
}); 

Vous pouvez décorer vos contrôleurs API/actions avec [Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]