4

In an app I am trying to have separate buttons to link to the sign up and sign in pages respectively of an Amazon Cognito User Pools' Hosted UI but so far I can only link to the sign in page.

I am using the AWS Amplify Package from npm and my code may look somehow like the following:

import { Auth } from "aws-amplify";

//...
function openSignIn() {
  Auth.federatedSignIn();
}

function openSignUp() {
  // ???
}

I have found no federatedSignUp() or a function that would accept options regarding it.

The url of the sign up page is:

<domain>/signup?redirect_uri=<redirect_uri>&response_type=<response_typ>&client_id=<client_id>&identity_provider=<identity_provider>&scopes=<scopes>&state=<state>

and while I know all parameters' values I don't know the value of the state param which makes it difficult to use it immediately in an anchor tag even though I don't like this solution.

Is there a proper/elegant solution at all?

vkvkvk
  • 129
  • 1
  • 7

2 Answers2

1

These values are ones you can generate yourself so long as they follow the proper patterns and you can make them available later in the OAuth process. The values required and their format depend on your specific Cognito implementation for that hosted UI / user pool and how you are using it.

Here is some code (pseudo nodejs) to get you started:

    var crypto = require('crypto');

    function getRandomString () {
          const randomItems = new Uint32Array(28);
          var bytes = crypto.randomBytes(28);
          randomItems.set(bytes);
          const binaryStringItems = randomItems.map(dec => `0${dec.toString(16).substr(-2)}`)
          return binaryStringItems.reduce((acc, item) => `${acc}${item}`, '');
    }
    
    var state = getRandomString();
    var code_verifier = getRandomString();
    var code_challenge = crypto.createHash('sha256').update(code_verifier).digest('base64').replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
    
    var redirect_url  = "https://"+domain+".auth."+region+".amazoncognito.com/oauth2/authorize?response_type=code&state="+state+"&client_id="+appClientId+"&redirect_uri="+redirectURI+"&scope=openid&code_challenge_method=S256&code_challenge="+code_challenge;

This assumes a number of things about the Cognito setup and how you are leveraging it (e.g. scope is openid, challenge is S256) but hopefully it can guide you a little. You will need to store some of these things locally before passing them on:

https://betterprogramming.pub/how-to-securely-implement-authentication-in-single-page-applications-670534da746f

Sorry this isn't more Amplify specific.

couchcode
  • 11
  • 1
1

I'm not sure this counts as elegant, but here is a way:

  1. Override the Amplify Auth urlOpener configuration.
  2. Initiate Auth.federatedSignIn()
  3. Modify the captured URL of the form /oauth2/authorize? and replace with /signup?.
  4. Open the URL.
  5. This will initiate Cognito Hosted UI sign-up and then proceed as normal.

Notes:

  1. Details on Cognito Hosted UI URLs are here.
  2. Details on Amplify Auth config here (manual configuration tab) and here.
  3. Note that you can "reconfigure" Amplify auth (Amplify.configure()) as required (multiple times works OK from my observation) if that is required as part of thes flow described above.
  4. The default launchUri implementation just uses window.open() - see here.

More detail on implementation below.

Define your own urlOpener:

const urlOpener = async (url: string, redirectUrl: string): Promise<any> => {
  const signupUrl = url.replace(/\/oauth2\/authorize\?/, '/signup?');
  return launchUri(signupUrl);
};

Configure Amplify:

const config = {
    Auth: {
        ...
        oauth: {
            ...
            urlOpener,
        },
    },
};
Amplify.configure(config);
Daniel
  • 717
  • 6
  • 21
  • This is a good write-up, but this did not work for me. Even if it did, I'm not sure if it would solve the problem, because then both the sign-in and signup would both send to Sign Up. – Braden.Biz Apr 13 '23 at 21:42
  • 1
    I just figured it out. I incorrectly assumed you were working with an older version of @aws-amplify, so I replaced your /\/oauth2\/authorize\?/ with /\/login\?/. Additionally, I did more testing and discovered that you can, in fact, Amplify.configure() a new config right before initiating federatedSignIn(), right as you need to in your onClick handlers. Thanks for a great solution! – Braden.Biz Apr 18 '23 at 18:34
  • 1
    @Braden.Biz Sorry for the slow response, and sorry if that was not clear. Yes, the discovery that you can "reconfigure" Amplify using Amplify.configure() immediately before initiating auth (or other operations). Thanks for the feedback and clarifying. – Daniel Apr 19 '23 at 11:59