React Native Cognito Token

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

These code snippets demonstrate how the React Native 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.

--

App.js

--

import 'react-native-gesture-handler';
import React from 'react';

import { Provider } from 'react-redux';

import RosettaEntry from './screens/RosettaEntry';
import store from './redux/store';
import useCachedResources from './hooks/useCachedResources';

export default function App(props) {
  // guard clause - loading failed
  const isLoadingComplete = useCachedResources();
  if (!isLoadingComplete) {
    return null;
  }

  // otherwise, return entry
  return (
    <Provider store={store}>
      <RosettaEntry />
    </Provider>
  );
}

--

screens/RosettaEntry.js

--

import React from 'react';

import {
  Authenticator,
  ConfirmSignIn,
  ConfirmSignUp,
  ForgotPassword,
  RequireNewPassword,
  SignIn,
  SignUp,
} from 'aws-amplify-react-native';

import AmplifyAuthComponent from '../components/AmplifyAuthComponent';
import AmplifyAuthLoading from '../components/AmplifyAuthLoading';

function RosettaEntry(props) {
  return (
    <Authenticator 
      hideDefault={true}
      usernameAttributes="email"
    >
      <ConfirmSignIn />
      <ConfirmSignUp />
      <ForgotPassword />
      <RequireNewPassword />
      <SignIn />
      <SignUp />

      <AmplifyAuthComponent />
    </Authenticator>
  );
}

export default RosettaEntry;

--

components/AmplifyAuthComponent.js

--

import React from 'react';
import { View } from 'react-native'

import { connect } from 'react-redux'

import AmplifyAuthLoading from './AmplifyAuthLoading'
import { overallAppAmplifySignIn } from '../redux/actions-overallApp'
import { serverTokenInitializeState } from '../redux/actions-serverToken'
import { sourcePasswordInitializeState } from '../redux/actions-sourcePassword'
import RosettaHome from '../screens/RosettaHome'

//
// assumption is that this component is being wrapped by a parent component and that
// it is only responsible for extacting the user information and signing them in,
// once the signedIn state is reached
//
// do NOT use this component directly
//
function AmplifyAuthComponent(props) {
  //
  // from https://docs.amplify.aws/ui/auth/authenticator/q/framework/react-native#customize-your-own-components
  // (Show your app after sign-in section of page)
  //
  let authState = props.authState;
  if (authState === 'signedIn') {
    let user = props.authData;
    if (user && user.attributes && user.attributes.email) {
      let userEmail = user.attributes.email;
      console.log("Amplify authenticated user email: " + userEmail);
    }
    
    console.log("Start initializing application...");
    props.overallAppAmplifySignIn();
    props.serverTokenInitializeState();
    props.sourcePasswordInitializeState();
    
    console.log("Start loading home component...");
    return <RosettaHome />;
  } else if (authState === 'loading') {
    return <AmplifyAuthLoading />;
  } else {
    return null;
  }
}

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

const mapDispatchToProps = {
  overallAppAmplifySignIn,
  serverTokenInitializeState,
  sourcePasswordInitializeState,
}

export default connect(
  mapStateToProps,
  mapDispatchToProps
)(AmplifyAuthComponent)

--

redux/actions-overallApp.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 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 }
}

--

redux/reducers-overallApp.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: '0.0.1',
}

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
  }
}