DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports
Events Video Library
Over 2 million developers have joined DZone. Join Today! Thanks for visiting DZone today,
Edit Profile Manage Email Subscriptions Moderation Admin Console How to Post to DZone Article Submission Guidelines
View Profile
Sign Out
Refcards
Trend Reports
Events
View Events Video Library
Zones
Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

Integrating PostgreSQL Databases with ANF: Join this workshop to learn how to create a PostgreSQL server using Instaclustr’s managed service

Mobile Database Essentials: Assess data needs, storage requirements, and more when leveraging databases for cloud and edge applications.

Monitoring and Observability for LLMs: Datadog and Google Cloud discuss how to achieve optimal AI model performance.

Automated Testing: The latest on architecture, TDD, and the benefits of AI and low-code tools.

Related

  • The Rise of Biometric Security: Protecting Data in the Future of Cybercrime
  • When To Boost Your Authentication Strategy: Signs for Security Enhancement
  • Spring Authentication With MetaMask
  • Identity and Access Management Best Practices

Trending

  • Modular Software Architecture: Advantages and Disadvantages of Using Monolith, Microservices and Modular Monolith
  • Introduction to ESP32 for Beginners Using the Xedge32 Lua IDE
  • Future Skills in Cybersecurity: Nurturing Talent for the Evolving Threatscape
  • LLMs for Bad Content Detection: Pros and Cons

Authentication and Authorization to Amazon Cognito With Lambdas

Take a look at this tutorial on this difficult-to-find topic.

Michal Letynski user avatar by
Michal Letynski
·
Mar. 07, 19 · Tutorial
Like (12)
Save
Tweet
Share
39.62K Views

Join the DZone community and get the full member experience.

Join For Free

Authentication

In our project, we were using Amazon Cognito for authentication, authorization and user management. It’s very easy to use, basically, you just need to create a user pool, identity pool, and users (everything you can “click” from AWS console).

I will not go into the details, you can read how to do this step by step from official AWS docs.

To authenticate from a web application you simply need to use this code:

    var authenticationData = {
        Username : 'username',
        Password : 'password',
    };
    var authenticationDetails = new AmazonCognitoIdentity.AuthenticationDetails(authenticationData);
    var poolData = { UserPoolId : 'us-east-1_TcoKGbf7n',
        ClientId : '4pe2usejqcdmhi0a25jp4b5sh3'
    };
    var userPool = new AmazonCognitoIdentity.CognitoUserPool(poolData);
    var userData = {
        Username : 'username',
        Pool : userPool
    };
    var cognitoUser = new AmazonCognitoIdentity.CognitoUser(userData);
    cognitoUser.authenticateUser(authenticationDetails, {
        onSuccess: function (result) {
            var accessToken = result.getAccessToken().getJwtToken();

            /* Use the idToken for Logins Map when Federating User Pools with identity pools or when passing through an Authorization Header to an API Gateway Authorizer*/
            var idToken = result.idToken.jwtToken;
        },

        onFailure: function(err) {
            alert(err);
        },

    });


As you can see besides providing a username and password, we also need to create a user pool object, which requires pool ID and client ID.

Everything is straightforward; however, in our case, we had to authenticate to different user pools. Our use case also provides us a pool name but we don't know, between the pool ID and client ID, which one needs to be provided together with username and password. But how do we get them? One of the options would be to provide a hardcoded list of both IDs, but in such case, we would need to do a redeployment of the UI. There is also no JavaScript API method to get the user pool by its name.

However, AWS has Java Cognito SDK which supports all kinds of operations on user pools and users. So why not to try move authentication to lambda?

We created a simple lambda which get 3 parameters (username, password, pool name). Let’s take a look at Cognito API SDK. It is coming from:

<dependency>
    <groupId>com.amazonaws</groupId>
    <artifactId>aws-java-sdk-cognitoidp</artifactId>
  <version>1.11.269</version>  
</dependency>


The interface which we are interested in is called AWSCognitoIdentityProvider.  To create default implementation, type:

AWSCognitoIdentityProvider cognito = AWSCognitoIdentityProviderClientBuilder.defaultClient();


To be able to invoke each API method you need to give your lambda the proper roles and permissions. Each API method is under a different action. You can define them in your SAM or configure it directly from AWS console. 

PolicyDocument:
  Statement:
    -
      Effect: "Allow"
      Action: [
        "cognito-idp:AdminInitiateAuth",
        "cognito-idp:DescribeUserPool",
        "cognito-idp:DescribeUserPoolClient",
        "cognito-idp:ListUserPoolClients",
        "cognito-idp:ListUserPools"
      ]


Now let’s look what this interface provides us. Unfortunately, we cannot get the user pool object by its unique name, only by its ID. But we can get all the user pools with their names so we can find ours and get all necessary data.

List<UserPoolDescriptionType> userPools =
    cognito.listUserPools(new ListUserPoolsRequest().withMaxResults(20)).getUserPools();


 UserPoolDescriptionType has a name, which we compare with our name and ID. With the ID, we can browse for the user pool object which will contain everything which we need for authentication.

ListUserPoolClientsResult response =
    cognito.listUserPoolClients(
        new ListUserPoolClientsRequest()
            .withUserPoolId(userPoolId)
            .withMaxResults(1)
    );

UserPoolClientType userPool =
    cognito.describeUserPoolClient(
        new DescribeUserPoolClientRequest()
            .withUserPoolId(userPoolId)
            .withClientId(
                response.getUserPoolClients().get(0).getClientId()
            )
    ).getUserPoolClient();


We can now authenticate the user. Since we are doing it on the server side, we can use a Non-SRP authentication flow and pass the username and password directly.

try {
    Map<String, String> authParams = new HashMap<>(2);
    authParams.put("USERNAME", loginRequest.getUsername());
    authParams.put("PASSWORD", loginRequest.getPassword());
    AdminInitiateAuthRequest authRequest =
        new AdminInitiateAuthRequest()
            .withClientId(userPool.getClientId())
            .withUserPoolId(userPool.getUserPoolId())
            .withAuthFlow(AuthFlowType.ADMIN_NO_SRP_AUTH)
            .withAuthParameters(authParams);
    AdminInitiateAuthResult result =
        client.adminInitiateAuth(authRequest);
    AuthenticationResultType auth = result.getAuthenticationResult();
} catch (final UserNotFoundException
    | NotAuthorizedException exception) {
    exception.printStackTrace();
}


 AuthenticationResultType contains accessToken , idToken   and refreshToken  so everything what our web client app needs.

Authorization

We also need to change authorization. Our existing implementation of authorization is based on the Cognito default mechanism. Example configuration:

securityDefinitions:
  CognitoUserPool:
    type: "apiKey"
    name: "Authorization"
    in: "header"
    x-amazon-apigateway-authtype: "cognito_user_pools"
    x-amazon-apigateway-authorizer:
      type: "cognito_user_pools"
      providerARNs:
      - !Ref UserPoolARN


Provider:

UserPoolARN:
  Type: String
  Default: 'arn:aws:cognito-idp:us-east-1:782624688943:userpool/us-east-xxx'

As you see here this security definition is connected to a concrete user pool which, in our case, will not work because of authenticating to multiple user pools. So here we need to write a lambda, but this time for authorization.

The authorization lambda is getting two parameters:

  •  authorizationToken which is our JWT accessToken  which is passed in header from our UI client
  •  methodArn it’s Amazon Resource Name of full method which is needed for returning auth policy

The first step is to verify the JWT against public keys which are separate for each user pool. They are here:

https://cognito-idp.us-east-1.amazonaws.com/%s/.well-known/jwks.json

Where "%s" is, the user pool ID should be,  which you can take from processing accessToken  (part of issuer). I will not write here details on how to verify the suck key I used jose4j. For this, you can check examples (using the https jwks endpoint).

After verifying jwt we need a return policy which will tell AWS to allow or deny the request. This is a little bit tricky because the authorization policy needs to have concrete fields, but this is very well-described by Jack Kohn in AWS Labs.

In our case, principalId  is a JWT subject.

Now we only need to configure our lambda in SAM and we are done:

securityDefinitions:
  AuthorizationLambda:
    type: "apiKey"
    name: "Authorization"
    in: "header"
    x-amazon-apigateway-authtype: "custom"
    x-amazon-apigateway-authorizer:
      authorizerCredentials:
        Fn::GetAtt: [ RestApiAuthorizerRole, Arn ]
      authorizerUri:
          Fn::Sub: arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${AuthorizationFunction.Arn}/invocations
      authorizerResultTtlInSeconds: 0
      type: "token"

During the implementation of authentication and authorization via Lambdas, it wasn’t easy to find something about this topic. It’s because our use case was not typical and now when you write a Javascript client you will simply use the js cognito API to do this. I hope this short article/tutorial will be helpful.

authentication

Opinions expressed by DZone contributors are their own.

Related

  • The Rise of Biometric Security: Protecting Data in the Future of Cybercrime
  • When To Boost Your Authentication Strategy: Signs for Security Enhancement
  • Spring Authentication With MetaMask
  • Identity and Access Management Best Practices

Comments

Partner Resources

X

ABOUT US

  • About DZone
  • Send feedback
  • Careers
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends: