amplify-js: Amplify missing the ability to programmatically create a user session using Cognito (id, access, refresh) tokens

Before opening, please confirm:

JavaScript Framework

React

Amplify APIs

Authentication

Amplify Categories

auth

Environment information

# Put output below this line
System:
    OS: macOS 10.15.7
    CPU: (12) x64 Intel(R) Core(TM) i7-8850H CPU @ 2.60GHz
    Memory: 56.16 MB / 16.00 GB
    Shell: 3.2.57 - /bin/bash
  Binaries:
    Node: 14.17.0 - /usr/local/opt/node@14/bin/node
    npm: 6.14.13 - /usr/local/opt/node@14/bin/npm
  Browsers:
    Chrome: 91.0.4472.164
    Firefox: 86.0
    Safari: 14.0.3
    Safari Technology Preview: 14.2
  npmPackages:
    @aws-amplify/ui-react: ^1.2.7 => 1.2.7 
    @babel/core: ^7.14.8 => 7.14.8 (7.12.3)
    @babel/plugin-proposal-class-properties: ^7.14.5 => 7.14.5 (7.12.1)
    @babel/plugin-proposal-decorators: ^7.14.5 => 7.14.5 (7.12.1)
    @babel/plugin-proposal-do-expressions: ^7.14.5 => 7.14.5 
    @babel/plugin-proposal-export-default-from: ^7.14.5 => 7.14.5 
    @babel/plugin-proposal-export-namespace-from: ^7.14.5 => 7.14.5 
    @babel/plugin-proposal-function-sent: ^7.14.5 => 7.14.5 
    @babel/plugin-proposal-json-strings: ^7.14.5 => 7.14.5 
    @babel/plugin-proposal-logical-assignment-operators: ^7.14.5 => 7.14.5 
    @babel/plugin-proposal-nullish-coalescing-operator: ^7.14.5 => 7.14.5 (7.12.1)
    @babel/plugin-proposal-numeric-separator: ^7.14.5 => 7.14.5 (7.12.1)
    @babel/plugin-proposal-optional-chaining: ^7.14.5 => 7.14.5 (7.12.1)
    @babel/plugin-proposal-pipeline-operator: ^7.14.8 => 7.14.8 
    @babel/plugin-proposal-throw-expressions: ^7.14.5 => 7.14.5 
    @babel/plugin-syntax-dynamic-import: ^7.8.3 => 7.8.3 
    @babel/plugin-syntax-import-meta: ^7.10.4 => 7.10.4 
    @babel/plugin-transform-runtime: ^7.14.5 => 7.14.5 (7.12.1)
    @babel/preset-env: ^7.14.8 => 7.14.8 (7.12.1)
    @babel/preset-react: ^7.14.5 => 7.14.5 (7.12.1)
    @babel/register: ^7.14.5 => 7.14.5 
    @fortawesome/fontawesome-svg-core: ^1.2.35 => 1.2.35 
    @fortawesome/free-brands-svg-icons: ^5.15.3 => 5.15.3 
    @fortawesome/free-regular-svg-icons: ^5.15.3 => 5.15.3 
    @fortawesome/free-solid-svg-icons: ^5.15.3 => 5.15.3 
    @fortawesome/pro-light-svg-icons: ^5.15.3 => 5.15.3 
    @fortawesome/pro-regular-svg-icons: ^5.15.3 => 5.15.3 
    @fortawesome/pro-solid-svg-icons: ^5.15.3 => 5.15.3 
    @fortawesome/react-fontawesome: ^0.1.14 => 0.1.14 
    @testing-library/jest-dom: ^5.14.1 => 5.14.1 
    @testing-library/react: ^12.0.0 => 12.0.0 
    @testing-library/user-event: ^13.2.0 => 13.2.0 
    @vue/preload-webpack-plugin: ^2.0.0 => 2.0.0 
    amazon-cognito-identity-js: ^5.0.6 => 5.0.6 
    aws-amplify: ^4.2.1 => 4.2.1 
    axios: ^0.21.1 => 0.21.1 
    babel-loader: ^8.2.2 => 8.2.2 (8.1.0)
    bootstrap: ^5.0.2 => 5.0.2 
    bundle-stats-webpack-plugin: ^3.0.1 => 3.0.1 
    clean-webpack-plugin: ^4.0.0-alpha.0 => 4.0.0-alpha.0 
    compression-webpack-plugin: ^8.0.1 => 8.0.1 
    core-js: ^3.15.2 => 3.15.2 (2.6.12, 1.2.7)
    css-loader: ^5.2.7 => 5.2.7 (4.3.0)
    css-minimizer-webpack-plugin: ^3.0.2 => 3.0.2 
    eslint: ^7.31.0 => 7.31.0 
    eslint-config-airbnb: ^18.2.1 => 18.2.1 
    eslint-loader: ^4.0.2 => 4.0.2 
    eslint-plugin-import: ^2.23.4 => 2.23.4 
    eslint-plugin-jsx-a11y: ^6.4.1 => 6.4.1 
    eslint-plugin-react: ^7.24.0 => 7.24.0 
    file-loader: ^6.2.0 => 6.2.0 (6.1.1)
    google-map-react: ^2.1.10 => 2.1.10 
    html-webpack-plugin: ^5.3.2 => 5.3.2 (4.5.0)
    image-minimizer-webpack-plugin: ^2.2.0 => 2.2.0 
    imagemin-gifsicle: ^7.0.0 => 7.0.0 
    imagemin-jpegtran: ^7.0.0 => 7.0.0 
    imagemin-mozjpeg: ^9.0.0 => 9.0.0 
    imagemin-optipng: ^8.0.0 => 8.0.0 
    imagemin-svgo: ^9.0.0 => 9.0.0 
    lodash-webpack-plugin: ^0.11.6 => 0.11.6 
    lodash.isequal: ^4.5.0 => 4.5.0 
    mini-css-extract-plugin: ^1.6.2 => 1.6.2 (0.11.3)
    node-sass: ^6.0.1 => 6.0.1 
    normalizr: ^3.6.1 => 3.6.1 
    postcss: ^8.3.6 => 8.3.6 (7.0.36)
    react: ^17.0.2 => 17.0.2 (16.14.0, 0.14.10)
    react-bootstrap: ^1.6.1 => 1.6.1 
    react-bootstrap/AbstractNav:  undefined ()
    react-bootstrap/AbstractNavItem:  undefined ()
    react-bootstrap/Accordion:  undefined ()
    react-bootstrap/AccordionCollapse:  undefined ()
    react-bootstrap/AccordionContext:  undefined ()
    react-bootstrap/AccordionToggle:  undefined ()
    react-bootstrap/Alert:  undefined ()
    react-bootstrap/Badge:  undefined ()
    react-bootstrap/BootstrapModalManager:  undefined ()
    react-bootstrap/Breadcrumb:  undefined ()
    react-bootstrap/BreadcrumbItem:  undefined ()
    react-bootstrap/Button:  undefined ()
    react-bootstrap/ButtonGroup:  undefined ()
    react-bootstrap/ButtonToolbar:  undefined ()
    react-bootstrap/Card:  undefined ()
    react-bootstrap/CardColumns:  undefined ()
    react-bootstrap/CardContext:  undefined ()
    react-bootstrap/CardDeck:  undefined ()
    react-bootstrap/CardGroup:  undefined ()
    react-bootstrap/CardImg:  undefined ()
    react-bootstrap/Carousel:  undefined ()
    react-bootstrap/CarouselCaption:  undefined ()
    react-bootstrap/CarouselItem:  undefined ()
    react-bootstrap/CloseButton:  undefined ()
    react-bootstrap/Col:  undefined ()
    react-bootstrap/Collapse:  undefined ()
    react-bootstrap/Container:  undefined ()
    react-bootstrap/Dropdown:  undefined ()
    react-bootstrap/DropdownButton:  undefined ()
    react-bootstrap/DropdownItem:  undefined ()
    react-bootstrap/DropdownMenu:  undefined ()
    react-bootstrap/DropdownToggle:  undefined ()
    react-bootstrap/ElementChildren:  undefined ()
    react-bootstrap/Fade:  undefined ()
    react-bootstrap/Feedback:  undefined ()
    react-bootstrap/Figure:  undefined ()
    react-bootstrap/FigureCaption:  undefined ()
    react-bootstrap/FigureImage:  undefined ()
    react-bootstrap/Form:  undefined ()
    react-bootstrap/FormCheck:  undefined ()
    react-bootstrap/FormCheckInput:  undefined ()
    react-bootstrap/FormCheckLabel:  undefined ()
    react-bootstrap/FormContext:  undefined ()
    react-bootstrap/FormControl:  undefined ()
    react-bootstrap/FormFile:  undefined ()
    react-bootstrap/FormFileInput:  undefined ()
    react-bootstrap/FormFileLabel:  undefined ()
    react-bootstrap/FormGroup:  undefined ()
    react-bootstrap/FormLabel:  undefined ()
    react-bootstrap/FormText:  undefined ()
    react-bootstrap/Image:  undefined ()
    react-bootstrap/InputGroup:  undefined ()
    react-bootstrap/Jumbotron:  undefined ()
    react-bootstrap/ListGroup:  undefined ()
    react-bootstrap/ListGroupItem:  undefined ()
    react-bootstrap/Media:  undefined ()
    react-bootstrap/Modal:  undefined ()
    react-bootstrap/ModalBody:  undefined ()
    react-bootstrap/ModalContext:  undefined ()
    react-bootstrap/ModalDialog:  undefined ()
    react-bootstrap/ModalFooter:  undefined ()
    react-bootstrap/ModalHeader:  undefined ()
    react-bootstrap/ModalTitle:  undefined ()
    react-bootstrap/Nav:  undefined ()
    react-bootstrap/NavContext:  undefined ()
    react-bootstrap/NavDropdown:  undefined ()
    react-bootstrap/NavItem:  undefined ()
    react-bootstrap/NavLink:  undefined ()
    react-bootstrap/Navbar:  undefined ()
    react-bootstrap/NavbarBrand:  undefined ()
    react-bootstrap/NavbarCollapse:  undefined ()
    react-bootstrap/NavbarContext:  undefined ()
    react-bootstrap/NavbarToggle:  undefined ()
    react-bootstrap/Overlay:  undefined ()
    react-bootstrap/OverlayTrigger:  undefined ()
    react-bootstrap/PageItem:  undefined ()
    react-bootstrap/Pagination:  undefined ()
    react-bootstrap/Popover:  undefined ()
    react-bootstrap/PopoverContent:  undefined ()
    react-bootstrap/PopoverTitle:  undefined ()
    react-bootstrap/ProgressBar:  undefined ()
    react-bootstrap/ResponsiveEmbed:  undefined ()
    react-bootstrap/Row:  undefined ()
    react-bootstrap/SafeAnchor:  undefined ()
    react-bootstrap/SelectableContext:  undefined ()
    react-bootstrap/Spinner:  undefined ()
    react-bootstrap/SplitButton:  undefined ()
    react-bootstrap/Switch:  undefined ()
    react-bootstrap/Tab:  undefined ()
    react-bootstrap/TabContainer:  undefined ()
    react-bootstrap/TabContent:  undefined ()
    react-bootstrap/TabContext:  undefined ()
    react-bootstrap/TabPane:  undefined ()
    react-bootstrap/Table:  undefined ()
    react-bootstrap/Tabs:  undefined ()
    react-bootstrap/ThemeProvider:  undefined ()
    react-bootstrap/Toast:  undefined ()
    react-bootstrap/ToastBody:  undefined ()
    react-bootstrap/ToastContext:  undefined ()
    react-bootstrap/ToastHeader:  undefined ()
    react-bootstrap/ToggleButton:  undefined ()
    react-bootstrap/ToggleButtonGroup:  undefined ()
    react-bootstrap/Tooltip:  undefined ()
    react-bootstrap/createChainedFunction:  undefined ()
    react-bootstrap/createWithBsPrefix:  undefined ()
    react-bootstrap/divWithClassName:  undefined ()
    react-bootstrap/helpers:  undefined ()
    react-bootstrap/transitionEndListener:  undefined ()
    react-bootstrap/triggerBrowserReflow:  undefined ()
    react-bootstrap/types:  undefined ()
    react-bootstrap/usePopperMarginModifiers:  undefined ()
    react-bootstrap/useWrappedRefWithWarning:  undefined ()
    react-datepicker: ^4.1.1 => 4.1.1 
    react-dom: ^17.0.2 => 17.0.2 (16.14.0, 0.14.10)
    react-ga: ^3.3.0 => 3.3.0 
    react-image-lightbox: ^5.1.4 => 5.1.4 
    react-redux: ^7.2.4 => 7.2.4 
    react-router-dom: ^5.2.0 => 5.2.0 
    react-router-hash-link: ^2.4.3 => 2.4.3 
    react-scripts: 4.0.3 => 4.0.3 
    react-star-rating-component: ^1.4.1 => 1.4.1 
    react-tooltip: ^4.2.21 => 4.2.21 
    redux: ^4.1.0 => 4.1.0 
    redux-actions: ^2.6.5 => 2.6.5 
    redux-devtools: ^3.7.0 => 3.7.0 
    redux-devtools-extension: ^2.13.9 => 2.13.9 
    redux-promise-middleware: ^6.1.2 => 6.1.2 
    redux-promise-middleware-actions: ^3.1.0 => 3.1.0 
    redux-thunk: ^2.3.0 => 2.3.0 
    remote-redux-devtools: ^0.5.16 => 0.5.16 
    sass-loader: ^12.1.0 => 12.1.0 (10.2.0)
    script-ext-html-webpack-plugin: ^2.1.5 => 2.1.5 
    source-sans-pro: ^3.6.0 => 3.6.0 
    style-loader: ^3.2.1 => 3.2.1 (1.3.0)
    svgo: ^2.3.1 => 2.3.1 (1.3.2)
    terser-webpack-plugin: ^5.1.4 => 5.1.4 (4.2.3, 1.4.5)
    url-loader: ^4.1.1 => 4.1.1 
    uuid: ^8.3.2 => 8.3.2 (3.4.0, 3.3.2, 7.0.3, 3.2.1)
    webpack: ^5.46.0 => 5.46.0 (4.44.2)
    webpack-bundle-analyzer: ^4.4.2 => 4.4.2 
    webpack-cli: ^4.7.2 => 4.7.2 
    webpack-dev-server: ^3.11.2 => 3.11.2 (3.11.1)
    webpack-merge: ^5.8.0 => 5.8.0 
    webpack-visualizer-plugin: ^0.1.11 => 0.1.11 
  npmGlobalPackages:
    @aws-amplify/cli: 0.1.41
    babel-upgrade: 0.0.23
    cordova-check-plugins: 4.0.5
    cordova: 9.0.0
    eslint-config-standard-react: 11.0.1
    eslint-config-standard: 16.0.2
    eslint: 7.29.0
    ios-deploy: 1.10.0
    less: 3.9.0
    npm-check-updates: 11.7.1
    npm: 7.13.0
    phonegap: 8.0.0
    svgo: 2.3.0
    uglify-js: 3.6.0
    webpack-cli: 3.3.12
    webpack-dev-server: 3.11.2
    webpack: 5.37.1

Describe the bug

I’m working on a straight react js mobile application using cordova, and want to use Amplify with Social Sign On. I can’t reliably use deep links, and need to handle the authentication flow within the app itself. I’m able to get to the point where I receive the authorization code grant URL from Cognito. But there doesn’t appear to be a way in Amplify to create the user session using the tokens provided by Cognito.

This functionality seems to have once existed in amazon-cognito-auth-js with the parseCognitoWebResponse() method. However, there is only one reference to CognitoAuth client in Auth.ts on line 209. It does not seem that you can retrieve a CognitoAuth client from the Auth object. So it would seem this capability is no longer supported anymore.

Amplify’s Auth._oAuthHandler.handleAuthResponse() function does parse a Cognito authorization code grant url against the oauth2/token endpoint, and returns the idtoken, refreshtoken and accesstoken, but the handleAuthResponse function does not store these tokens or create a Cognito User Session. There does not appear to be any way to create a User Session, using these tokens, via Amplify, the amazon-cognito-identity-js library, or the AWS JS SDK v3 (Cognito Identity Provider).

This issue was acknowledged by the Amplify team in a comment by @powerful23 in #825 — there should be a way to create a session apart from having to use the urlListener, and worst case, provide tokens to Amplify to create a user session. The Amplify team coded a rudimentary capability, but it was never released out of beta. #825 seems to have been closed without any resolution.

Based on comments in #825, it may be possible to trick Amplify by pushing these tokens into storage, but that’s super hacky.

Am I missing something?

Expected behavior

I would expect the Amplify Authorization library to offer some mechanism to both (1) parse the authorization code grant response from Cognito, and (2) provide a way of creating a user session with the provided tokens without the reliance on urlListener. Just having the first by itself is not helpful. The lack of being able to create a user session greatly constrains Amplify’s usefulness as soon as you go offroad.

Reproduction steps

Try to create a Cognito user session without urlListener, or using Auth._oAuthHandler.handleAuthResponse(). It doesn’t appear to be possible.

Code Snippet

// Put your code below this line.

Log output

// Put your logs below this line


aws-exports.js

No response

Manual configuration

No response

Additional configuration

No response

Mobile Device

No response

Mobile Operating System

No response

Mobile Browser

No response

Mobile Browser Version

No response

Additional information and screenshots

No response

About this issue

  • Original URL
  • State: closed
  • Created 3 years ago
  • Reactions: 2
  • Comments: 18

Most upvoted comments

Continuing to work with this --it seems like you can get Amplify to pick up the session if you cache it in localstorage as described in #825 (very convoluted). Been working all morning to find a cleaner way to do this using either Amplify or the amazon-cognito-identity-js package; the best I’ve come up with so far (which relies on the code in my last post) is this:


    // create an object with the UserPoolId and ClientId
    var poolData = {
        UserPoolId: <<UserPoolId>>,
        ClientId: <<ClientId>>,
    };
     
    // pass the poolData object to CognitoUserPool
    var userPool = new AmazonCognitoIdentity.CognitoUserPool(
         poolData
    );
    
    // create an object containing the username and user pool.  You can get the username from the accessToken 
    // using the AmazonCognitoIdentity.CognitoAccessToken class (from previous post)
    var userData = {
        Username: AccessToken.payload.username,
        Pool: userPool,
    };
    
    // create a cognito user using the userData object
    var cognitoUser = new AmazonCognitoIdentity.CognitoUser(
        userData
    );
    
    // set the cognito user session w/ the session created in my previous post from
    // calling the AmazonCognitoIdentity.CognitoUserSession() class with the Cognito tokens
    cognitoUser.setSignInUserSession(session);

Now, Amplify will return the authenticated user correctly. Is there a cleaner/simpler way of doing this? If Amplify/Auth are already configured, and you have the CognitoUserSession separately, it seems as though there should be a single method that just does the above for you – rather than making the developer have to spend hours upon hours figuring all this out.

I documented my approach in a medium blog. https://medium.com/codex/how-to-process-an-aws-cognito-authorization-code-grant-using-aws-amplify-b49d9ee052ca

Not 100% sure why you’re getting ‘undefined’; I know typescript takes issue with the fact that _oAuthHandler is private. There are ways to bypass this I’ve read and access the method, but it’s not recommended. I submitted a feature request to make this method public (linked earlier in this issue).

Thank you very much @jglesner for your implementation it saved me a lot of time. I updated the code to a TypeScript function for my purpose. Maybe someone will find it useful:

import * as AmazonCognitoIdentity from "amazon-cognito-identity-js";
import type {CognitoUser} from "amazon-cognito-identity-js";

export const authorizeByToken = async (
authData: {
  idToken: string, 
  accessToken: string, 
  refreshToken: string,
},
userConfig: {
  userPoolId: string,
  clientId: string
}
): Promise<CognitoUser | any> => {

  // create a CognitoAccessToken using the response accessToken
  const AccessToken = new
  AmazonCognitoIdentity.CognitoAccessToken({
    AccessToken: authData.accessToken,
  });

  // create a CognitoIdToken using the response idToken   
  const IdToken = new AmazonCognitoIdentity.CognitoIdToken({
    IdToken: authData.idToken,
  });

  // create a RefreshToken using the response refreshToken 
  const RefreshToken = new
  AmazonCognitoIdentity.CognitoRefreshToken({
    RefreshToken: authData.refreshToken,
  });

  // create a session object with all the tokens
  const sessionData = {
    IdToken: IdToken,
    AccessToken: AccessToken,
    RefreshToken: RefreshToken,
  };

  // create the CognitoUserSession using the sessionData
  const session = new AmazonCognitoIdentity.CognitoUserSession(
    sessionData
  );

  // create an object with the UserPoolId and ClientId 
  let poolData = {
    UserPoolId: userConfig.userPoolId,
    ClientId: userConfig.clientId,
  };

  // pass the poolData object to CognitoUserPool 
  let userPool = new AmazonCognitoIdentity.CognitoUserPool(
    poolData
  );

  // create an object containing the username and user pool. 
  // You can get the username from CognitoAccessToken object 
  // we created above.
  let userData = {
    Username: AccessToken.payload.username,
    Pool: userPool,
  };

  // create a cognito user using the userData object 
  let cognitoUser = new AmazonCognitoIdentity.CognitoUser(
    userData
  );

  // set the cognito user session w/ the CognitoUserSession
  cognitoUser.setSignInUserSession(session);

  // get the Amplify authenticated user      
  return Auth.currentAuthenticatedUser();
};`

@chanpod There’s a separate feature request for this: https://github.com/aws-amplify/amplify-js/issues/8933. You might +1 it to support the request.

Based on the error, my first guess would be that Amplify/Auth is misconfigured. Otherwise, Auth._oAuthHandler wouldn’t be undefined. I looked at your post #9917, and I don’t see that you’ve provided your oauth config to Amplify.configure({ … }). I’ve pasted the oauth segment below; you can see how this snippet fits in the overall Amplify config as shown in the documentation. Can you confirm whether you’ve configured Cognito >> User Pools >> App Integration, and included that information w/ your configuration?

oauth: {
     domain: 'your_cognito_domain',
     scope: ['phone', 'email', 'profile', 'openid', 'aws.cognito.signin.user.admin'],
     redirectSignIn: 'http://localhost:3000/',
     redirectSignOut: 'http://localhost:3000/',
     responseType: 'code' // or 'token', note that REFRESH token will only be generated when the responseType is code
     }

You can console.log(Amplify); to see whether AmplifyClass > Auth > _oAuthHandler is configured. If you don’t see the info above filled in, then that’s why _oAuthHandler is undefined.

You seem to have solved your use case. Our team will consider your feature request in the future for Auth