Amplify-js: Switching Unauthenticated Users to Authenticated Users

Created on 12 Nov 2018  ·  28Comments  ·  Source: aws-amplify/amplify-js

It's common scenario, user starts as unauthenticated, later registers. I allow user to use services as unauthenticated in identity pool. Using amplify, if I run this scenario, an identity created as unauthenticated, later user signs up, unauthenticated identity stays as it is, and new authenticated identity created. It looks there is a way to convert unauthenticated users to authenticated ones in cognito, any idea how to achieve it using amplify?

Initially Unauthenticated User

// set the default config object
var creds = new AWS.CognitoIdentityCredentials({
    IdentityPoolId: 'us-east-1:1699ebc0-7900-4099-b910-2df94f52a030'
});
AWS.config.credentials = creds;

Switch to Authenticated User

// Called when an identity provider has a token for a logged in user
function userLoggedIn(providerName, token) {
    creds.params.Logins = creds.params.Logins || {};
    creds.params.Logins[providerName] = token;

    // Expire credentials to refresh them on the next request
    creds.expired = true;
}

Do we have access to CognitoIdentityCredentials function through amplify?

* Which Category is your question related to? *
Auth
* What AWS Services are you utilizing? *
S3, AppSync
* Provide additional details e.g. code snippets *
https://docs.aws.amazon.com/cognito/latest/developerguide/switching-identities.html

Auth Cognito feature-request

Most helpful comment

This issue was opened stunningly 9 months ago!!! 14 Interactions and lot of hacky-ways still nothing...
Started a project with Amplify and I really can see that unfortunately is not a production ready yet.

All 28 comments

Hi @ahmetcetin thanks for your feedback, what is the use you have in mind?

My question is similar to the original question. My application allows both unauthenticated and authenticated identities. However, I'm having issues switching from an unauthenticated to authenticated identity. Here's a simple example that demonstrates the issue I'm seeing.

const AWS = require("aws-sdk");
const Auth = require("aws-amplify").Auth;

const signInAndQueryDynamo = async (username, password) => {
  // user is unauthenticated at this point
  await Auth.signIn(username, password);

  const credentials = await Auth.currentCredentials();
  const dynamo = new AWS.DynamoDB.DocumentClient({
    service: new AWS.DynamoDB({
      apiVersion: "2013-04-01",
      credentials: Auth.essentialCredentials(credentials)
    })
  });

  // this API call will still use the unauthenticated role credentials
  return dynamo.get({
    TableName: "TableXYZ",
    Key: "1"
  });
};

It seems that calling Auth.signIn does not set the credentials to that of the authenticated identity.

@ahmetcetin @salimhamed thanks for your feedback. Currently switching users on amplify is not available. I will mark this as feature-request

👍

Am looking for support for this feature as well, currently using amazon-cognito-identity-js directly but would like to switch to Amplify.Auth. Can we possibly workaround by manually expiring the credentials and forcing a refresh?

With amazon-cognito-identity-js, am currently doing AWS.config.credentials.expired = true before fetching new authenticated credentials.

@elorzafe What would you suggest as a work around?

@salimhamed sorry to be late. I am currently working this issue. The code you posted should work as when Auth.signIn() returns, Amplify will get an authenticated credentials in the background. Can you make sure you are using the latest version? Also you can turn on the debug mode: window.LOG_LEVEL='DEBUG' in your app to get the debug info.

@ahmetcetin to make this issue clear, the feature wanted is to transfer the unauthenticated identity id into the authenticated one at the first time when the user signs in. Because as I know you cannot assign the unauthenticated identity id to a user who already has an authenticated one. So this case can only happen when the user first signs in.

@powerful23 right, it's exactly as you describe.

@powerful23 Thanks for the reply! Yes, I'll make sure I'm on the latest version and turn log_level to debug, then report back.

To provide a use-case:

It would be nice if I could allow a new user to:

  1. Arrive at my website for the first time.
  2. upload a photo (access S3)
  3. execute an appsync mutation where they are the "owner" of the object. (Other users can't query this object0).
  4. query appsync to list the items for which they are the "owner"
  5. create an account so that they can access their resources later
  6. [fifty days later, on a different computer]... login and access the photo from step 1.

Currently, I have to force them to create an account (step 4) before they can upload a photo (step 1). It would be nice if my users could test the service before being asked to create an account. Thus, It would be great if steps 1 through 3 could be performed as a guest user and then the guest user could be upgraded to an authenticated user when they perform step 4.

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

any news on this @powerful23 ?

@powerful23 BTW, this is still an issue for our team

This issue was opened stunningly 9 months ago!!! 14 Interactions and lot of hacky-ways still nothing...
Started a project with Amplify and I really can see that unfortunately is not a production ready yet.

Another month has passed. Any update?

Any update here?

Keen to know any update on this thread?

Keen to know any update on this thread?

My workaround is:
Create an account with deviceID as Mail ([email protected]) and password (deviceID). Than the user can change later mail and password (with a new Mail confirm)

But yes! It’s important to get this function from cognito.

@davidbiller
Are you using Cognito for authentication?
Cognito normally requires verify email. How do you bypass that?

@davidbiller
Are you using Cognito for authentication?
Cognito normally requires verify email. How do you bypass that?

@ruodingt

Yes. I do.
Use this pre sign up lambda:

exports.handler = (event, context, callback) => {
event.response.autoConfirmUser = true;
context.done(null, event);
};

@davidbiller Thanks so much, that's really helpful.
So later user changes their password and email. That email has to be verified as well.
Will the verification be automatically triggered by Cognito or we have to configure somethings.

One more question may be off the topic a bit but I think it you probably also thought about it before:

Previously we use the post-confirmation trigger to send user an welcome email. However, if we automatically confirm the dummy user with only deviceID, we can't send the welcome message using post-confirmation trigger. And I didn't find other appropriate trigger for that.
Do you know a workaround for sending welcome email?

@davidbiller Thanks so much, that's really helpful.
So later user changes their password and email. That email has to be verified as well.
Will the verification be automatically triggered by Cognito or we have to configure somethings.

One more question may be off the topic a bit but I think it you probably also thought about it before:

Previously we use the post-confirmation trigger to send user an welcome email. However, if we automatically confirm the dummy user with only deviceID, we can't send the welcome message using post-confirmation trigger. And I didn't find other appropriate trigger for that.
Do you know a workaround for sending welcome email?

There is a cognito function for this. You will find it easy.

You can’t send a mail without the mail address. But you can send it after the „fake“ signUp.
I build a ui as a signup, but it’s a password and mail change :)

Hi all, I couldn't wait and came up with a workaround. Hope it will help to progress with the proper solution sooner.

This TypeScript code solves it for me (should be executed before the first call to anything in Amplify):

import { Auth } from "aws-amplify";
import { Credentials, ICredentials, getAmplifyUserAgent } from "@aws-amplify/core";
import { CognitoIdentityClient, GetCredentialsForIdentityCommand } from '@aws-sdk/client-cognito-identity';
import { CognitoUser, IAuthenticationCallback } from 'amazon-cognito-identity-js';

type AuthCallbacksDelegate = (
  user: CognitoUser, 
  resolve: (value?: CognitoUser | any) => void, 
  reject: (value?: any) => void
) => IAuthenticationCallback;

const fixAuth = () => {
  const AuthClassInstance = Auth as any;
  const baseAuthCallbacks = AuthClassInstance.authCallbacks as AuthCallbacksDelegate;

  AuthClassInstance.authCallbacks = function(user: any, resolve: any, reject: any) {
    const callbacks = baseAuthCallbacks.call(this, user, resolve, reject);
    const baseOnSuccess = callbacks.onSuccess;
    const { region, userPoolId } = this._config;

    callbacks.onSuccess = async (session, userConfirmationNecessary?) => {
      const credentials = await Credentials.get() as ICredentials;
      if (credentials?.identityId && credentials.authenticated === false) {
        const cognitoClient = new CognitoIdentityClient({
          region,
          customUserAgent: getAmplifyUserAgent()
        });

        /* 
         * This call to GetCredentialsForIdentityCommand links the newly authenticated user with the anonymous identity in the Identity Pool.
         * When https://github.com/aws-amplify/amplify-js/blob/e11e938184fbb935374ca8f19b96365a7d6af7ce/packages/core/src/Credentials.ts#L354
         * is called further down the chain inside `baseOnSuccess`, it will return the same IdentityId instead of creating a new one.
         * Thus, the same value of UserId will be used for Pinpoint before and after registration.
         * In the case when the user is already registered and is signing in (i.e. they already have their account in User Pool linked to an Identity), 
         * this call to GetCredentialsForIdentityCommand will not make any additional links. Instead it will mark the new Identity as disabled and
         * will merge it into the old one - https://docs.aws.amazon.com/cognito/latest/developerguide/authentication-flow.html (under Merging Identities).
         * There's also this thread that covers that https://forums.aws.amazon.com/thread.jspa?threadID=238042
         */
        await cognitoClient.send(new GetCredentialsForIdentityCommand({
          IdentityId: credentials.identityId,
          Logins: {
            [`cognito-idp.${region}.amazonaws.com/${userPoolId}`]: session.getIdToken().getJwtToken()
          }
        }));
      }

      await (baseOnSuccess(session, userConfirmationNecessary) as unknown as Promise<void>);
    }  

    return callbacks;
  }
};

I'm happy to send a PR, please just confirm this is the right approach.

How does GitHub Support handle and write bug reports? ... questions in your bug report is the best way to get a speedy resolution of your issue !

Any update on this issue?

2 years almost ?

Any updates on this?

Was this page helpful?
0 / 5 - 0 ratings

Related issues

TheRealRed7 picture TheRealRed7  ·  3Comments

cosmosof picture cosmosof  ·  3Comments

rygo6 picture rygo6  ·  3Comments

guanzo picture guanzo  ·  3Comments

oste picture oste  ·  3Comments