Base solution for your next web application
Open Closed

How to access user details (and claims) from JWT Authentication Token in Application Services and Controllers? #11348


User avatar
0
Theunis created

We are using ASP.NET Zero and we want to use AWS Cognito as Authentication Provider. We are using Cognito to authenticate on Mobile Apps and we also aim to use Cognito to authenticate on the Angular ASP.Net Zero Web Application. Thus, we want to authenticate against a Cognito JWT Access Token in our Application Services as well as our Controllers. I have added JWT Token validation in the Web.Host project and I have decorated the Application Service class with the [Authorize] attribute. When I make a call to the API with a valid JWT token from Cognito I am able to access the service whereas if I have an invalid token I get a 401 Unauthorized error. So the authorization and token validation works nicely as expected. However, I am now trying to identify the user within the Application Service and I am unable to do so. If I set a breakpoint inside the method in the Application Service I can see that the PrincipalAccessor contains the claims from the JWT token. The Principal Accessor isn't accessible from the Application Service though so I don't have a means of reading it. When I try to read a user identifier I also get null values and if I use GetCurrentUser() I get an exception that Session.UserId is null.

I have two questions:

  1. How can I get access to the claims from within the Application Service?
  2. I assume that there is a better way of achieving Cognito Authentication where, after I've validated the JWT token I probably perform some ABP magic to create an Abp Session such that all Application Services and Controllers can use the AbpSession just like they normally would, oblivious to the fact that the identity or the authentication actually came from AWS Cognito rather than ABP's built-in authentication mechanism. But I have no idea how to achieve this? How should AWS Cognito Authentication be implemented appropriately?

This is my code in my Application Service:

public class HelloWorldAppService : ITSAppServiceBase, IHelloWorldAppService
{
    [Authorize]
    public HelloWorldDto GetAll()
    {
        var temp = this.AbpSession.ToString();
        var id = this.AbpSession.ToUserIdentifier(); //id is null
        //var u = this.GetCurrentUser(); //method throws an exception: Session.UserId is null! Probably, user is not logged in.
        return new HelloWorldDto() { Message = "Hello world!"};
    }
}

This is the content of AbpSession when I set a breakpoint in the method in the Application Service:

This is the extension we have added in our Web.Host project to validate the JWT token:

`	public static IServiceCollection AddAwsCognitoAuthentication(this IServiceCollection services, AppSettings appSettings) {

		services.AddAuthentication(options => {
			options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
			options.DefaultSignInScheme = JwtBearerDefaults.AuthenticationScheme;
			options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
		})
			.AddJwtBearer(options => {
			options.ClaimsIssuer = CognitoIssuer;
			options.SaveToken = true;

			options.TokenValidationParameters = new TokenValidationParameters() {
				ValidateLifetime = true,
				ValidateIssuerSigningKey = true,
				IssuerSigningKeyResolver = (s, securityToken, identifier, parameters) => {
					// get JsonWebKeySet from AWS
					var json = new WebClient().DownloadString(CognitoIssuer + "/.well-known/jwks.json");
					// serialize the result
					var keys = JsonConvert.DeserializeObject<JsonWebKeySet>(json).Keys;
					// cast the result to be the type expected by IssuerSigningKeyResolver
					return keys;
				},
				ValidateIssuer = true,
				ValidIssuer = CognitoIssuer,
				ValidateAudience = false,
			};

				options.Events = new JwtBearerEvents() {
					//Code omitted for brevity
				};
			});

		return services;
	}`

and we are calling this extension in Startup.cs:

services.AddAwsCognitoAuthentication(_appSettings);


5 Answer(s)
  • User Avatar
    0
    ismcagdas created
    Support Team

    Hi @Theunis

    Just a question to understand your use case. Is there a user record in AspNet Zero's username which is authenticated via AWS Cognito ? If not, it will be a problem because AspNet Zero will try to set CreationUserId, LastModifierUserId of several entities.

    Maybe, instead of directly using AWS Cognito to retrieve a token, you can use OpenID Connect to login via AWS Cognito so, a local user will be created on AspNet Zero's database and you will not face such a problem.

  • User Avatar
    0
    Theunis created

    Hi @Theunis

    Just a question to understand your use case. Is there a user record in AspNet Zero's username which is authenticated via AWS Cognito ? If not, it will be a problem because AspNet Zero will try to set CreationUserId, LastModifierUserId of several entities.

    Maybe, instead of directly using AWS Cognito to retrieve a token, you can use OpenID Connect to login via AWS Cognito so, a local user will be created on AspNet Zero's database and you will not face such a problem.

    I agree that we somehow need a user created on AspNet Zero's database as well. (Since permissions on AspNet Zero is also associated to a user in the database.). So let me explain our use cases:

    AWS Cognito will be our sole Authentication Provider. We have two use cases:

    1. Mobile users: users will install a mobile app and register on Cognito. Upon successful registration the mobile device will make an API call to the ASP.NET Zero Web app to register the user on the ASP.NET Zero app (this API call should be authenticated such that it is only allowed with a valid AWS Cognito Access Token). The ASP.NET Zero app will assign the appropriate permissions for a "mobile user" to such a user. These mobile users will always authenticate to all allowed API calls using the validated Cognito Access Token.
    2. Web users on the ASP.NET Zero Web App: users on this application are our employees. An admin user should be able to add and remove users (and set their permissions) on the ASP.NET Zero Web App's User Management screens. Yet, login and authentication on the ASP.NET Zero Web App should happen on Cognito as that is the official Authentication Provider we need to use.
  • User Avatar
    0
    ismcagdas created
    Support Team

    Hi @Theunis

    If you are creating a local AspNet Zero user, then you can create a middleware and validate AWS Cognito Access Token first and then, AspNet Zero will validate its own token after that. So, a custom middleware might solve your problem.

  • User Avatar
    0
    Theunis created

    Hi @ismcagdas

    It is not clear what the solution is you suggest and how to implement it.

    • How/where do we change the admin screens such that when a user gets created on the user management screens, a user also gets created on AWS Cognito? And how do we tie the user we create on Cognito to the user in AspNet Boilerplate such that when we receive a JWT token from Cognito we can identify the user?
    • Are you still suggesting we implement OpenID Connect? If so, do we still retain the JWT authentication extension we already have or does it become redundant? Does the custom middleware you suggest differ from the jwt authentication we already have (for which we've included the code in our question)? If so, how?
    • Does Identity Server play a role here? Is the use of OpenID Connect, JWT token validation and identity server mutually exclusive? Which parts (open id connect, jwt auth, identity server and others) are needed and need to be customised and how do they relate to each other?

    It is really unclear how OpenID connect, JWT middleware, Identity Server and ABP Authentication magic relates to each other and what is involved in the solution you suggest.

  • User Avatar
    0
    Theunis created

    Hi @ismcagdas and Support

    It is now 13 days since we first logged this question and we still don't have any resolution. This is highly concerning. We really need help on this but the turnaround time on communications is really unhelpful. This is a dependency we urgently need to resolve. We cannot wait days for answers. Can you please urgently assist?

    Thank you.