We had https://github.com/Azure/static-web-apps-cli/issues/390 reported with the Static Web Apps CLI, in which using a ClaimsPrincipal in a C# Function doesn't contain the roles that you would expect and IsInRole returns false when it shouldn't, resulting in a different experience with auth locally compared to when it's deployed.
Context
The SWA CLI provides a mock of EasyAuth, in which you can simulate a flow through and then a token is generated for you. This token is then handled by the proxy in the CLI and serialized before being injected into the x-ms-client-principal header. The header can be parsed like so:
public static class StaticWebAppsAuth
{
private class ClientPrincipal
{
public string IdentityProvider { get; set; }
public string UserId { get; set; }
public string UserDetails { get; set; }
public IEnumerable<string> UserRoles { get; set; }
}
public static ClaimsPrincipal Parse(HttpRequest req)
{
var principal = new ClientPrincipal();
if (req.Headers.TryGetValue("x-ms-client-principal", out var header))
{
var data = header[0];
var decoded = Convert.FromBase64String(data);
var json = Encoding.UTF8.GetString(decoded);
principal = JsonSerializer.Deserialize<ClientPrincipal>(json, new JsonSerializerOptions { PropertyNameCaseInsensitive = true });
}
principal.UserRoles = principal.UserRoles?.Except(new string[] { "anonymous" }, StringComparer.CurrentCultureIgnoreCase);
if (!principal.UserRoles?.Any() ?? true)
{
return new ClaimsPrincipal();
}
var identity = new ClaimsIdentity(principal.IdentityProvider);
identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, principal.UserId));
identity.AddClaim(new Claim(ClaimTypes.Name, principal.UserDetails));
identity.AddClaims(principal.UserRoles.Select(r => new Claim(ClaimTypes.Role, r)));
return new ClaimsPrincipal(identity);
}
}
Given that the x-ms-claims-principal header that is injected via the SWA CLI can be decoded it's expected that the ClaimsPrincipal would contain the same information.
Is it possible to inject a header that the Functions runtime (locally) will be able to create a ClaimsPrincipal correctly, or at least one that responds to the IsInRole call correctly? (so, mapping the roles from the header payload correctly)
Aside - I've tried to debug this myself and see where the problem is coming from but I'm unable to find docs on how to do end-to-end debugging of Azure Functions locally. I tried to load symbols from SourceLink but didn't managed to get the debugging working competely (I'm unable to hit the point where it creates the ClaimsPrincipal), and when my debugging chain consisted of SWA CLI -> My Functions App -> Core Tools -> WebJobs Script Host -> WebJobs SDK -> WebJobs SDK Extensions I gave up š¤£. I'm yet to find where the ClaimsPrincipal is created.
We had https://github.com/Azure/static-web-apps-cli/issues/390 reported with the Static Web Apps CLI, in which using a
ClaimsPrincipal
in a C# Function doesn't contain the roles that you would expect andIsInRole
returns false when it shouldn't, resulting in a different experience with auth locally compared to when it's deployed.Context
The SWA CLI provides a mock of EasyAuth, in which you can simulate a flow through and then a token is generated for you. This token is then handled by the proxy in the CLI and serialized before being injected into the
x-ms-client-principal
header. The header can be parsed like so:Reference
But when deployed to Azure, the
ClaimsPrincipal
is automatically populated with the claims information from that header and you are not required to do manual parsing. You can see a sample app here https://github.com/aaronpowell/swa-blazor-cli-bug, which is deployed to https://thankful-desert-0d90ef310.1.azurestaticapps.net/secured (yeah, it's an ugly dumping string of claims).Given that the
x-ms-claims-principal
header that is injected via the SWA CLI can be decoded it's expected that theClaimsPrincipal
would contain the same information.Is it possible to inject a header that the Functions runtime (locally) will be able to create a
ClaimsPrincipal
correctly, or at least one that responds to theIsInRole
call correctly? (so, mapping the roles from the header payload correctly)Aside - I've tried to debug this myself and see where the problem is coming from but I'm unable to find docs on how to do end-to-end debugging of Azure Functions locally. I tried to load symbols from SourceLink but didn't managed to get the debugging working competely (I'm unable to hit the point where it creates the
ClaimsPrincipal
), and when my debugging chain consisted ofSWA CLI
->My Functions App
->Core Tools
->WebJobs Script Host
->WebJobs SDK
->WebJobs SDK Extensions
I gave up š¤£. I'm yet to find where theClaimsPrincipal
is created.