React Cognito Token

How can we obtain a Cognito token during the first factor of authentication?

These code snippets demonstrate how the React application obtains a Cognito JWT that can then be used to request and claim a Rosetta JWT. The AWS Amplify user interface and library components allow us to obtain and get access to the Cognito JWT and the information within it. And the Redux integration allows us to organize the authentication related events and behavior outside of the user interface components themselves.

--

src/index.js

--

import React from 'react';
import { render } from 'react-dom';

import { Provider } from 'react-redux';

import './index.css';
import App from './App';
import store from './redux/store';
import * as serviceWorker from './serviceWorker';

render(
  <Provider store={store}> 
    <App /> 
  </Provider>,
  document.getElementById('root')
);

// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: https://bit.ly/CRA-PWA
serviceWorker.unregister();

--

src/App.js

--

import React, { useEffect } from 'react';

import { connect } from 'react-redux'
import {
  overallAppAmplifySignIn,
  overallAppAmplifySignOut,
  overallAppInitialize
} from './redux/actions'

import Amplify from "aws-amplify";
import { Hub, Logger } from 'aws-amplify';
import {
  AmplifyAuthenticator,
  AmplifyForgotPassword,
  AmplifySignIn,
  AmplifySignOut,
  AmplifySignUp,
} from "@aws-amplify/ui-react";

import './App.css';
import awsconfig from "./config/aws-config";
import RosettaMain from './component/RosettaMain.js';

Amplify.configure(awsconfig);

function App(props) {
  // https://aws-amplify.github.io/docs/js/hub#listening-authentication-events
  const logger = new Logger('Amplify-Logger');
  const listener = (data) => {
      switch (data.payload.event) {
          case 'signIn':
              props.overallAppAmplifySignIn();
              break;
          case 'signOut':
              props.overallAppAmplifySignOut();
              break;
          case 'configured':
              logger.info('Amplify Auth module is now configured');
              break;
          default:
              logger.info('Unhandled event: ' + data.payload.event);
              break;
      }
  }

  useEffect(() => {
    props.overallAppInitialize();
    Hub.listen('auth', listener);
  });  
  
  return (
    <AmplifyAuthenticator usernameAlias="email" >
      <AmplifyForgotPassword usernameAlias="email" headerText="Reset your Rosetta Salt password" slot="forgot-password" />
      <AmplifySignIn usernameAlias="email" headerText="Sign in to your Rosetta Salt account" slot="sign-in" />
      <AmplifySignUp usernameAlias="email" headerText="Create a new Rosetta Salt account" slot="sign-up" />
      
      <div className="App">
        <RosettaMain />
        <AmplifySignOut />        
      </div>
    </AmplifyAuthenticator>
  );
}

const mapStateToProps = (state /*, ownProps*/) => {
  return {
  }
}

const mapDispatchToProps = {
  overallAppAmplifySignIn,
  overallAppAmplifySignOut,
  overallAppInitialize
}

export default connect(
  mapStateToProps,
  mapDispatchToProps
)(App)

--

src/redux/actions.js

--

import {
  OVERALL_APP_GET_USER_ERROR,
  OVERALL_APP_GET_USER_STARTED,
  OVERALL_APP_GET_USER_SUCCESS,
  OVERALL_APP_RESET_USER
} from './actionTypes'

import { Auth } from 'aws-amplify';

export function overallAppInitialize() {
  return (dispatch, getState) => {
    dispatch(overallAppGetUserStarted());
    Auth.currentAuthenticatedUser( { bypassCache: false } )
    .then(user => { dispatch(overallAppGetUserSuccess(user)) })
    .catch(err => { dispatch(overallAppGetUserError(err.response)) });
  }
}

export function overallAppAmplifySignIn() {
  return (dispatch, getState) => {
    dispatch(overallAppGetUserStarted());
    Auth.currentAuthenticatedUser( { bypassCache: false } )
    .then(user => { dispatch(overallAppGetUserSuccess(user)) })
    .catch(err => { dispatch(overallAppGetUserError(err.response)) });
  }
}

export function overallAppAmplifySignOut() {
  return { type: OVERALL_APP_RESET_USER }
}

export function overallAppGetUserError(errorResponse) {
  return { type: OVERALL_APP_GET_USER_ERROR, errorResponse }
}

export function overallAppGetUserStarted() {
  return { type: OVERALL_APP_GET_USER_STARTED }
}

export function overallAppGetUserSuccess(appAmplifyUser) {
  return { type: OVERALL_APP_GET_USER_SUCCESS, appAmplifyUser }
}

--

src/redux/reducers.js

--

import {
  OVERALL_APP_GET_USER_ERROR,
  OVERALL_APP_GET_USER_STARTED,
  OVERALL_APP_GET_USER_SUCCESS,
  OVERALL_APP_RESET_USER
} from './actionTypes'

import { commonGetErrorMessage } from './reducers-common'

const overallAppInitialState = {
  appAmplifyUser: null,
  appAmplifyUserError: '',
  appAmplifyUserIdJwtToken: '',
  appAmplifyUserIdJwtEmail: '',
  appVersionValue: '1.0.16',
}

export function overallAppReducer(state = overallAppInitialState, action) {
  let errorMessage = "";
  switch (action.type) {
    case OVERALL_APP_GET_USER_ERROR:
      errorMessage = commonGetErrorMessage(action.errorResponse);
      return { 
        ...state, 
        appAmplifyUserError: errorMessage
      }
    case OVERALL_APP_GET_USER_STARTED:
      return { 
        ...state, 
        appAmplifyUser: null,
        appAmplifyUserError: '',
        appAmplifyUserIdJwtToken: '',
        appAmplifyUserIdJwtEmail: '',
      }
    case OVERALL_APP_GET_USER_SUCCESS:
      let idJwtToken = action.appAmplifyUser.signInUserSession.idToken.jwtToken;
      let idJwtEmail = action.appAmplifyUser.attributes.email;
      return { 
        ...state, 
        appAmplifyUser: action.appAmplifyUser,
        appAmplifyUserIdJwtToken: idJwtToken,
        appAmplifyUserIdJwtEmail: idJwtEmail,
      }
    case OVERALL_APP_RESET_USER:
      return { 
        ...state, 
        appAmplifyUser: null,
        appAmplifyUserError: '',
        appAmplifyUserIdJwtToken: '',
        appAmplifyUserIdJwtEmail: '',
      }
    default:
      return state
  }
}

--