aws / aws-aspnet-cognito-identity-provider

ASP.NET Core Identity Provider for Amazon Cognito
https://aws.amazon.com/developer/language/net/
Apache License 2.0
213 stars 89 forks source link

Role based authentication in AWS cognito #147

Closed junaid-ahmed92 closed 3 years ago

junaid-ahmed92 commented 4 years ago

I am totally new to AWS and I am using aws cognito for authetication and authorization purpose. By following this link https://aws.amazon.com/blogs/aws/new-amazon-cognito-groups-and-fine-grained-role-based-access-control-2/ I created one group and added some users in that group. Now the issue I am facing is how can I validate user roles in my .net core webapi? I can validate token signatures (iss, aud) in the middleware but if I have an api with [Authorize(Roles="Admin")] attribute then how can I validate the role?

Except this I have one other question as well. In the above mentioned document, it's written that we have to create an IAM role and assign the role to that group. So would it be a simple role with no attached policies? I created one IAM web identity role for AWS cognito and assigned that role to an Admin group in cognito and I can see a role claim in my IdToken but I just want to make sure that if it's a right way or not?

klaytaybai commented 4 years ago

It looks like we probably have a bug or at least a confusing UX here. Check out these other issues for more info: https://github.com/aws/aws-aspnet-cognito-identity-provider/issues/86 https://github.com/aws/aws-aspnet-cognito-identity-provider/issues/101

markdwags commented 4 years ago

I'm also running into this issue. Based on the docs here:

https://aws.amazon.com/blogs/developer/now-generally-available-the-asp-net-core-identity-provider-for-amazon-cognito

Based on the guide, you should be able to add [Authorize(Roles = "Admin")] but when I do that, I get errors. Based on https://github.com/aws/aws-aspnet-cognito-identity-provider/issues/86 the solution is to write a custom authorization handler -- is this still the case?

2020-04-13 20:28:38.495 -05:00 [Information] Service=Services Env=VM Context=Microsoft.AspNetCore.Authorization.DefaultAuthorizationService Authorization failed.
2020-04-13 20:28:38.499 -05:00 [Information] Service=Services Env=VM Context=Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker Authorization failed for the request at filter 'Microsoft.AspNetCore.Mvc.Authorization.AuthorizeFilter'.
2020-04-13 20:28:38.508 -05:00 [Information] Service=Services Env=VM Context=Microsoft.AspNetCore.Mvc.ChallengeResult Executing ChallengeResult with authentication schemes ([]).
2020-04-13 20:28:38.537 -05:00 [Information] Service=Services Env=VM Context=Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationHandler AuthenticationScheme: Identity.Application was challenged.
markdwags commented 4 years ago

I've been playing with it more without any success on .NET Core 2.2. Following this guide I have in my appsettings.json

"AWS": {
    "Region": "us-east-2",
    "UserPoolClientId": "xxx",
    "UserPoolClientSecret": "xxx",
    "UserPoolId": "us-east-2_xxxxxx"
  },

Under ConfigureServices, among a few other settings, I have services.AddCognitoIdentity(); and lately I have app.UseAuthentication(); in Configure.

In my API controller, I simply have [Authorize(Roles = "name_of_group")]

I successfully have Cognito setup, and issuing tokens based on a SAML link to ADFS. I can get a token via Postman from Cognito and when I use this token with API gateway behind a mock method linked to that group, it successfully works.

When I use that same token hitting the .NET Core API directly using the setup above, it fails without really any messages of value and then tries to forward the API call to https://localhost:8080/Account/Login?ReturnUrl=/api/test

Ultimately I'm trying to maintain multiple user groups in a Cognito pool, and based on that group, authorize (or not) someone the ability to use a specific API in a controller. It looks simple on the guide above, hopefully I'm just missing something silly.

MikeEllisARB commented 4 years ago

I'm trying to do the same thing and have used the same blog, and I'm seeing the same issue. I noticed that /Account/Login is in the sample code on GitHub so maybe /Account/Login is hardcoded somewhere. I don't even have an /Account/Login in my code.

ashishdhingra commented 4 years ago

Hi @junaid-ahmed92,

I came across the useful article ASP.NET Core with AWS Lambda and Cognito which might be useful for configuring Cognito Group based authorization in an ASP.NET Core application. It has a section Groups and Policies which provides the following details to add a custom policy.

You can use [Authorize] to ensure that only logged-in users can access the Page/Controller/Route. However you’d probably like more fine-grained control, so for that you can add users to Cognito Groups. On the ASP.NET Core app, those groups are sent as part of the user Claims. You can create authorization polices during the Startup.Configure method as follows:

services.AddAuthorization(options =>
{
    options.AddPolicy("AdminOnly", policy =>
    policy.RequireAssertion(context =>
    context.User.HasClaim(c => c.Type == "cognito:groups" && c.Value == "Admin")));
});

This creates a policy requiring the logged in user to be part of the Cognito group Admin. To use this on a Page/Controller/Route, refer to the policy name in the Authorize attribute:

[Authorize(Policy = "AdminOnly")]

Article Now generally available: the ASP.NET Core Identity Provider for Amazon Cognito also provides similar guidance.

Please let me know if this helps.

Thanks, Ashish

MikeEllisARB commented 4 years ago

@ashishdhingra We have many clients that we plan on using one User Pool for in order to enable Single Sign-On. If we use Cognito for authorization groups, then we end up with conflicting group names. We could use AppA_Admin and AppB_Admin, but that just seems too convoluted. Is there a better way to accomplish what we're trying to accomplish? Otherwise, we are thinking we'll just add authorization to each individual app and only use Cognito for Authentication. It would be nice if Cognito had a groups "layer" where we could associate groups to a Client and to a user.

ashishdhingra commented 4 years ago

@ellismichaelarb You can create multiple app clients in a user pool having groups. Not sure how it would fit to your scenario, but please refer the Cognito documentation for more details and give it a try.

ashishdhingra commented 4 years ago

@junaid-ahmed92 Please review the guidance above and confirm if this issue could be closed.

Thanks, Ashish

github-actions[bot] commented 3 years ago

This issue has not recieved a response in 2 weeks. If you want to keep this issue open, please just leave a comment below and auto-close will be canceled.

Renegade13Code commented 1 year ago

I have solved the issue of [Authorize(Roles = "Admin")] not working correctly by creating a custom authorization handler and registered it in the DI container as follows:

public class CognitoGroupAuthorizationHandler: AuthorizationHandler<RolesAuthorizationRequirement>
{
    protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, RolesAuthorizationRequirement requirement)
    {
        if (context.User.HasClaim(c => c.Type == "cognito:groups" && requirement.AllowedRoles.Contains(c.Value)))
        {
            context.Succeed(requirement);
        }
        else
        {
            context.Fail();
        }
        return Task.CompletedTask;
    }
}

And registration:

builder.Services.AddSingleton<IAuthorizationHandler, CognitoGroupAuthorizationHandler>();

Hope this helps :)