When having multiple authentication schemes, how do I access the ClaimsPrincipal for non-default ClaimsPrincipal?
I have a POC app that has 2 authentication schemes, both are cookie-based. When I login with the first (default) authentication scheme, I can see the created identity and associated claims on context.User
.
However, when I try to login with the second (non-default) authentication scheme, I do not get any identities or claims at all even though the second cookie is set.
What am I missing here?
using System.Security.Claims;
using System.Text.Json;
using System.Text.Json.Serialization;
using Microsoft.AspNetCore.Authentication;
var builder = WebApplication.CreateBuilder(args);
const string scheme1 = "cookie1";
const string scheme2 = "cookie2";
builder.Services.AddAuthentication(scheme1)
.AddCookie(scheme1, (options) => { options.Cookie.Name = scheme1; })
.AddCookie(scheme2, (options) => { options.Cookie.Name = scheme2; });
var app = builder.Build();
app.MapGet("/", (HttpContext context) =>
{
var result = JsonSerializer.Serialize(context.User, new JsonSerializerOptions
{
ReferenceHandler = ReferenceHandler.IgnoreCycles,
WriteIndented = true
});
return result;
});
app.MapGet("/login1", async context =>
{
await SignInAsync(context, "Name1", scheme1);
context.Response.Redirect("/");
});
app.MapGet("/login2", async context =>
{
await SignInAsync(context, "Name2", scheme2);
context.Response.Redirect("/");
});
app.MapGet("/logout1", async context =>
{
await context.SignOutAsync(scheme1);
context.Response.Redirect("/");
});
app.MapGet("/logout2", async context =>
{
await context.SignOutAsync(scheme2);
context.Response.Redirect("/");
});
app.UseAuthentication();
app.Run();
return;
async Task SignInAsync(HttpContext context, string name, string authScheme)
{
var claim = new Claim(ClaimTypes.Name, name, ClaimValueTypes.String, "CIA");
var claims = new List<Claim> {claim};
var identity = new ClaimsIdentity(claims, authScheme);
var user = new ClaimsPrincipal(identity);
await context.SignInAsync(authScheme, user);
}
You should create authorization policy, and also add authorization to your "/" mapping, another way it will be fetching user from default scheme only.
Steps -
AuthorizationPolicy multiSchemePolicy = new AuthorizationPolicyBuilder(scheme1, scheme2)
.RequireAuthenticatedUser()
.Build();
builder.Services.AddAuthorization(options =>
{
options.DefaultPolicy = multiSchemePolicy;
});
app.UseAuthentication();
app.UseAuthorization();
app.MapGet("/", (HttpContext context) =>
{
var result = JsonSerializer.Serialize(context.User, new JsonSerializerOptions
{
ReferenceHandler = ReferenceHandler.IgnoreCycles,
WriteIndented = true
});
return result;
}).RequireAuthorization();
Full class will be looking like this -
// Add services to the container.
using System.Security.Claims;
using System.Text.Json;
using System.Text.Json.Serialization;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authorization;
const string scheme1 = "cookie1";
const string scheme2 = "cookie2";
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddAuthentication()
.AddCookie(scheme1, (options) => {
options.Cookie.Name = scheme1;
})
.AddCookie(scheme2, (options) =>
{
options.Cookie.Name = scheme2;
});
AuthorizationPolicy multiSchemePolicy = new AuthorizationPolicyBuilder(scheme1, scheme2)
.RequireAuthenticatedUser()
.Build();
builder.Services.AddAuthorization(options =>
{
options.DefaultPolicy = multiSchemePolicy;
});
builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.MapGet("/login1", async context =>
{
await SignInAsync(context, "Name1", scheme1);
context.Response.Redirect("/");
});
app.MapGet("/login2", async context =>
{
await SignInAsync(context, "Name2", scheme2);
context.Response.Redirect("/");
});
app.MapGet("/logout1", async context =>
{
await context.SignOutAsync(scheme1);
context.Response.Redirect("/");
});
app.MapGet("/logout2", async context =>
{
await context.SignOutAsync(scheme2);
context.Response.Redirect("/");
});
app.UseAuthentication();
app.UseAuthorization();
app.MapControllers();
app.MapGet("/", (HttpContext context) =>
{
var result = JsonSerializer.Serialize(context.User, new JsonSerializerOptions
{
ReferenceHandler = ReferenceHandler.IgnoreCycles,
WriteIndented = true
});
return result;
}).RequireAuthorization();
app.Run();
async Task SignInAsync(HttpContext context, string name, string authScheme)
{
var claim = new Claim(ClaimTypes.Name, name, ClaimValueTypes.String, "CIA");
var claims = new List<Claim> {claim};
var identity = new ClaimsIdentity(claims, authScheme);
var user = new ClaimsPrincipal(identity);
await context.SignInAsync(authScheme, user);
}