I have a .NET 7 Web API that I'm integrating with AWS Cognito. I expect users to supply a bearer token in order to access the endpoints.
The API does not allow users to sign in or have a sign in page or anything. It's just a RESTful API and expects you to show up with an access token.
In my Program.cs
I have the following:
builder.Services
.AddAuthentication(options => options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options => builder.Configuration.GetSection("Auth:Cognito").Bind(options));
In my appsettings.Development.json
I have the following:
"Auth": {
"Cognito": {
"Authority": "https://cognito-idp.[redacted].amazonaws.com/[redacted]",
"ClientId": "[redacted]",
"TokenValidationParameters": {
"NameClaimType": "username",
"RoleClaimType": "cognito:groups",
"ValidateIssuerSigningKey": true,
"ValidateIssuer": true,
"ValidateAudience": false
}
}
}
This works great and successfully grants logged in users to access my API. I even have the user's groups as roles because of the RoleClaimType
.
The problem is that I want a few more claims. Cognito has user pool standard attributes.
For example, I know the user has an email
claim that I can access. And another claim I want is zoneinfo
.
Of course, the attributes are part of OIDC, and therefore they are not in the access token that is supplied as the bearer token.
My question is: how do I get these attributes?
I guess could implement my own token validation code in C# and go fetch the user's profile from the /oauth2/userInfo
endpoint with an HttpClient, but this feels hacky and like I'm re-inventing the wheel?
I then tried adding builder.services.AddOpenIdConnect()
from the OpenIdConnect Nuget package. But I couldn't figure out how to make this work with a JWT. It seems like it's built to be used with cookies and a local sign in page, but my API cannot do that.
I was hoping that there was some way to configure things so that I can simply tell my application that I need more claims, like email and zoneinfo, and it will fetch these from the OIDC endpoints (userInfo) automatically.
Is this possible? I feel like I've misunderstood things and I'm a bit lost on how to proceed.
Your understanding seems right to me. Preferably claims used for authorization, such as role / tenant_id / country etc should be issued to the access token. Issuing scopes and claims together ensures least privilege access tokens.
Yet Cognito does not support custom claims in access tokens so you have to look them up. One way to do so is to design a ClaimsPrincipal with JWT + extra claims, then inject that into your business logic.
public class CustomClaimsPrincipal : ClaimsPrincipal
{
public JwtClaims JwtClaims:
public ExtraClaims ExtraClaims:
}
This can be a useful technique anyway, since the finer business permissions used for authorization are not usually issued to access tokens, yet your API logic may need them to be readily available.
Here is some code of mine that looks up extra claims:
Another common option in .NET, which may be simpler than my .NET code, is to use the onTokenValidated hook to rewrite the claims principal.