User.Identity is not retrieved when JWT auth is enabled - asp.net-core

I have first started with a cookie authentication and somehow after also enabling JWT it doesnt let me to retrieve the user by the User.Identity, object
and I found this article which suggested that you can enable both
https://weblog.west-wind.com/posts/2022/Mar/29/Combining-Bearer-Token-and-Cookie-Auth-in-ASPNET
this is my program cs on the Authentication config
builder.Services.AddAuthentication(options => {
options.DefaultAuthenticateScheme = "JWT_OR_COOKIE"/*"Identity.Application"*/;
options.DefaultChallengeScheme = "JWT_OR_COOKIE";
})
.AddCookie("Cookies", options =>
{
options.LoginPath = "/identity/account/login";
options.ExpireTimeSpan = TimeSpan.FromDays(1);
})
.AddJwtBearer("Bearer",options =>
{
options.RequireHttpsMetadata = false;
options.Authority = "/Security/Token/Validate"; // TODO: Update URL
options.TokenValidationParameters = new TokenValidationParameters
{
ValidIssuer = builder.Configuration["Jwt:Issuer"],
ValidAudience=builder.Configuration["Jwt:Audience"],
IssuerSigningKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(builder.Configuration["Jwt:Key"])),
};
options.Events = new JwtBearerEvents
{
OnMessageReceived = context =>
{
var accessToken = context.Request.Query["access_token"];
var path = context.HttpContext.Request.Path;
if (!string.IsNullOrEmpty(accessToken) &&
(path.StartsWithSegments("/ConnectionsHub")))
{
context.Token = accessToken;
}
return Task.CompletedTask;
}
};
})
.AddPolicyScheme("JWT_OR_COOKIE", "JWT_OR_COOKIE", options =>
{
// runs on each request
options.ForwardDefaultSelector = context =>
{
string authorization = context.Request.Headers[HeaderNames.Authorization];
if (!string.IsNullOrEmpty(authorization) && authorization.StartsWith("Bearer "))
return "Bearer";
return "Cookies";
};
});
...
app.UseAuthentication();
app.UseAuthorization();

Related

External Connection With MicrosoftIdentityWebApp And Identity

I try to login User from Microsoft account into my app.
First : I'm following this info, and that'a working well.
https://learn.microsoft.com/fr-fr/azure/active-directory/develop/web-app-quickstart?pivots=devlang-aspnet-core
But, when I try merge it with the classic Identity Login, on the external Login, this function return null all the time. And the login info on the starup is correct. I think it was a schema problem but I try to change it and it was not.
var info = await _signInManager.GetExternalLoginInfoAsync();
public async Task<IActionResult> OnGetCallbackAsync(string returnUrl = null, string remoteError = null)
{
returnUrl = returnUrl ?? Url.Content("~/");
if (remoteError != null)
{
ErrorMessage = $"Error from external provider: {remoteError}";
return RedirectToPage("./Login", new { ReturnUrl = returnUrl });
}
var userl = _signInManager.IsSignedIn(User);
//ExternalLoginInfo info = new ExternalLoginInfo(User, "Microsoft", User.Claims.Where(x => x.Type == "http://schemas.microsoft.com/identity/claims/objectidentifier").FirstOrDefault().Value.ToString(), "Microsoft");
var info = await _signInManager.GetExternalLoginInfoAsync();
if (info == null)
{
ErrorMessage = "Error loading external login information.";
return RedirectToPage("./Login", new { ReturnUrl = returnUrl });
}
// Sign in the user with this external login provider if the user already has a login.
var result = await _signInManager.ExternalLoginSignInAsync(info.LoginProvider, info.ProviderKey, isPersistent: false, bypassTwoFactor: true);
if (result.Succeeded)
{
_logger.LogInformation("{Name} logged in with {LoginProvider} provider.", info.Principal.Identity.Name, info.LoginProvider);
return LocalRedirect(returnUrl);
}
if (result.IsLockedOut)
{
return RedirectToPage("./Lockout");
}
else
{
// If the user does not have an account, then ask the user to create an account.
ReturnUrl = returnUrl;
ProviderDisplayName = info.ProviderDisplayName;
if (info.Principal.HasClaim(c => c.Type == ClaimTypes.Email))
{
Input = new InputModel
{
Email = info.Principal.FindFirstValue(ClaimTypes.Email)
};
}
return Page();
}
}
Startup.cs
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
var connectionString = builder.Configuration.GetConnectionString("DefaultConnection");
builder.Services.AddDbContext<ApplicationDbContext>(options => options.UseSqlServer(connectionString));
builder.Services.AddDbContext<DatabaseContext>(options => options.UseSqlServer(connectionString));
builder.Services.AddDatabaseDeveloperPageExceptionFilter();
builder.Services.AddDefaultIdentity<ApplicationUser>(options => options.SignIn.RequireConfirmedAccount = false)
.AddRoles<IdentityRole>()
.AddErrorDescriber<FrenchIdentityErrorDescriber>()
.AddEntityFrameworkStores<ApplicationDbContext>();
builder.Services.AddRazorPages();
builder.Services.AddDatabaseDeveloperPageExceptionFilter();
builder.Services
.AddControllersWithViews()
.AddRazorRuntimeCompilation()
.AddJsonOptions(options =>
{
options.JsonSerializerOptions.PropertyNameCaseInsensitive = true;
options.JsonSerializerOptions.PropertyNamingPolicy = null;
});
builder.Services.AddMvc();
builder.Services.AddDistributedMemoryCache();
builder.Services.AddSession(options =>
{
options.IdleTimeout = TimeSpan.FromHours(8);
options.Cookie.HttpOnly = true;
options.Cookie.IsEssential = true;
//options.Cookie.Name = "WebSignature.Session";
});
builder.Services.Configure<IdentityOptions>(options =>
{
// Password settings.
options.Password.RequireDigit = true;
options.Password.RequireLowercase = true;
options.Password.RequireNonAlphanumeric = true;
options.Password.RequireUppercase = true;
options.Password.RequiredLength = 6;
options.Password.RequiredUniqueChars = 1;
// Lockout settings.
options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(5);
options.Lockout.MaxFailedAccessAttempts = 5;
options.Lockout.AllowedForNewUsers = true;
// User settings.
options.User.AllowedUserNameCharacters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._#+";
options.User.RequireUniqueEmail = false;
});
builder.Services.ConfigureApplicationCookie(options =>
{
// Cookie settings
options.Cookie.HttpOnly = true;
options.ExpireTimeSpan = TimeSpan.FromHours(12);
options.LoginPath = ApplicationRedirection.PageConnexion;
options.LogoutPath = "/Identity/Account/Logout";
options.AccessDeniedPath = "/Erreurs/403";
options.SlidingExpiration = true;
});
builder.Services
.AddAuthentication()
.AddJwtBearer(options =>
{
var SecretKey = builder.Configuration.GetSection("JwtToken").GetValue<string>("SecretKey") ?? string.Empty;
var Audience = builder.Configuration.GetSection("JwtToken").GetValue<string>("Audience") ?? string.Empty;
var Issuer = builder.Configuration.GetSection("JwtToken").GetValue<string>("Issuer") ?? string.Empty;
options.RequireHttpsMetadata = false;
options.SaveToken = true;
options.TokenValidationParameters = new TokenValidationParameters
{
ClockSkew = TimeSpan.Zero,
ValidateAudience = true,
ValidAudience = Audience,
ValidateIssuer = true,
ValidIssuer = Issuer,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(SecretKey))
};
});
builder.Services.AddAuthentication()
.AddMicrosoftIdentityWebApp(options =>
{
var config = builder.Configuration.GetSection("AzureAd");
options.Instance = config["Instance"];
options.Domain = config["Domain"];
options.ClientId = config["ClientId"];
options.TenantId = config["TenantId"];
options.ClientSecret = config["ClientSecret"];
options.CallbackPath = config["CallbackPath"];
options.RequireHttpsMetadata = true;
options.GetClaimsFromUserInfoEndpoint = true;
options.SaveTokens = true;
options.UsePkce = true;
options.ResponseType = OpenIdConnectResponseType.Code;
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true
};
})
.EnableTokenAcquisitionToCallDownstreamApi(builder.Configuration.GetValue<string>("DownstreamApi:Scopes")?.Split(' '))
.AddMicrosoftGraph(builder.Configuration.GetSection("DownstreamApi"))
.AddInMemoryTokenCaches();
builder.Services.AddControllersWithViews(options =>
{
var policy = new AuthorizationPolicyBuilder().RequireAuthenticatedUser().Build();
options.Filters.Add(new AuthorizeFilter(policy));
});
builder.Services.AddAuthorization(options =>
{
options.AddPolicy(PolicyApplication.Administrateur, policy => policy.RequireAuthenticatedUser().RequireRole(RoleUtilisateur.Administrateur)/*.AddAuthenticationSchemes(IdentityConstants.ApplicationScheme)*/);
options.AddPolicy(PolicyApplication.Utilisateur, policy => policy.RequireAuthenticatedUser().RequireRole(RoleUtilisateur.Utilisateur)/*.AddAuthenticationSchemes(IdentityConstants.ApplicationScheme)*/);
/// Récupère tous les roles de la class
var AllRole = typeof(RoleUtilisateur).GetFields().Select(x => x.GetValue(x.Name)?.ToString() ?? "").ToArray();
options.AddPolicy(PolicyApplication.All, policy => policy.RequireAuthenticatedUser().RequireRole(AllRole)/*.AddAuthenticationSchemes(IdentityConstants.ApplicationScheme)*/);
/// Pour l'API
options.AddPolicy(PolicyApplication.API, policy => policy.RequireAuthenticatedUser().AddAuthenticationSchemes(JwtBearerDefaults.AuthenticationScheme));
});
builder.Services.Configure<GzipCompressionProviderOptions>(o => o.Level = CompressionLevel.Optimal);
builder.Services.AddKendo();
builder.Services.AddAntiforgery(o => o.HeaderName = "XSRF-TOKEN");
builder.Services.AddRazorPages()
.AddMicrosoftIdentityUI();
var app = builder.Build();
var supportedCultures = new[] { new CultureInfo("fr-FR") };
app.UseRequestLocalization(new RequestLocalizationOptions()
{
DefaultRequestCulture = new RequestCulture(new CultureInfo("fr-FR")),
SupportedCultures = supportedCultures,
SupportedUICultures = supportedCultures
});
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseMigrationsEndPoint();
}
else
{
app.UseExceptionHandler("/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseSwagger();
app.UseSwaggerUI();
app.UseStatusCodePages(context =>
{
...
return Task.CompletedTask;
});
app.UseSession();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
endpoints.MapRazorPages();
});
app.Run();
Value Null
I the problem is due to the claims who was empty. But i don't know why.
enter image description here
So what is wrong, any ideas ?
Finally I found.
Simply use that
builder.Services.AddAuthentication()
.AddMicrosoftAccount(options =>
{
var config = builder.Configuration.GetSection("AzureAd");
options.ClientId = config["ClientId"] ?? string.Empty;
options.ClientSecret = config["ClientSecret"] ?? string.Empty;
options.CallbackPath = config["CallbackPath"] ?? string.Empty;
});
than
builder.Services.AddAuthentication()
.AddMicrosoftIdentityWebApp(options =>
{
var config = builder.Configuration.GetSection("AzureAd");
options.Instance = config["Instance"];
options.Domain = config["Domain"];
options.ClientId = config["ClientId"];
options.TenantId = config["TenantId"];
options.ClientSecret = config["ClientSecret"];
options.CallbackPath = config["CallbackPath"];
options.RequireHttpsMetadata = true;
options.GetClaimsFromUserInfoEndpoint = true;
options.SaveTokens = true;
options.UsePkce = true;
options.ResponseType = OpenIdConnectResponseType.Code;
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true
};
})
.EnableTokenAcquisitionToCallDownstreamApi(builder.Configuration.GetValue<string>("DownstreamApi:Scopes")?.Split(' '))
.AddMicrosoftGraph(builder.Configuration.GetSection("DownstreamApi"))
.AddInMemoryTokenCaches();

HttpContext.User.Identity.IsAuthenticated is false after calling HttpContext.SignInAsync("schema-name", claimsPrincipal);

I am using multiple authentication schemes. Here is my Startup.cs code -
public void ConfigureServices(IServiceCollection services)
{
//code
services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = "Default";
options.DefaultSignInScheme = "Default";
options.DefaultChallengeScheme = "O365OpenId";
})
.AddCookie("Default",options =>
{
options.LoginPath = "/";
options.LogoutPath = "/Logout";
options.Cookie.Name = "ip" + guid.ToString();
options.AccessDeniedPath = "/Auth/Denied";
})
.AddCookie("External", options =>
{
options.LoginPath = "/Auth/External";
options.LogoutPath = "/Logout";
options.Cookie.Name = "ip" + guid.ToString();
options.AccessDeniedPath = "/Auth/Denied";
})
.AddOAuth("O365OpenId", options =>
{
//options.Authority = "given";
options.ClientId = "given";
options.ClientSecret = "given";
options.CallbackPath = "/auth/callback";
options.AuthorizationEndpoint = "https://login.microsoftonline.com/common/oauth2/v2.0/authorize";
options.AuthorizationEndpoint += "?prompt=select_account";
options.TokenEndpoint = "https://login.microsoftonline.com/common/oauth2/v2.0/token";
options.UserInformationEndpoint = "https://graph.microsoft.com/oidc/userinfo";
options.AccessDeniedPath = "/Auth/Denied";
options.Scope.Add("openid");
options.Scope.Add("email");
//options.Scope.Add("profile");
//options.Scope.Add("offline_access");
//options.Scope.Add("user.read");
//options.Scope.Add("calendars.read");
options.SaveTokens = false;
//options.ClaimActions.MapJsonKey(ClaimTypes.NameIdentifier, "id");
//options.ClaimActions.MapJsonKey(ClaimTypes.GivenName, "given_name");
//options.ClaimActions.MapJsonKey(ClaimTypes.Surname, "family_name");
//options.ClaimActions.MapJsonKey(ClaimTypes.Name, "name");
options.ClaimActions.MapJsonKey(ClaimTypes.Email, "email");
//options.ClaimActions.MapJsonKey(ClaimTypes., "picture");
options.Events = new OAuthEvents
{
OnCreatingTicket = async context =>
{
if (context.AccessToken is { })
{
context.Identity?.AddClaim(new Claim("access_token", context.AccessToken));
}
var request = new HttpRequestMessage(HttpMethod.Get, context.Options.UserInformationEndpoint);
request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", context.AccessToken);
var response = await context.Backchannel.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, context.HttpContext.RequestAborted);
response.EnsureSuccessStatusCode();
var json = JsonDocument.Parse(await response.Content.ReadAsStringAsync());
context.RunClaimActions(json.RootElement);
};
});
services.AddAuthorization(options =>
{
options.AddPolicy("Admin", policy =>
{
policy.AuthenticationSchemes.Add(CookieAuthenticationDefaults.AuthenticationScheme);
policy.RequireAuthenticatedUser();
policy.RequireRole("Admin");
});
options.AddPolicy("Regular&Admin", policy =>
{
policy.AuthenticationSchemes.Add(CookieAuthenticationDefaults.AuthenticationScheme);
policy.RequireAuthenticatedUser();
policy.RequireRole("Regular");
});
options.AddPolicy("External", policy =>
{
policy.AuthenticationSchemes.Add("External");
policy.RequireAuthenticatedUser();
policy.RequireRole("External");
});
});
}
When I sign in user using the "Default" scheme, I find that the HttpContext.User.Identity.IsAuthenticated is true after HttpContext.SignInAsync("schema-name", claimsPrincipal); method call. Here is the code for signing in -
public async Task<IActionResult> Success(string returnUrl)
{
// code
var claimIdentity = new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationScheme);
var claimsPrincipal = new ClaimsPrincipal(claimIdentity);
await HttpContext.SignInAsync("Default",claimsPrincipal);
// more code
}
isAuthenticated is true
But When I sign in user using "External" scheme, HttpContext.User.Identity.IsAuthenticated is false after HttpContext.SignInAsync("schema-name", claimsPrincipal); call. Here is the code for signing in -
public async Task<IActionResult> External([FromQuery] string ReturnUrl, LoginOTP loginOTP, string email)
{
//code
var claimIdentity = new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationScheme);
var claimsPrincipal = new ClaimsPrincipal(claimIdentity);
await HttpContext.SignInAsync("External", claimsPrincipal);
//code
}
IsAuthenticated is false.
I cannot understand what I am doing wrong. Why is the IsAuthenticated returning false?
Please help. Thank you.
you have 3 authentication schemes
the first one is cookie-based and you named it: "Default",
the second one is also cookie-based and you named it: "External",
the third one is OAhut-based and you named it: "O365OpenId",
when referring to one of these schemes you should use the names that you have created.
now the issue is, that you're creating a ClaimeIdentity with one scheme but you signing the user with a different scheme.
for the external method it should be like this:
var claimIdentity = new ClaimsIdentity(claims, "External");
var claimsPrincipal = new ClaimsPrincipal(claimIdentity);
// code ...
await HttpContext.SignInAsync("External", claimsPrincipal);
now the claims principal will be associated with the "External" scheme and the user will be authenticated.

Cannot access [Authorize] controllers with cookie authentication

I'm using .NET Core 3.1 and Cookie authentication to login.
I saw the cookie generated as the picture below, but I still cannot access [Authorize] controllers.
This is my code in Startup.cs
ConfigureServices(IServiceCollection services)
services.Configure<CookiePolicyOptions>(options =>
{
options.CheckConsentNeeded = context => true;
options.MinimumSameSitePolicy = SameSiteMode.Lax;
});
services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
.AddCookie(CookieAuthenticationDefaults.AuthenticationScheme, options =>
{
options.Cookie.Name = "SRLoginCookie";
options.Cookie.HttpOnly = true;
options.LoginPath = new PathString("/users/login");
options.ExpireTimeSpan = TimeSpan.FromDays(1);
options.SlidingExpiration = false;
});
services.PostConfigure<CookieAuthenticationOptions>(IdentityConstants.ApplicationScheme,
options =>
{
options.LoginPath = "/users/login";
});
Configure(IApplicationBuilder app)
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
Login code
[HttpPost]
[Route("login")]
[AllowAnonymous]
public async Task<ActionResult> Login([FromForm]LoginRequest model)
{
var identity = new ClaimsIdentity(CookieAuthenticationDefaults.AuthenticationScheme);
identity.AddClaim(new Claim(ClaimTypes.Email, model.Email));
ClaimsPrincipal principal = new ClaimsPrincipal(identity);
await HttpContext.SignInAsync(
CookieAuthenticationDefaults.AuthenticationScheme,
principal);
//if (HttpContext.User.Identity.IsAuthenticated)
return RedirectToAction("Index", "Home");
}
The cookie was generated, but can't access home/index as you see. What is my problem? Many thanks for help!
I found the solution after posting this question for a while.
Just need [Authorize(AuthenticationSchemes = CookieAuthenticationDefaults.AuthenticationScheme)] instead of [Authorize], everthing solved.
Try this:
builder.Services
.AddAuthentication(options =>
{
options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(JwtBearerDefaults.AuthenticationScheme, options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
ValidIssuer = builder.Configuration["Jwt:Issuer"],
ValidAudience = builder.Configuration["Jwt:Issuer"],
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(builder.Configuration["Jwt:Key"]))
};
options.Events = new JwtBearerEvents()
{
OnMessageReceived = context =>
{
// get token from cookie
if (context.Request.Cookies.ContainsKey("SRLoginCookie"))
{
context.Token = context.Request.Cookies["SRLoginCookie"];
}
return Task.CompletedTask;
}
};
});

OpenIdConnectEvents.OnTokenValidated not being reached

using asp.net core 2.2, I have the following in my startup below
i reach OnRedirectToIdentityProvider breakpoint , and then I reach relative path in appsettings "CallbackPath": " . But I don't i reach OnTokenValidated breakpoint . the Auth is triggered by [Authorize] decoration of a controller.
What am i missing ?
services.AddAuthentication(AzureADDefaults.AuthenticationScheme)
.AddAzureAD(options => Configuration.Bind("AzureAd", options))
.AddCookie();
services.Configure<OpenIdConnectOptions>(AzureADDefaults.OpenIdScheme, options =>
{
options.Authority = options.Authority + "/v2.0/"; // Microsoft identity platform
options.Events = new OpenIdConnectEvents
{
OnRedirectToIdentityProvider = async n =>
{
//save url to state
n.ProtocolMessage.State = n.HttpContext.Request.Path.Value.ToString();
},
OnTokenValidated = ctx =>
{
var url = ctx.ProtocolMessage.GetParameter("state");
var claims = new List<Claim>
{
new Claim("myurl", url)
};
var appIdentity = new ClaimsIdentity(claims);
//add url to claims
ctx.Principal.AddIdentity(appIdentity);
return Task.CompletedTask;
},
OnTicketReceived = ctx =>
{
var url = ctx.Principal.FindFirst("myurl").Value;
ctx.ReturnUri = url;
return Task.CompletedTask;
}
};
options.TokenValidationParameters.ValidateIssuer = false; // accept several tenants (here simplified)
});
You can change of ResponseMode to FormPost and add async to OnTokenValidated then it will be fixed.
services.Configure<OpenIdConnectOptions>(AzureADDefaults.OpenIdScheme, options =>
{
options.Authority = options.Authority + "/v2.0/"; // Microsoft identity platform
options.ResponseMode = OpenIdConnectResponseMode.FormPost;
options.CallbackPath = "/";
options.Events = new OpenIdConnectEvents
{
OnRedirectToIdentityProvider = async n =>
{
...
},
OnTokenValidated = async ctx =>
{
...
},

HttpContext.User null with authenticated user?

So I'm working on a .net core 2 project which where we want to create a basic platform which we can use for our future projects. For the login we use Identity. We have it all setup, the user can succesfully login and the cookie gets set. For some reason once we call HttpContext.User this results in a null. I'm pretty sure it does find an identity, yet this identity is empty. We have checked the cookie and it is perfectly fine, it has it's token. We did add token authentication, but that should not interfere with the cookie system when it sets the cookie.
Below is the Startup.cs
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<MyIdentityDbContext>(options => options
.UseSqlServer("Data Source=PATH;Initial Catalog=DB;Persist Security Info=True;User ID=ID;Password=*******"));
services.AddSingleton<IJwtFactory, JwtFactory>();
services.AddAuthentication(options =>
{
options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
}).AddCookie();
services.AddIdentity<User, IdentityRole>(options =>
{
options.SignIn.RequireConfirmedEmail = true;
options.User.RequireUniqueEmail = false;
options.Tokens.PasswordResetTokenProvider = TokenOptions.DefaultEmailProvider;
})
.AddEntityFrameworkStores<MyIdentityDbContext>()
.AddDefaultTokenProviders();
services.Configure<IISOptions>(options =>
{
options.ForwardClientCertificate = false;
});
var jwtAppSettingOptions = Configuration.GetSection(nameof(JwtIssuerOptions));
services.Configure<JwtIssuerOptions>(options =>
{
options.Issuer = jwtAppSettingOptions[nameof(JwtIssuerOptions.Issuer)];
options.Audience = jwtAppSettingOptions[nameof(JwtIssuerOptions.Audience)];
options.SigningCredentials = new SigningCredentials(_signingKey, SecurityAlgorithms.HmacSha256);
});
var tokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidIssuer = jwtAppSettingOptions[nameof(JwtIssuerOptions.Issuer)],
ValidateAudience = true,
ValidAudience = jwtAppSettingOptions[nameof(JwtIssuerOptions.Audience)],
ValidateIssuerSigningKey = true,
IssuerSigningKey = _signingKey,
RequireExpirationTime = false,
ValidateLifetime = true,
ClockSkew = TimeSpan.Zero
};
services
.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(cfg =>
{
cfg.ClaimsIssuer = jwtAppSettingOptions[nameof(JwtIssuerOptions.Issuer)];
cfg.TokenValidationParameters = tokenValidationParameters;
cfg.SaveToken = true;
});
services.Configure<IdentityOptions>(options =>
{
// Password settings
options.Password.RequireDigit = true;
options.Password.RequiredLength = 8;
options.Password.RequireNonAlphanumeric = true;
options.Password.RequireUppercase = true;
options.Password.RequireLowercase = true;
options.Password.RequiredUniqueChars = 6;
// Lockout settings
options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(30);
options.Lockout.MaxFailedAccessAttempts = 5;
options.Lockout.AllowedForNewUsers = true;
// User settings
options.User.RequireUniqueEmail = true;
});
services.ConfigureApplicationCookie(options =>
{
// Cookie settings
options.LoginPath = "/Account/Login"; // If the LoginPath is not set here, ASP.NET Core will default to /Account/Login
options.LogoutPath = "/Account/Logout"; // If the LogoutPath is not set here, ASP.NET Core will default to /Account/Logout
options.AccessDeniedPath = "/Account/AccessDenied"; // If the AccessDeniedPath is not set here, ASP.NET Core will default to /Account/AccessDenied
options.SlidingExpiration = true;
options.Cookie = new CookieBuilder
{
HttpOnly = true,
Name = "MyAuthToken",
Path = "/",
SameSite = SameSiteMode.Lax,
SecurePolicy = CookieSecurePolicy.SameAsRequest
};
});
services.AddAuthorization(options =>
{
options.AddPolicy("EmployeeOnly", policy => policy.RequireClaim("Employee"));
options.AddPolicy("OwnerOnly", policy => policy.RequireClaim("Owner"));
options.AddPolicy("AdminOnly", policy => policy.RequireClaim("Admin"));
options.AddPolicy("ModeratorOnly", policy => policy.RequireClaim("Moderator"));
});
services.AddTransient<IEmailSender, EmailSender>();
services.Configure<AuthMessageSenderOptions>(Configuration);
services.AddMvc().AddJsonOptions(options => options.SerializerSettings.ContractResolver = new DefaultContractResolver());
}
Here is the code used in the controller to get the user:
User _user = await _userManager.GetUserAsync(HttpContext.User);
And the code we use to login the user:
var result = await _signInManager.PasswordSignInAsync(model.Email,
model.Password, true, false);
You wrote AddAuthentication two times, once for Cookie and once for JWT and override the defaults.
only use AddAuthentication once and add Cookie and JWT to it.
services.AddAuthentication(options =>
{
// set schema here
})
.AddCookie(config =>
{
//config cookie
})
.AddJwtBearer(config =>
{
//config jwt
});
Now that you have two authentication scheme, you must select which one you want to authenticate your request with
[Authorize(CookieAuthenticationDefaults.AuthenticationScheme)]
or
[Authorize(JwtBearerDefaults.AuthenticationScheme)]
or even both
[Authorize($"{CookieAuthenticationDefaults.AuthenticationScheme},{JwtBearerDefaults.AuthenticationScheme}")]