68

I have been looking into setting up a login for a web app that lets clients view data hosted in S3 and found that AWS Cognito has a hosted web UI [link] that handles most of the authentication flow for me, the issue I am facing is I cannot find out how to integrate the output of the web UI into my app. Most of the existing documentation in Cognito just references how to use the various APIs in creating your own UI which is leaving me with confusing answers to my issue.

Is there any information that has been created with the Cognito hosted UI in mind?

Amazon says that you can integrate authenticated login with Cognito in minutes but I have been looking at this for a few weeks and cant figure it out.

Fyreye
  • 683
  • 1
  • 5
  • 8
  • Do you have a link to any details regarding the hosted UI? – BryceH Aug 29 '17 at 10:54
  • @BryceH I have been trying to modify the amazon-cognito-auth-js [link](https://github.com/aws/amazon-cognito-auth-js) to allow for the hosted UI to communicate to my app. Unfortunately the only other information I have is the promotional 'we have a new thing, check it out' posts from Amazon about the UI – Fyreye Aug 30 '17 at 14:35
  • Please use more describing question titles. – Vitaly Zdanevich Jun 12 '19 at 12:01

3 Answers3

97

I also struggled with this; I agree that the documentation is a little light.

The link you provided shows what your Cognito UI URL might look like:

https://<your_domain>/login?response_type=code&client_id=<your_app_client_id>&redirect_uri=<your_callback_url>

The idea is that you send your user to this URI, they do their business, and then they get redirected back to you with some sort of token(s) or code. You can check your domain by clicking "Domain name" in the left nav bar.

App Client Settings and OAuth Grant Types

First, check your App client settings. You'll need to whitelist your Callback URL(s) (where Cognito will redirect back to), and make sure at least one OAuth Flow is allowed.

Cognito App client settings

"Authorization code grant" will return an authorization code, which you then send to the oauth2/token endpoint to get an access_token, id_token, and refresh_token. This is a good choice if you have a back-end application and want refresh tokens.

"Implicit grant" is what I'm using in my front-end application. It will return an access token and an id token directly to my front-end app.

To use implicit grant, change response_type=code to response_type=token in your Cognito UI URL.

Implicit Grant Example

So if your redirect after successful authentication looks like this:

https://localhost:3000/#access_token=eyJraWQiOiJG...&id_token=eyJraWQZNg....&token_type=Bearer&expires_in=3600

You just need to peel the id_token off the URL and send it to Cognito, with your User Pool as the key in the Logins map. In Javascript:

AWS.config.credentials = new AWS.CognitoIdentityCredentials({
    IdentityPoolId: 'us-east-1:bxxxxxx6-cxxx-4xxx-8xxx-xxxxxxxxxx3c',
    Logins: {
        'cognito-idp.us-east-1.amazonaws.com/us-east-1_ixxxxxxx': idToken
    }
});

Where idToken is the id token that came back to you on the redirect.

Authorization Code Grant Type

If you use authorization code grant type instead (response_type=code), your back end will need to call the /oauth2/token endpoint to exchange the code for tokens. That call would look something like this:

curl -X POST \
  https://<my-cognito-domain>.auth.us-east-1.amazoncognito.com/oauth2/token \
  -H 'content-type: application/x-www-form-urlencoded' \
  -d 'grant_type=authorization_code&scope=email%20openid%20profile&redirect_uri=https%3A%2F%2Flocalhost%3A3000%2F&client_id=15xxxxxxxxxxxxxx810&code=54826355-b36e-4c8e-897c-d53d37869ee2'

Then you can give this id token to Cognito as above.

UI Notes

My application is popping up the Cognito UI in a new tab when the user clicks a link. When the redirect comes back to my app, I use postMessage() to send the tokens to the parent window, which then closes the new tab. I think this is a relatively common pattern.

I haven't tried it, but I'm guessing rendering the UI into an iframe is disallowed, as a mitigation against click-jacking. Source


I hope this is at least somewhat helpful. Good luck!

Mike Patrick
  • 10,699
  • 1
  • 32
  • 54
  • I couldn't get your authorization code grant example to work in a python Chalice lambda, using python "Requests" library instead of curl to make the POST. I'm sure that the user pool is set up ok etc etc as a request in the other direction using the Warrant lib (which needs the app id) works Unfortunately the info you present here seems to be the best resource currently available anywhere :) It's the first time I've found the docs on an AWS feature to be such a wild goose chase – Vorsprung Dec 10 '17 at 15:31
  • @Vorsprung if you want to post a question containing your python code and any error(s) you're getting, I'd be happy to take a look. – Mike Patrick Dec 11 '17 at 01:42
  • Thanks a lot, very good documentation. But how have you been able to integrate postMessage() in the Cognito UI? – Tobias Dec 11 '17 at 17:19
  • if im correct, this requires to set up an identity pool. could you please provide an example for this case? 1) i sign up using the cognito ui 2) then i log in using the cognito ui 3) then i load the attributes about the user which are stored in the user pool – gabo Mar 06 '18 at 21:43
  • @MikePatrick This is so helpful! In terms of implementing a flow where frontend is making calls to backend that need authorization does it make sense to have the frontend pass Authorization Code and then backend call /oauth2/token endpoint? – Edwin Evans May 14 '18 at 21:11
  • @MikePatrick So if i have a page like www.mypage.com and user sign in is required to access it. How do I actually implement this cognito ui into mypage.com? I am having trouble visualizing this – user2763557 Feb 19 '19 at 21:53
  • It's much easier now with Amplify SDK. If you use Amplify SDK with hosted UI and Implicit Code Grant setup, once Cognito redirects the user to your app after successful authentication it will inject the tokens into your app (i.e. localStorage) which you can then access using the Amplify methods. Amplify will also do the token refresh for you. – Anjan Biswas Mar 18 '19 at 02:35
  • @AnBisw do you know if the localStorage domain is Amazon's, or the application's (the client that reaches out to Cognito)? Thanks. – cellepo May 25 '20 at 02:51
  • You can also pass in a state parameter to the login url that is passed through to the callback URL. &state=tenant_id – MichaelG Feb 07 '22 at 18:12
9

I implemented this flow, not using Amplify, just using Cognito Hosted UI:

  1. User navigates in my website (tab 1), and in any page user clicks the login/register button.
  2. A new tab(Tab 2) is open with the cognito hosted UI using my own domain (auth.example.com)
  3. Then user makes their business on hosted ui (login/new account/recover password,etc)
  4. Cognito send a HASH in the URL (with many tokens) to my site callback.(https://example.com/login )
  5. My site process the tokens: The trick is to create an Auth instance, this one can parse the hash and create the user on the LocalStorage:

    // mysite.com/login 
    import {CognitoAuth} from 'amazon-cognito-auth-js';
    
    // Configuration for Auth instance.
    var authData = {
        UserPoolId: 'us-east-1_xxxx',
        ClientId: '1vxxxxx',
        RedirectUriSignIn : 'https://example.com/login',
        RedirectUriSignOut : 'https://example.com/logout',
        AppWebDomain : 'example.com',
        TokenScopesArray: ['email']
        };
    var auth = new CognitoAuth(authData);
    
    //Callbacks, you must declare, but can be empty. 
    auth.userhandler = {
        onSuccess: function(result) {
    
        },
        onFailure: function(err) {
        }
    };
    
    //Get the full url with the hash data.
    var curUrl = window.location.href;
    
    
    //here is the trick, this step configure the LocalStorage with the user.
    auth.parseCognitoWebResponse(curUrl);
    window.top.close();
    
  6. After the user has been set in the Local Storage, the callback(tab 2) is closed.

  7. On my site (tab 1) I configure an EventListener to listen if there is a change in the Local Storage.

          constructor() {
          window.addEventListener('storage', this.userLogged);
          }
    
          userLogged(event) {
    
            if (event.key.indexOf('CognitoIdentityServiceProvider') !== -1) {
    
              var data = {
                          UserPoolId: 'us-east-1_xxxxx',
                          ClientId: 'xxxxx'
                          };
    
             var userPool = new CognitoUserPool(data);
    
             //behind the scene getCurrentUser looks for the user on the local storage. 
             var cognitoUser = userPool.getCurrentUser();
                }
           }
    
  8. With cognitoUser you are done, because you can retrieve credentials or other data.
Cristian Sepulveda
  • 1,572
  • 1
  • 18
  • 25
  • Do you know if the localStorage domain is Amazon's, or the application's (the client that reaches out to Cognito)? Thanks. – cellepo May 25 '20 at 02:52
  • 1
    the local storage is the domain where the customer is... so you have to configure your own domain an do not use the aws domains – Cristian Sepulveda May 25 '20 at 15:04
  • OK thanks! Was asking since this is for the Cognito _hosted UI_, so I wasn't sure for that if Amazon manages the localStorage for that under their own domain [as a 3rd-party storage] on the client (as I think might be the case for using Cognito with e.g: Google authentication, where I think there would be Google-3rd-party localStorage). – cellepo May 27 '20 at 00:39
  • 1
    This approach sends the user to cognito UI in tab 2, there the user makes the login process, if it is using google cognito display google login page, etc... and after the user completes all the process, Cognito UI sends to you a callback with a token that you must process, in that processing the data is stored in the localStorage of your domain and is available for using it in tab 1. – Cristian Sepulveda May 27 '20 at 12:44
  • 1
    The amazon-cognito-auth-js library is no longer maintained. It's been pulled into the monster that is AWS Amplify. – Adam Fanello Oct 10 '22 at 19:05
2

That use case, putting Cognito in front of S3, using the hosted UI, is covered in this AWS blog: https://aws.amazon.com/blogs/networking-and-content-delivery/authorizationedge-using-cookies-protect-your-amazon-cloudfront-content-from-being-downloaded-by-unauthenticated-users/

The blog contains sample code you can steal from. And you can deploy the sample solution easily using the serverless application repository.

Disclaimer: I authored that blog. Hope it can be of use to you!

Otto
  • 1,787
  • 1
  • 17
  • 25