i've been stuck for a few days trying to configure the PasswordFlow/Refresh sample and need some help on how to troubleshoot my issue.

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

I tried the suggestion made by Kevin Chalet on July 19th to use 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>();
    }
}

I was able to successfully login but when I'm trying to make a GET call /api/company and getting this Authorization failed for user. I upgraded to .NET Core 2.0 and using Angular 2.0 (with angular-jwt). I'm getting the access token and refresh token. I'm able to decode the token on the browser. I would greatly appreciate the help!

I also added the 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);
    }

}

Here's the api that I'm calling. (nothing special)

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

    }

}

Logs when my /api/user is called.

    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 
1

There are 1 answers

0
Kévin Chalet On BEST ANSWER

Update your authentication options to override the default schemes set by services.AddIdentity():

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

Alternatively, you can decorate your API controllers/actions with [Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]