Amplify-js: Example of working Federated Auth in React Native EXPO

Created on 13 Nov 2019  ยท  39Comments  ยท  Source: aws-amplify/amplify-js

I'm posting this because I finally got my auth to work & documentation did not completely help me.
I hope this will help other people. If you spot something wrong in my post, feel free to contribute.

npm list -g --depth=0
/usr/local/lib
โ”œโ”€โ”€ @aws-amplify/[email protected]
โ”œโ”€โ”€ [email protected]
โ”œโ”€โ”€ [email protected]
โ”œโ”€โ”€ [email protected]
โ””โ”€โ”€ [email protected]

Now init your expo & amplify project.
Then get your client id & secret from Google and Facebook.
Do your "amplify add auth". Use federation & paste those ids & secrets.

So far this is all from the docs.

I created a new provider in AWS IAM for accounts.google.com and used that in the cognito federation settings. You need this for google because you will be testing on your EXPO app and then compiling to an actual app (via expo build:android or ios).

In Cognito User pool, I have the basic Google ID not the new provider.
(Side note: I am unclear as to why I need both userpool and federation to have this info. But its working.)

App.js

import React from "react";
import { AppLoading } from "expo";
import { AsyncStorage } from "react-native";
// import AsyncStorage from '@react-native-community/async-storage' if you use expo, dont use the community package
import Amplify, { Auth, Cache } from "aws-amplify";
import awsconfig from "./aws-exports";

// Thank you to contributors of this thread https://github.com/aws-amplify/amplify-js/issues/4351 
const MEMORY_KEY_PREFIX = "@MyStorage:";
let dataMemory = {};

/** @class */
class MemoryStorage {
  static syncPromise = null;
  /**
   * This is used to set a specific item in storage
   */
  static setItem(key: string, value: string) {
    AsyncStorage.setItem(MEMORY_KEY_PREFIX + key, value);
    dataMemory[key] = value;
    return dataMemory[key];
  }

  /**
   * This is used to get a specific key from storage
   */
  static getItem(key) {
    return Object.prototype.hasOwnProperty.call(dataMemory, key)
      ? dataMemory[key]
      : undefined;
  }

  /**
   * This is used to remove an item from storage
   */
  static removeItem(key) {
    AsyncStorage.removeItem(MEMORY_KEY_PREFIX + key);
    return delete dataMemory[key];
  }

  /**
   * This is used to clear the storage
   */
  static clear() {
    dataMemory = {};
    return dataMemory;
  }

  /**
   * Will sync the MemoryStorage data from AsyncStorage to storageWindow MemoryStorage
   */
  static sync() {
    if (!MemoryStorage.syncPromise) {
      MemoryStorage.syncPromise = new Promise((res, rej) => {
        AsyncStorage.getAllKeys((errKeys, keys) => {
          if (errKeys) rej(errKeys);
          const memoryKeys = keys.filter(key =>
            key.startsWith(MEMORY_KEY_PREFIX)
          );
          AsyncStorage.multiGet(memoryKeys, (err, stores) => {
            if (err) rej(err);
            stores.map((result, index, store) => {
              const key = store[index][0];
              const value = store[index][1];
              const memoryKey = key.replace(MEMORY_KEY_PREFIX, "");
              dataMemory[memoryKey] = value;
            });
            res();
          });
        });
      });
    }
    return MemoryStorage.syncPromise;
  }
}
const refreshToken = async () => {
  await MemoryStorage.sync();
  const federatedInfo = MemoryStorage.getItem("aws-amplify-federatedInfo");
  const federatedInfoParsed = JSON.parse(federatedInfo);

  const { token } = federatedInfoParsed;
  return {
    token
  };
};
Amplify.configure(awsconfig);
Auth.configure({
  refreshHandlers: {
    'accounts.google.com': refreshToken,   // NB i previously did not use the quotes & it didn't work
    'facebook': refreshToken
  },
  storage: MemoryStorage
});
...
export default class App extends React.Component {
..... 
}

My login.js screen

import React from "react";
...
import * as Constants from "expo-constants";
import * as Facebook from "expo-facebook";
import * as Google from "expo-google-app-auth";
import Amplify, {  Auth,   API,  Cache,  Credentials, } from "aws-amplify";
import Utils from "./utils.js";

export default class Login extends React.Component {
...
  async fbsignIn() {
    const {
      type,
      token,
      expires,
      permissions
    } = await Facebook.logInWithReadPermissionsAsync("123123123123123123123", {
      permissions: ["public_profile", "email"]
    });
    if (type === "success") {
        const credentials = await Auth.federatedSignIn(
          "facebook",
          {
            token: token,
            expires_at: expires
          },
          { name: "USER_NAME" }
        );
...
    }
...
  }

  async GoogleSignin() {
       const tokenResponse = await Google.logInAsync({
      androidClientId:        "XXX.apps.googleusercontent.com",
      iosClientId:        "YYY.apps.googleusercontent.com",
      androidStandaloneAppClientId:        "XXX.apps.googleusercontent.com",
      iosStandaloneAppClientId:        "YYY.apps.googleusercontent.com",
      scopes: ["profile", "email"]
    });
    const {      user,      accessToken,     idToken,      accessTokenExpirationDate    } = TokenResponse;
    if (tokenResponse.type == "success") {
        const credentials = await Auth.federatedSignIn(
          "accounts.google.com",
          {
            token: idToken,
            expires_at: accessTokenExpirationDate * 1000 + new Date().getTime()
          },
          user
        );
...
...
  }

Results:
1) I can login with AWS, Google, Facebook correctly.
2) For google, I dont get a uniform AWS looking username like "eu-west-1....". Its just numbers like "1102000384762384". I would like the AWS looking ones but I'm just happy this works.
3) When i reload the app, the "componentDidMount" section of the login screen will do a "Auth.currentAuthenticatedUser" and it will return me the correct info & do the refreshing of the tokens so that I have access to API and S3 again.
4) There is a current issue I face with Facebook. Each login consumes 100+ api calls, where it should only consume 2. I've read that this is something with expo and deep linking and might go away once I build the app. Will find out soon.

I hope this helps someone. Will update if i have improvements/bugs.

Auth React Native documentation pending-close-response-required

All 39 comments

Hi @mithunkalan - thanks for the example. I am implementing similar example using React Native (not Expo). I am able to get the login work and get CognitoIdentity but I am not able to get Cognito access token, refresh token and id token (Not Facebook/Google but Cognito access/refresh tokens)

Can you please help if you have encountered this situation? Thanks.

Hi @mithunkalan - thanks for the example. I am implementing similar example using React Native (not Expo). I am able to get the login work and get CognitoIdentity but I am not able to get Cognito access token, refresh token and id token (Not Facebook/Google but Cognito access/refresh tokens)

Can you please help if you have encountered this situation? Thanks.

Sorry mate, haven't done this before. You're just using Cognito and no federation? Auth & refresh should have worked out of the box.

I am using federated sign in method same as yours but instead of refresh,
aceess and id tokens I am getting CognitoIdentity token

On Wed, Nov 13, 2019, 21:33 mithun kalan notifications@github.com wrote:

Hi @mithunkalan https://github.com/mithunkalan - thanks for the
example. I am implementing similar example using React Native (not Expo). I
am able to get the login work and get CognitoIdentity but I am not able to
get Cognito access token, refresh token and id token (Not Facebook/Google
but Cognito access/refresh tokens)

Can you please help if you have encountered this situation? Thanks.

Sorry mate, haven't done this before. You're just using Cognito and no
federation? Auth & refresh should have worked out of the box.

โ€”
You are receiving this because you commented.
Reply to this email directly, view it on GitHub
https://github.com/aws-amplify/amplify-js/issues/4388?email_source=notifications&email_token=AJ5ONHOG7IUIVKUDDO37MNDQTQQMBA5CNFSM4JMUZ742YY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOED6UIVI#issuecomment-553469013,
or unsubscribe
https://github.com/notifications/unsubscribe-auth/AJ5ONHIVLGQSRGM5ZWW4A63QTQQMBANCNFSM4JMUZ74Q
.

Hi @mithunkalan - can you please do me a favor? Can you please console log your credentials variable in which you get Auth.federatedSignIn() results? I just want to confirm the results with mine. Many thanks.

Hi @mithunkalan - can you please do me a favor? Can you please console log your credentials variable in which you get Auth.federatedSignIn() results? I just want to confirm the results with mine. Many thanks.

No idea how to help you. Have you looked into making a developer provider in cognito? Or the JWT section of the Amplify docs?

CognitoIdentityCredentials {
  "_clientConfig": Object {
    "region": "eu-west-1",
  },
  "_identityId": "eu-west-1:0XXXXXXXXXXXXXXXbf085",
  "accessKeyId": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
  "authenticated": true,
  "cognito": Object {
    "CALL_EVENTS_BUBBLE": [Function CALL_EVENTS_BUBBLE],
    "MONITOR_EVENTS_BUBBLE": [Function EVENTS_BUBBLE],
    "_clientId": 3,
    "_events": Object {
      "apiCall": Array [
        [Function CALL_EVENTS_BUBBLE],
      ],
      "apiCallAttempt": Array [
        [Function EVENTS_BUBBLE],
      ],
    },
    "config": Config {
      "apiVersion": null,
      "apiVersions": Object {},
      "clientSideMonitoring": false,
      "computeChecksums": true,
      "convertResponseTypes": true,
      "correctClockSkew": false,
      "credentialProvider": null,
      "credentials": null,
      "customUserAgent": "aws-amplify/1.2.3 react-native",
      "dynamoDbCrc32": true,
      "endpoint": "cognito-identity.eu-west-1.amazonaws.com",
      "endpointCacheSize": 1000,
      "endpointDiscoveryEnabled": false,
      "hostPrefixEnabled": true,
      "httpOptions": Object {
        "timeout": 120000,
      },
      "logger": null,
      "maxRedirects": 10,
      "maxRetries": undefined,
      "paramValidation": true,
      "params": Object {
        "IdentityId": "eu-west-1:XXXXXXXXXXXXXXX",
        "IdentityPoolId": "eu-west-1:XXXXXXXXXXXXXXX1",
        "Logins": Object {
          "accounts.google.com": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
        },
        "RoleSessionName": "web-identity",
      },
      "region": "eu-west-1",
      "retryDelayOptions": Object {},
      "s3BucketEndpoint": false,
      "s3DisableBodySigning": true,
      "s3ForcePathStyle": false,
      "signatureCache": true,
      "signatureVersion": "v4",
      "sslEnabled": true,
      "systemClockOffset": 0,
      "useAccelerateEndpoint": false,
    },
    "endpoint": Endpoint {
      "host": "cognito-identity.eu-west-1.amazonaws.com",
      "hostname": "cognito-identity.eu-west-1.amazonaws.com",
      "href": "https://cognito-identity.eu-west-1.amazonaws.com/",
      "path": "/",
      "pathname": "/",
      "port": 443,
      "protocol": "https:",
    },
    "isGlobalEndpoint": false,
  },
  "data": Object {
    "Credentials": Object {
      "AccessKeyId": "XXXXXXXXXXXXXXX",
      "Expiration": 2019-11-14T07:11:24.000Z,
      "SecretKey": "XXXXXXXXXXXXXXX",
      "SessionToken": "XXXXXXXXXXXXXXX......",
    },
    "IdentityId": "eu-west-1:XXXXXXXXXXXXXXX",
  },
  "expireTime": 2019-11-14T07:11:24.000Z,
  "expired": false,
  "params": Object {
    "IdentityId": "eu-west-1:XXXXXXXXXXXXXXX",
    "IdentityPoolId": "eu-west-1:XXXXXXXXXXXXXXX",
    "Logins": Object {
      "accounts.google.com": "XXXXXXXXXXXXXXX",
    },
    "RoleSessionName": "web-identity",
  },
  "refreshCallbacks": Array [],
  "sessionToken": "XXXXXXXXXXXXXXX.....",
  "sts": Object {
    "CALL_EVENTS_BUBBLE": [Function CALL_EVENTS_BUBBLE],
    "MONITOR_EVENTS_BUBBLE": [Function EVENTS_BUBBLE],
    "_clientId": 4,
    "_events": Object {
      "apiCall": Array [
        [Function CALL_EVENTS_BUBBLE],
      ],
      "apiCallAttempt": Array [
        [Function EVENTS_BUBBLE],
      ],
    },
    "config": Config {
      "apiVersion": null,
      "apiVersions": Object {},
      "clientSideMonitoring": false,
      "computeChecksums": true,
      "convertResponseTypes": true,
      "correctClockSkew": false,
      "credentialProvider": null,
      "credentials": null,
      "customUserAgent": "aws-amplify/1.2.3 react-native",
      "dynamoDbCrc32": true,
      "endpoint": "https://sts.amazonaws.com",
      "endpointCacheSize": 1000,
      "endpointDiscoveryEnabled": false,
      "hostPrefixEnabled": true,
      "httpOptions": Object {
        "timeout": 120000,
      },
      "logger": null,
      "maxRedirects": 10,
      "maxRetries": undefined,
      "paramValidation": true,
      "region": "eu-west-1",
      "retryDelayOptions": Object {},
      "s3BucketEndpoint": false,
      "s3DisableBodySigning": true,
      "s3ForcePathStyle": false,
      "signatureCache": true,
      "signatureVersion": "v4",
      "sslEnabled": true,
      "systemClockOffset": 0,
      "useAccelerateEndpoint": false,
    },
    "endpoint": Endpoint {
      "host": "sts.amazonaws.com",
      "hostname": "sts.amazonaws.com",
      "href": "https://sts.amazonaws.com/",
      "path": "/",
      "pathname": "/",
      "port": 443,
      "protocol": "https:",
    },
    "isGlobalEndpoint": true,
  },
  "webIdentityCredentials": WebIdentityCredentials {
    "_clientConfig": Object {
      "region": "eu-west-1",
    },
    "accessKeyId": undefined,
    "data": null,
    "expireTime": null,
    "expired": true,
    "params": Object {
      "IdentityId": "eu-west-1:XXXXXXXXXXXXXXX",
      "IdentityPoolId": "eu-west-1:XXXXXXXXXXXXXXX",
      "Logins": Object {
        "accounts.google.com": "XXXXXXXXXXXXXXX...(truncated to the first 10000 characters)

Yeah @mithunkalan - thanks for posting the object, much appreciated. I did open another question issue over amplify JS (ref) and turns out that React Native Federated Sign in without hosted solution is just identifying the Federated Pool and not Cognito User pool.

Hosted UI solution is the only way as of now for React Native to get user logged in through Facebook and Google etc and still get access, refresh and Id tokens.

Thanks.

@mithunkalan thanks for posting your example! Have you run it as a standalone app on a physical Android device yet, or have you only run it in the Expo app thus far?

I am curious if you are seeing or will see this issue with your implementation, since the issue prevents standalone apps from signing in with Amplify on Android devices.

@schellack I have built a standalone apk. Easy expo build:android. Nothing fancy. Works fine. Played for a 10mins & all is fine. Auth, S3 & API still work.

Thanks for the quick reply @mithunkalan. Personally I get multiple "Code flow event" messages on standalone apks on Android device hardware only.

Do you override the urlOpener in your amplify auth config? If so, would you mind posting how?

@schellack no probs. I dont override it. My amplify auth is out of the box and then I add in the storage and refreshhandler as posted in my first post.
I'm not using any HOC or withauthenticator conponents in RN.

Update: Just keeping my notes up to date....
If you do a "amplify push", you might want to check aws-exports.js "aws_appsync_authenticationType": "AWS_IAM" if you start seeing 'no current user' everywhere.

@mithunkalan doesn't have the 'multiple code flow calls' issue (could be worked around using this PR ) because he uses the expo google login flow, and not aws-amplify withOAuth HOC.
My question for @mithunkalan is why? why not using the aws-amplify withOAuth and Federated Identity Providers in Cognito?

@oriharel at the time i started the build, I had way too many problems with the Auth and customising the UI. My website auth is working out the box. I think I'll only change RN auth and UI in future. If I had to always keep stuff up-to-date, between Amplify, expo, react native, things change so much and so fast, I would never ship.

@oriharel My code failed to refresh tokens after an hour. Drove me nuts. Dumped everything and went to withOauth. Bit of a mission to figure it out get it all working. Seems like is works. No HOC. using the web hostedUI.

Hey @oriharel @schellack @Ravim-addweb
Need some help with withOauth.
I get my app to run fine and maintain tokens on the expo simulator. But when I build for android the hosted UI tells me "an error was encountered with the requested page". I see that the URL says "...error=redirect_mismatch..."

My setup is
in app.json

{ 
"expo": {
...
"scheme":"somenewapp",
}
}

then
in someloginpage.js, before the class is declared

Amplify.configure({
  Auth: {
    oauth: {
      domain:
        "ZZZ.auth.eu-west-1.amazoncognito.com",
      scope: [
        "phone",
        "email",
        "openid",
        "profile",
        "aws.cognito.signin.user.admin"
      ],
      redirectSignIn: "somenewapp://main/",
      redirectSignOut: "somenewapp://main/",
      responseType: "code",
      options: {
        // Indicates if the data collection is enabled to support Cognito advanced security features. By default, this flag is set to true.
        AdvancedSecurityDataCollectionFlag: true
      },
      urlOpener: urlOpener
    },
   }
});

my cognito app client settings have
somenewapp:// in the callback and the signout fields.

Any ideas what i'm doing wrong? How do your expo based standalone RN apps do it?
Thanks

Your not doing anything wrong - you've encountered a known issue with Expo standalone app that causes the Linking.urlListener('url', callback) to fire multiple times. calling multiple times to coginto with federated identity token (Google, Facebook, etc) is errornous (you can only call one time with new token).
So... I've filed a PR so there will be a more 'elegant' way to override the url handler. untill the PR is merged I just used good old Javascript and override Linking.addUrlListener method with mine, keeping tap on the times the callback is called with that token (debouncing all but the first call).

Here's my code:

/**
 * Todo - this is a horrible hack to overcome amplify unable to handle multiple events
 * @param originalHandler
 */
const urlHandlerWrapper = function(originalHandler) {
    return function(urlObj) {
        const { url } = urlObj;
        if (url.indexOf('signIn?code=') > -1) {
            if (!usedUrls.includes(url)) {
                usedUrls.push(url);
                originalHandler(urlObj);
            }
        } else {
            originalHandler(urlObj);
        }
    };
};
let originalFunc = Linking.addEventListener;
Linking.addEventListener = (type, handler) => {
    originalFunc.call(Linking, type, urlHandlerWrapper(handler));
};
//*****

Thanks @oriharel . Spent the last few days trying to get it to work. Its working now. Had to do something extra to your stuff. Here's mine:

in my login.js page. After the imports & before the class definition.

var usedUrls = [];

const urlHandlerWrapper = function(input, originalHandler) {
  usedUrls.push(input.url);
  if (input.url.indexOf("?code=") > -1) {
    if (!usedUrls.includes(input.url)) {
      usedUrls.push(input.url);
      originalHandler(input);
    }
  }
  if (usedUrls.length === 1 )
    return function(input) {
      originalHandler(input);
    };
else return null
};

const urlOpener = async (url, redirectUrl) => {
  // On Expo, use WebBrowser.openAuthSessionAsync to open the Hosted UI pages.
  const { type, url: newUrl } = await WebBrowser.openAuthSessionAsync(
    url,
    redirectUrl
  );
  if (type === "success") {
    if (Platform.OS === "ios") {
      await WebBrowser.dismissBrowser();
      return Linking.openURL(newUrl);
    }
  }
};


class login extends.....

  onAuthChange = event => {
    switch (event.payload.event) {
      case "signIn":
        usedUrls = [];
        this.setState({ user: event.payload.data });
        this.doSomethingBecauseYoureNowLoggedInCorrectly()
        break;
      case "signOut":
        this.setState({ user: null });
        break;
      case "signIn_failure":
        Auth.signOut();
        break;
      case "customOAuthState":
        this.setState({ customState: event.payload.data });
    }
  };

  async componentDidMount() {
    Linking.addEventListener("url", urlHandlerWrapper);
    Hub.listen("auth", this.onAuthChange);
    Auth.currentAuthenticatedUser()
      .then(user => {
        this.doSomethingBecauseYoureNowLoggedInCorrectly()
      })
      .catch(() => {
        this.doSomethingElseBecauseYoureNotLoggedIn()
        console.log("Not signed in");
      });
  }

  componentWillUnmount() {
    Linking.removeEventListener("url", urlHandlerWrapper);
    Hub.remove("auth", this.onAuthChange);
  }

This so far should have worked, but it didn't. I looked at the logs and still saw codeflow and parsedUrl events being fired. Console logs showed that your code was being executed but something else looked like it was firing the events and your code was opening the links. I think the right solution was to stop the firing in the first place. So I went to town on the library to see what is the firing order of the code & came up with my solution.

This awful hack is done in /node_modules/@aws-amplify/auth/lib/Auth.js
There was a comment stating that some 3 lines of code should have been removed. But the last few major builds looked like that the 'breaking change' code was still there.

...
AuthClass.prototype.configure = function (config) {
.....
  // **NOTE** - Remove this in a future major release as it is a breaking change
            var d = []
            urlListener_1.default(function (_a) {
                var url = _a.url;
                if (!d.includes(url)){
                  d.push(url)
                  _this._handleAuthResponse(url);
                }
            });
        }
        dispatchAuthEvent('configured', null, "The Auth category has been configured successfully");
        return this._config;
    };


After 6 days of hack, build, install, logcat on android studio, repeat... that's the best I can come up with that actually works. For me. From the forums, everyone has some slight difference in what they do and thats why a thousand people aren't reporting my exact problem.

I hope this helps someone.

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.

@oriharel Can you link the PR that you mention in this comment?

Your not doing anything wrong - you've encountered a known issue with Expo standalone app that causes the Linking.urlListener('url', callback) to fire multiple times.

@mithunkalan Thank you so much for your patch! That line in question is the source of several open issues and we've been investigating ways to address it.

So that I better understand your solution, it is ensuring that the _handleAuthResponse is only called _once_ per-URL, due to the Expo bug mentioned above?

If that bug/PR for Expo is resolved, is there another scenario that we should be accounting for?

Hey @ericclemmons
Yes. _handleAuthResponse is only being called once.
As for other scenarios, I don't know. From what i've been seeing, there's a lot of people doing a lot of different things.

@ericclemmons do we know when a fix might be available for the oauth token request being called twice. We tested a bunch of versions of the amplify library and discovered that the issue started when the new _handleAuthResponse flow was added (the one that is indicated to be removed in a future release because its a breaking change).

We are experiencing this issue on our react-native (61.4, no expo) app and as a result are experiencing the "invalid_grant" error due to the double token request (the user is ultimately signed in). This seems to be recoverable on IOS but on Android the user can appear to be reverted back to the "login" screen even though the user is ultimately successfully logged in.

@HilSny We don't have a fix ready, but we could get @mithunkalan's fix in quickly if you're able to confirm it works for you:

AuthClass.prototype.configure = function (config) {
.....
  // **NOTE** - Remove this in a future major release as it is a breaking change
            var d = []
            urlListener_1.default(function (_a) {
                var url = _a.url;
                if (!d.includes(url)){
                  d.push(url)
                  _this._handleAuthResponse(url);
                }
            });
        }
        dispatchAuthEvent('configured', null, "The Auth category has been configured successfully");
        return this._config;
    };

In the interim, I'll revisit my test app for validation...

Thanks @ericclemmons, I am testing now and it is working but we want to be extra sure so I'll get back to you in an hour or so if its consistent.

Hi @ericclemmons we've actually confirmed that this definitely works with a series of logging. Are you planning to PR this fix?

@HilSny Wow, awesome work! Thanks so much! I can work on a PR for this if you're unable to.

If you can share more about the logging you have, that would help the PR review process!

@ericclemmons we're about to have our product meeting and I don't have aws-amplify building locally on my machine at the moment, so if you could PR that would be awesome. The logging we did was fairly simple, we turned on debug logging in our RN app and then logged out the url and auth step in the changed code.

With the logging, we could see in our app that the url is caught twice by the listener but because of the debounce, we were only auth-ed in once with the new code. We knew the issue was likely in some part due to the oauth/token endpoint being called twice (on the second time it would be an invalid_grant) so this solution appears to mitigate that.

Let me know if I can provide other details and thank you for being so responsive. I've been triaging this for a couple days :)

       var d = []
            urlListener_1.default(function (_a) {
                var url = _a.url;
                console.log("URL", url)
                if (!d.includes(url)){
                  console.log("Auth-ing")
                  d.push(url)
                  _this._handleAuthResponse(url);
                }
            });
        }
        dispatchAuthEvent('configured', null, "The Auth category has been configured successfully");
        return this._config;
    };

@oriharel Can you link the PR that you mention in this comment?

Your not doing anything wrong - you've encountered a known issue with Expo standalone app that causes the Linking.urlListener('url', callback) to fire multiple times.

@mithunkalan Thank you so much for your patch! That line in question is the source of several open issues and we've been investigating ways to address it.

So that I better understand your solution, it is ensuring that the _handleAuthResponse is only called _once_ per-URL, due to the Expo bug mentioned above?

If that bug/PR for Expo is resolved, is there another scenario that we should be accounting for?

https://github.com/aws-amplify/amplify-js/pull/4005

@ericclemmons I submitted a PR with the code from yesterday. We have it building in a forked version of the repo and it is working.

@mithunkalan : Since the fix merged in by @HilSny ? If everything is good , can we close this issue?

Go for it. We've had a fork of this working in production for two days @ashika01

@ashika01 Yes please.
Glad it helped.

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.

Thanks for validating @HilSny !

any chance someone of the amplify team can test out federated login with auth0 on react-native? I've had issues where I need to manually re-login the user when S3 requests and pinpoint requests start failing. Been an issue for a year that I've had this library in my app and have had to write a lot of defensive code to mitigate it. Happens randomly, sometimes when app updates are pushed to the app, sometimes when opening the app from close.

@mithunkalan Does your code example create the user in Cognito user pool?

I think its default pool. But ive never used specific pools.

On Sun, 20 Dec 2020, 23:04 amin79, notifications@github.com wrote:

@mithunkalan https://github.com/mithunkalan Does your code example
create the user in Cognito user pool?

โ€”
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
https://github.com/aws-amplify/amplify-js/issues/4388#issuecomment-748670602,
or unsubscribe
https://github.com/notifications/unsubscribe-auth/AA62T5C4D5AURREZ5GEKE7DSVZREVANCNFSM4JMUZ74Q
.

@mithunkalan I mean when no user created to the Cognito user pool, how we can control the users authorization in AWS AppSync queries?

I do a check if the user is authed. If it throws an error, i do the
getcredentials call and that makes the user a guest. Then in cognito
console federation, i allow guest access. Then IAM to allow guest role
access to appsync.

I think the default way the awscli sets this up is ok these days. No need
to manual modify it. But my original post is a year old and i havent had to
dig into it for a while. Customer is happy and daily using it just fine.
One year old libraries.

On Sun, 20 Dec 2020, 23:21 amin79, notifications@github.com wrote:

@mithunkalan https://github.com/mithunkalan I mean when no user created
to the Cognito user pool, how we can control the users authorization in AWS
AppSync queries?

โ€”
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
https://github.com/aws-amplify/amplify-js/issues/4388#issuecomment-748672471,
or unsubscribe
https://github.com/notifications/unsubscribe-auth/AA62T5GYJKP52UDAGHUFH23SVZTFHANCNFSM4JMUZ74Q
.

Thank you for your answer anyway.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

guanzo picture guanzo  ยท  3Comments

romainquellec picture romainquellec  ยท  3Comments

DougWoodCDS picture DougWoodCDS  ยท  3Comments

ddemoll picture ddemoll  ยท  3Comments

TheRealRed7 picture TheRealRed7  ยท  3Comments