Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / Javascript

Calling API Gateway/Cognito from JavaScript

5.00/5 (1 vote)
29 Dec 2019CPOL2 min read 7.8K  
Learn how to call API Gateway with Cognito credentials from a JavaScript client application

Introduction

In this article, we'll look at a simple JavaScript application that can make authenticated HTTP requests to an AWS API Gateway instance using credentials from a Cognito login.

Background

The source code for this sample project can be found on GitHub.

This video on YouTube is a screencast that shows how to deploy this code to AWS with API Gateway, Cognito, Lambda and S3.

Using the Code

We start by importing the Amplify and Auth classes from the AWS Amplify library:

JavaScript
import Amplify, {Auth} from 'aws-amplify';

The Amplify class needs to be configured with the AWS region, Cognitio User Pool ID, and the Cognitio App Client ID. We source these from URL query parameters to make the example generic.

JavaScript
/*
    Configure the Amplify library with the details required to access a Cognito user pool
 */
var urlParams = new URLSearchParams(window.location.search);
Amplify.configure({
    Auth: {
        // Amazon Cognito Region
        region: 'us-east-1',

        // Amazon Cognito User Pool ID
        userPoolId: urlParams.get('poolId'),

        // Amazon Cognito Web Client ID (26-char alphanumeric string)
        userPoolWebClientId: urlParams.get('appId'),
    }
});

This is where we do the initial login. The username and password are sent to Cognito with the Auth.signin() method, and the response will either be success, or requests for additional information.

We capture only the request for a password change here, as the Cognito service forces every user created via the AWS web console into a state where the initial password must be changed. But there are a number of other cases that you may need to respond to including requests for phone numbers, email address, two factor authentication tokens, etc.

Once the login or password change succeeds, we call the displayTokens() function.

JavaScript
/**
 * Attempt the initial login of a Cognito user. This may result in a password change
 * form being displayed if required. Once logged in, the id and access tokens will
 * be displayed.
 * @param username The Cognito username
 * @param password The Cognito password
 */
async function signIn(username, password) {
    const user = await Auth.signIn(username, password);
    if (user.challengeName === 'NEW_PASSWORD_REQUIRED') {

        initiallogin.style.display = "none";
        changepassword.style.display = "";

        changepass.onclick = async () => {
            await Auth.completeNewPassword(
                user,
                newpassword.value
            ).then((user) => {
                displayTokens(user);
            });
        };
    } else {
        displayTokens(user);
    }
}

In the displayTokens() function, we get the user session, from which we can get the ID and access tokens. These tokens are sent in the Authorization header when calling the API Gateway endpoint (passed in via the invokeURL query parameter).

It is important to note here that we do not add the bearer prefix in the header value, even though the HTTP specification says you must do this. It is a known bug in API Gateway Cognito authorizers that this prefix is not supported.

JavaScript
/**
 * Display the id and access tokens from the logged in Cognito user
 * @param user The CognitoUser returned as a result of a successful login
 */
function displayTokens(user) {
    initiallogin.style.display = "none";
    changepassword.style.display = "none";
    results.style.display = "";

    user.getSession((err, session) => {
        accessToken.value = session.accessToken.jwtToken;
        idToken.value = session.idToken.jwtToken;

        fetch(urlParams.get('invokeURL') + "/test", 
             {headers: {'Authorization': session.idToken.jwtToken}})
            .then(response => response.text())
            .then(text => apiCall.value = text);
    });
}

The last step is to hook up an event to the initial login button click.

JavaScript
/*
    Attach an event handler to the login click event
 */
login.onclick = () => signIn(username.value, password.value);

Here is the HTML page that displays the login form:

HTML
<html>
<head>

</head>
<body>
<div id="initiallogin">
    <label>Username</label>
    <input id="username"
           type="text"/>
    <label>Password</label>
    <input id="password"
           type="text"/>
    <input id="login"
           type="button"
           value="Login"/>
</div>
<div id="changepassword"
     style="display: none">
    <label>New Password</label>
    <input id="newpassword"
           type="text"/>
    <input id="changepass"
           type="button"
           value="Change Password"/>
</div>
<div id="results"
     style="display: none">
    <h3>ID Token</h3>
    <textarea cols="100"
              id="idToken"
              readonly="true"
              rows="15"></textarea>
    <h3>Access Token</h3>
    <textarea cols="100"
              id="accessToken"
              readonly="true"
              rows="15"></textarea>
    <h3>API Call Result</h3>
    <textarea cols="100"
              id="apiCall"
              readonly="true"
              rows="15"></textarea>
</div>
<script src="main.js"></script>
</body>
</html>

History

  • 30th December, 2019 - Initial post

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)