Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Amplify missing the ability to programmatically create a user session using Cognito (id, access, refresh) tokens #8632

Closed
3 tasks done
jglesner opened this issue Jul 23, 2021 · 18 comments
Labels
Auth Related to Auth components/category question General question

Comments

@jglesner
Copy link

jglesner commented Jul 23, 2021

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

@jglesner jglesner changed the title CognitoAuth parseCognitoWebResponse functionality missing after retirement of amazon-cognito-auth-js library Amplify missing the ability to programmatically create a cognito user session Jul 23, 2021
@jglesner jglesner changed the title Amplify missing the ability to programmatically create a cognito user session Amplify missing the ability to programmatically create a user session using Cognito (id, access, refresh) tokens Jul 24, 2021
@jglesner
Copy link
Author

jglesner commented Jul 25, 2021

Upon further investigation, it does appear you can create a CognitoUserSession with the id/access/refresh tokens using the amazon-cognito-identity-js package with the code below, but Amplify doesn't recognize that the session exists.

import * as AmazonCognitoIdentity from "amazon-cognito-identity-js";

// passing a valid cognito response url with authorization_code and state
Auth._oAuthHandler.handleAuthResponse(url).then((resp) => {

    // using the id, access and refresh tokens provided by the AuthHandler:
    const AccessToken = new AmazonCognitoIdentity.CognitoAccessToken({
           AccessToken: resp.accessToken,
    });
                
    const IdToken = new AmazonCognitoIdentity.CognitoIdToken({
          IdToken: resp.idToken,
    });

    const RefreshToken = new AmazonCognitoIdentity.CognitoRefreshToken({
           RefreshToken: resp.refreshToken,
    });

    const sessionData = {
          IdToken: IdToken,
          AccessToken: AccessToken,
          RefreshToken: RefreshToken,
    };

    const session = new AmazonCognitoIdentity.CognitoUserSession(
          sessionData
    );
});

Once the above code completes successfully, running Auth.currentAuthenticatedUser() (code below for clarity sake) logs "Not signed in The user is not authenticated" in the console.

      Auth.currentAuthenticatedUser()
        .then((user) => console.log(user))
        .catch((err) => console.log(`Not signed in ${err}`));

Screen Shot 2021-07-25 at 7 39 08 AM

Is there anyway for amplify.auth to pick up the CognitoUserSession, or to do something similar through Amplify? This looks similar to #6555.

@jglesner
Copy link
Author

jglesner commented Jul 25, 2021

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.

@iartemiev iartemiev added Auth Related to Auth components/category question General question labels Jul 26, 2021
@aws-eddy
Copy link

aws-eddy commented Oct 22, 2021

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

@Marcussx
Copy link

Marcussx commented May 19, 2022

Hello everyone,

could you check my post here: #9917

@Marcussx
Copy link

@jglesner Hey mate, could you help here?

@jglesner
Copy link
Author

jglesner commented May 20, 2022

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).

@Marcussx
Copy link

Marcussx commented May 20, 2022

@jglesner I already red your blog yesterday and congratulations mate, it is really helpful and nicely written article! Thanks a lot.

Regarding my issue, I'm not using typescript, just ReactJs and I really can't figure out why this is happening..

configureAmplify exception handler: TypeError: Cannot read properties of undefined (reading 'handleAuthResponse')
    at configureAmplify (configureAmplify.js:15:1)
    at _callee2$ (Login.js:168:1)
    at tryCatch (runtime.js:64:1)
    at Generator.invoke [as _invoke] (runtime.js:281:1)
    at Generator.next (runtime.js:117:1)
    at asyncGeneratorStep (asyncToGenerator.js:3:1)
    at _next (asyncToGenerator.js:25:1)

I would like simple to try this, if this works or not, so how could I pass this ? I'm aware that scenario is not recommended, but could you help here a little bit? I would be really thankful!

Thanks in advance Jeremy

@jglesner
Copy link
Author

jglesner commented May 23, 2022

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.

@Marcussx
Copy link

Marcussx commented May 30, 2022

@jglesner Thanks a lot Jeremy, this post was helpful a lot !

But I have one issue and I'm wondering how could we solve this... Actually if I close the tab or the browser, Aplify somehow lose configuration and it says:


_[WARN] 27:15.651 AWSS3Provider - ensure credentials error No Auth module registered in Amplify
Uncaught (in promise) No credentials_

Have you face this issue already or ?

Thanks in advance

@jglesner
Copy link
Author

jglesner commented May 30, 2022

Actually if I close the tab or the browser, Aplify somehow lose configuration

I assume you’re doing oAuth Federated login … but it sounds like a separate tab/browser is being opened? Are you doing this in a standard web app or a js mobile app?

@bobthebuilder1997
Copy link

bobthebuilder1997 commented Jul 4, 2022

Need a way to programmatically create a user session using Cognito (id, access, refresh) tokens. This will be a huge plus for advanced workflows including allowing users to log in to Chrome extensions using amplify. @evcodes @abdallahshaban557 is it possible to mark this as a feature request?

@chanpod
Copy link

chanpod commented Aug 20, 2022

I'm currently needing this feature as well. We have a shell app that does the login and passes the tokens to the micro UI applications. One of which is actually an SSR application. The server has no context for auth other than the tokens and it would be a terrible UX to make the user login again. So even the local storage hack won't work for us. I can get away with forcing the calls to have the correct headers, but that doesn't stop the library from fussing about no "active users" when trying to get subscriptions.

@jglesner
Copy link
Author

@chanpod There's a separate feature request for this: #8933. You might +1 it to support the request.

@Marcussx
Copy link

Marcussx commented Sep 13, 2022

Hey, @jglesner ! Thanks a lot mate for your contribution here! I'm interested in this line of code:

responseType: 'code', // or 'token', note that REFRESH token will only be generated when the responseType is code

so basically if we would like to achieve refresh token implementation we would need to keep this responseType: value as code ?

@kalousmichal
Copy link

kalousmichal commented Mar 31, 2023

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();
};`

@DefinitelyNotANugget
Copy link

Hello @jglesner !

I tried to replicate what you did thanks to your code.

My first question is : what's the value of the "url" parameter ? I currently have "https://.com/login/code=<AUTH_CODE>&state=. Would it work with it ?

I also have a problem : once I do the Auth._oAuthHandler.handleAuthResponse(url).then((resp) => { ............. });, my tokens have no value and if I tried to make a console.log(resp), I just have a "state:undefined".

Do you have an idea about what could be wrong ?

image

@jglesner
Copy link
Author

jglesner commented Oct 14, 2023

Hello @DefinitelyNotANugget

The URL is constructed/vended by cognito; it’s partially the redirect_uri that you set up in the Cognito Dashboard user pools > App integration > App client settings. Cognito appends a unique code grant to the URL that is used to retrieve the tokens.

After sign-in via the Hosted UI, Cognito redirects the user to that URL. You have to intercept it. Amplify natively uses a urlListner to process that. But here, we want to process it ourselves.

If the URL isn’t legit, handleAuthResponse won’t work. This method calls cognito with the code grant to return your app tokens.

@AndyMeagher
Copy link

I implemented @kalousmichal solution in typescript but on the very first load Auth.currentAuthenticatedUser() returns an error with No Authenticated User. When I refresh / call it again it succeeds. Has anyone else experienced this / know what is going on?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Auth Related to Auth components/category question General question
Projects
None yet
Development

No branches or pull requests

9 participants