We are using Auth.signIn for users in our user pool, and all is working as expected. We use an idToken to authorize users. We have all custom UI, and are using react-native and Amplify.
When we tried setting this up to log in with Facebook/Google, we are running into issues. Our two options were:
1) Use Cognito web portal:
a) Open up the web view for Cognito -> Federation Identity Providers.
b) When they click the facebook, we get back either a code, or we get tokens, e.g. idToken/accessToken.
c) the problem is now signing them in with Amplify.
2) Use Amplify Auth.federatedSignIn
a) this works, and when we sign them in, we get a user object with various details.
b) the problem is, when we call Auth.currentSession(), it says there is no signed in user.
c) this might be related to B, as we need to wrap the app in Authenticator, but this doesn't seem to be supported by Amplify yet?
d) This leads to another problem, even if this works, we dont get any idTokens and would not be able to hit our endpoints
We are stuck at finding a solution where we use our own UI/components, and are unable to think of a way to sign the user in, even though both options 1 and 2 "work" up until a certain point.
Hi thanks for your feedback.
For the first option, do you mean using Cognito hosted UI to get you sign in with Facebook/Google?
For the second option, you are actually signing with Facebook/Google and get a AWS credentials from Cognito Federated Identity pool, for details: https://github.com/aws/aws-amplify/wiki/FAQ#how-can-i-get-jwt-token-when-using-amplify-to-get-federated-users-login
Also we are working on to provide a session object when using Auth.federatedSignIn through Auth.currentSession.
Hi @powerful23
After doing some more research and reading up on docs, the behavior I would like is the following:
1) User hits on the custom Facebook button we have created
2) this signs the user in somehow to amplify (by using identity pool federation, and not federated identities), and we create a user pool entity.
It seems there's no way to currently do this, correct? Because even if I wanted to use the HOC authenticator component, I couldn't as it is not compatible with react native quite yet?
Any suggestions? I was able to create a federated identity login, and then through some hacky steps get an idToken, but the problem is we also want to create a user in the cognito user pools.
@VicFrolov I am running into the same problem. @powerful23 , this issue seems also seems to prevent the facebook (and google) user group from being populated. This then cascades into the inability to use user group based authentication techniques in AWS AppSync.
Related to #585
For now as I know you have to use Cognito Hosted UI to create external federated users in the cognito user pools. Unfortunately this feature is not implemented in React Native. We have this in the road map.
It's hard to identify, as the docs indicate you can indeed use HOC components, and Cognito hosted UI in react native. I tested it without any styling, and the cognito hosted UI did appear by using aws-amplify-react-native with a HOC component wrapping the entire test project app. I was greeted with a login page.
The concern I have, if this does work and isn't just the UI portion, is how to then style each component, as my login/signup pages are in separate components/views.
e.g. in the docs for Authentication, it has this line:
import { withAuthenticator } from 'aws-amplify-react'; // or 'aws-amplify-react-native';
The missing userSession when using federatedSignIn is critical to me too... :(
Any chances it could be released soon ? 😄
Meanwhile, is there any workaround I could use ? 😟
My org is also wondering about this. When a user signs up via facebook there is no record in the user pool. Can someone point me to docs on how this can work? I have not found anything and this is a deal breaker for using Amazon Cognito.
A custom code work around would be fine, I just have not seen any information on creating a user record with a federated auth. Is this possible?
Thanks
For now you have to use the Hosted UI to do what you are looking for. I
haven't tried it myself.
On Mon, Aug 6, 2018, 8:32 PM Mike Osterhout notifications@github.com
wrote:
My org is also wondering about this. When a user signs up via facebook
there is no record in the user pool. Can someone point me to docs on how
this can work? I have not found anything and this is a deal breaker for
using Amazon Cognito.A custom code work around would be fine, I just have not seen any
information on creating a user record with a federated auth. Is this
possible?Thanks
—
You are receiving this because you commented.
Reply to this email directly, view it on GitHub
https://github.com/aws-amplify/amplify-js/issues/1143#issuecomment-410894853,
or mute the thread
https://github.com/notifications/unsubscribe-auth/AHAb-Cv88h9ITAqrAxZB3n8fHr5F8P-Jks5uOOArgaJpZM4U8VEl
.
You have to use Hosted UI, however it is not compatible with react-native, nor is ample customization. The HOC itself is react-native compatible, but not identity providers.
We're most likely going to get rid of cognito all together, and just use federated identities as this is too much of a headache/waiting game.
It's a shame, because this seems like a core feature.
Any update on when this will be supported on React Native without hosted UI? Next month, end of this year or next year?
Can we help or is there some internal Cognito problem that needs to be solved before?
Related to #311 , #513 and PR #878
Why is the AWS Amplify team silent on this? Do you even have a plan to fix this? This should be a high priority item, if you claim that federated auth works in React Native with Amplify!
Currently we are working with the Cognito team to provide the api to sign in the User Pool with federated providers without using the Hosted UI. We are not sure about when it will be implemented but we will try to make it ASAP.
So I still don't understand how one could use the Hosted UI for facebook/google with amplify, and after they get the tokens from the callbacks, to have the user be logged in via cognito on amplify, with no HOC components.
@VicFrolov when calling Amplify.configure(), the library will check if oauth is configured and if yes, it will try to parse the current url which contains the tokens, and then construct a session based on those tokens. You can call Auth.currentAuthenticatedUser() when the page is loaded to tell whether the user is logged in. I have a pr about how to use the currentAuthenticatedUser method: https://github.com/aws-amplify/amplify-js/pull/1450/files
@powerful23 Hmmm, interesting! But if I were to be using react native + safari view for example, the user taps the facebook button in our app, it opens up safari view, and shows Cognito's hosted web UI.
The user then hits the cognito facebook button, they confirm on facebook, and get the callback URL + redirect, how do I pass the url in, for it to parse it and construct a session?
@VicFrolov good question. Unfortunately for now Amplify doesn't support that but you can do a workaround by using amazon-cognito-auth-js to parse the url like Amplify does. If you look into the auth module: https://github.com/aws-amplify/amplify-js/blob/master/packages/auth/src/Auth.ts#L173 and https://github.com/aws-amplify/amplify-js/blob/master/packages/auth/src/Auth.ts#L149
@powerful23 So just to confirm, I can use this workaround to log users into cognito via federated identities, using amplify and can then use Auth.currentUser() and stuff, right?
I just don't see where I can pass in the url that is returned to me from the callback, or where it is reading it.
If this is the case though, this is incredible news!
@VicFrolov Till we'll have the api to create a user in the user pool from Federated token, we used the following workaround:
Used react-native-app-auth to identify with cognito using oauth 2.0, it will open the browser and ask for a login then return the user to our app where we get the parsed tokens
We are using our own buttons in the ui but calling the Facebook/Google authorize point explicitly
Hope it makes sense
@dsinai14 thank you for this!
I would ideally prefer to remain within the Amplify framework, if powerful23 was suggesting I can use amazon-cognito-auth-js in unison with Amplify, but if not this sounds like a solid alternative :)
@VicFrolov yes. So once you parse the url successfully, you can use methods in the Amplify Auth module like currentSession or currentAuthenticatedUser. The root reason for this happening is that Amplify, amazon-cognito-auth-js and amazon-cognito-identity-js share the same cache items so as long as one of the library stores those tokens into the cache, the other ones can work by reading it.
@powerful23 oh my goodness, this is amazing, I had no idea they shared cache. This makes me very happy.
Also, slightly off topic but since we're discussing cache, is there any risk / downside to manually refreshing the tokens, and saving it to async storage in the correct place? We need to sometimes force token refreshes when users are changed to a group, as it adjusts their permissions.
Regardless, thank you for all of this information powerful23, I'm happy I get to remain with Amplify and not have to do a massive refactor of our SDK + front end authentication.
@VicFrolov as I know there is no risk refreshing tokens manually.
@powerful23 Hey so unfortunately this doesn't seem to be working for me. I was able to get cognitoAuthClient to log a user in via facebook callbackURL after using the hosted UI:
// some set up above
cognitoAuthClient.userhandler = {
onSuccess: function(result) {
console.log(result, 'Sign in success');
},
onFailure: function(err) {
console.log('Error!' + err);
},
};
await cognitoAuthClient.parseCognitoWebResponse(callbackURL);
if I then called
cognitoAuthClient.isUserSignedIn()
it returned true, and
cognitoAuthClient.getCurrentUser()
returned the appropriate user. However, if I called
await Auth.currentAuthenticatedUser();
after the above, it returned "Not Authenticated".
And vice versa, if I logged a user in via Amplify with username/pass, it worked for Amplify, but if I called cognitoAuthClient.isUserSignedIn(), I would get false.
Even more strange, I tried to see what user I was getting with:
const userPool = new CognitoUserPool(poolData);
userPool.storage.sync(function(err, result) {
if (err) {
console.log(err, "error")
} else if (result === 'SUCCESS') {
var cognitoUser = userPool.getCurrentUser();
console.log(cognitoUser, "some user")
}
});
And the above was in sync with amplify, but not CognitoAuth.
@VicFrolov did you check the localStorage after you are logged in?
The Cognito Auth Client will cache those tokens with the function: https://github.com/aws/amazon-cognito-auth-js/blob/master/src/CognitoAuth.js#L404
Can you confirm there is an item with the key LastAuthUser in your cache? Because when you call Auth.currentAuthenticatedUser(), it will try to get this item which happens here: https://github.com/aws-amplify/amplify-js/blob/master/packages/amazon-cognito-identity-js/src/CognitoUserPool.js#L125
Also please make sure you are using the default localStorage.
@powerful23 Hmmm yes you're right, they are not being stored at all in local storage. For some reason, after successfully "signing in" via google or facebook, I get:
cognitoAuthClient.isUserSignedIn() // Google_1234567890
cognitoAuthClient.getCurrentUser() // true
but when I print out all the local storage keys, they are not being generated (neither key or value). However, if I log in via amplify, it creates:
"@MemoryStorage:CognitoIdentityServiceProvider.clientId.username.idToken"
"@MemoryStorage:CognitoIdentityServiceProvider.clientId.username.refreshToken"
"@MemoryStorage:CognitoIdentityServiceProvider.clientId.username.accessToken"
"@MemoryStorage:CognitoIdentityServiceProvider.clientId.LastAuthUser"
On amplify logout, the above keys and values are removed.
I didn't change anything to localStorage, but maybe I need to set it?
Here is my cognitoAuthParams:
const cognitoAuthParams = {
ClientId: 'clientId',
UserPoolId: 'poolId',
AppWebDomain:
'https://my-app.auth.us-west-2.amazoncognito.com',
TokenScopesArray: [
'phone',
'email',
'profile',
'openid',
'aws.cognito.signin.user.admin',
],
RedirectUriSignIn: 'https://www.google.com',
RedirectUriSignOut: 'https://www.google.com',
IdentityProvider: 'Google',
};
I didn't set anything for Storage or AdvancedSecurityDataCollectionFlag.
Might as well post my functions:
async googleSignIn(callbackURL) {
try {
const AmplifyUser = await Auth.currentAuthenticatedUser();
console.log(AmplifyUser, '\nAMPLIFY LOGGED IN USER');
return AmplifyUser;
} catch (e) {
try {
await this.cognitoAuthClient.parseCognitoWebResponse(callbackURL);
console.log(this.cognitoAuthClient.getCurrentUser()); // returns correct username
console.log(this.cognitoAuthClient.isUserSignedIn()); // returns true
} catch (err) {
console.log(err, 'error parsing cognito web response');
}
}
}
and in the constructor of my class, I set the pre-mentioned params to:
this.cognitoAuthClient = new CognitoAuth(cognitoAuthParams);
this.cognitoAuthClient.userhandler = {
onSuccess: result => {
console.log(result, 'Sign in success');
},
onFailure: err => {
console.log(err, 'Sign in error');
},
};
@VicFrolov You can debug by setting a break point at https://github.com/aws/amazon-cognito-auth-js/blob/master/src/CognitoAuth.js#L404 to see how the CognitoAuth client stores your token after you logged in with Google.
@powerful23 So I'm actively trying to debug this, but to no avail. I erased all contents and settings, and tried fresh. The only guess I have is that rather than store it in AsyncStorage it's somehow storing it in memory, or somewhere else? That's how calling:
console.log(this.cognitoAuthClient.getCurrentUser(), 'getCurrentUser');
provides the correct username, since if I look into those methods, it clearly just checks for the key:
getCurrentUser() {
const lastUserKey = `CognitoIdentityServiceProvider.${this.clientId}.LastAuthUser`;
const lastAuthUser = this.storage.getItem(lastUserKey);
return lastAuthUser;
}
which is impossible to find if it isn't in AsyncStorage?
A second issue that I believe we discovered with someone on AWS is the response after successful authentication contains idToken, accessToken, but the refreshToken is an empty string.
@powerful23 Upon further review, there definitely appears to be something wrong. I created a brand new react native project, the refreshToken is still an empty string, and if I tinkered around in the getCurrentUser() of cognitoAuth:
CognitoAuth.prototype.getCurrentUser = function getCurrentUser() {
var lastUserKey = 'CognitoIdentityServiceProvider.' + this.clientId + '.Facebook_10154753544217255' + '.accessToken';
var lastAuthUser = this.storage.getItem(lastUserKey);
return lastAuthUser;
};
It does indeed return to me the accessToken, but asyncStorage is an empty array. And if I replaced '.accessToken' with '.refreshToken', it was also an empty string.
I can upload this to a repo, and remove my pool/client logic if you'd like to see for yourself.
I can't get more information about this.storage, but it does seem to be using window.location I believe.
@VicFrolov I see. So you are actually using it in React Native? Then it would be complicated. You need to pass a storage object to CognitoAuth to let it use the AsyncStorage because for now the amazon-cognito-auth-js would only choose memory storage or localStorage by default. You can try:
import { StorageHelper } from '@aws-amplify/core';
const storage = new StorageHelper().getStorage();
const cognitoAuthParams = {
ClientId: 'clientId',
UserPoolId: 'poolId',
AppWebDomain:
'https://my-app.auth.us-west-2.amazoncognito.com',
TokenScopesArray: [
'phone',
'email',
'profile',
'openid',
'aws.cognito.signin.user.admin',
],
RedirectUriSignIn: 'https://www.google.com',
RedirectUriSignOut: 'https://www.google.com',
IdentityProvider: 'Google',
Storage: storage // by doing this, you are passing a storage object to the CognitoAuth
};
// Also you need to pass the storage object to Amplify or amazon-cognito-identity-js to make it be shared among those libraries
Auth.configure({
storage: storage
});
You can also check this doc if you want to provide your own storage object: https://aws-amplify.github.io/amplify-js/media/authentication_guide.html#managing-security-tokens
@powerful23 Would it be possible to add React Native label to this issue?
+1 for this issue. Our mobile guys have hit the same roadblock
@powerful23 I wanted to thank you, as I can now have users sign in/sign up :)))).
One question still remains, the refreshToken? Is that expected to be returned, as I only get id/access tokens. I presume that this means after an hour, the user will have to re-sign in?
@markl-vesper just wanted to confirm that this solves the problem, even if it's temporary until Amplify allows this for RN.
tks @powerful23
Will get the app guys to take a look if they have not already
Big thanks @VicFrolov @powerful23
@VicFrolov so with above changes from @powerful23 you are able to solve the problem? Is your user getting populated to the UserPool?
@VicFrolov there are different OAuth flows for the Cognito Hosted UI. One is Authorization code grant which will return a code in the callback url and then using this code to get tokens including id token, access token and refresh token. Another one is the Implicit grant by which the tokens are included in the callback url but without refresh token.
So if you want to get the refresh token, you should use the first one.
Reference:
https://aws-amplify.github.io/amplify-js/media/authentication_guide.html#configuring-the-hosted-ui
https://docs.aws.amazon.com/cognito/latest/developerguide/cognito-user-pools-configuring-app-integration.html
Is it correct that above solution only works with Cognito Hosted UI?
Do we still need to wait for Amplify/Cognito upgrade if we want to use for example react-native-fbsdk?
Any update on the how much longer it will take to resolve this issue ! Please reply . Thank you very much :)
I get [Error: Cannot open, already sending] when trying to parse the webResponse
cognitoAuthClient.parseCognitoWebResponse(webResponse)
the response look like this "exp://localhost:19000/--/expo-auth-session?code=88ace2cc-9866-4740-b180-e3daef645f7
I use expo auth to get the code from my custom oidc from cognito.
https://docs.expo.io/versions/latest/sdk/auth-session
The users is created in the user pool , but when i try cognitoAuthClient.parseCognitoWebResponse(response) i get an error :(
Someone have the same problem?
@tobiastornros you mean you want to use react-native-fbsdk to login from Facebook and use the token to exchange for a Cognito User Pool token?
@wdevon99 I can't give a timeline for that. We are still waiting for the Cognito team to provide a sdk to login into User Pool with federated providers instead of using Cognito Hosted UI.
@mikaelwecode did you give your callback function to the user handler so we can see if the parsing failed or not?
this.cognitoAuthClient = new CognitoAuth(cognitoAuthParams);
this.cognitoAuthClient.userhandler = {
onSuccess: result => {
console.log(result, 'Sign in success');
},
onFailure: err => {
console.log(err, 'Sign in error');
},
};
@powerful23 Yes, correct.
@mikaelwecode Yes, I experience this error ONLY when using code grant, instead of tokens.
@powerful23 for the error mentioned above, I found the issue, and have most of the solution, with one last hurdle remaining:
https://github.com/aws/amazon-cognito-auth-js/blob/808e304aac9f61b4a1e8d9af0f8f5196d32f3ce8/src/CognitoAuth.js#L541
1) It is calling xhr.open(method, url, true); twice. Commenting out line 541 "solves" that error
2) For some reason the url for me is https://https://(...), so I then need to slice the string to get rid of the repetition of https://.
3) This then allows users to SIGN IN using codes, and returns all tokens including refresh.
4) The issue now however is if I log the user out, and make them sign in again, the hosted UI state now simply says "Continue with [insertProviderHere]", when they click that, errors occur. But if I say "sign in with a different provider", and then select either provider I have setup, it works fine.
Not sure what the solution to this is, but sounds like we might need to fork the repo or make a PR. I'm actually confused as to why xhr.open(method, url, true); is called before the if else sequence, which then calls it again.
@mikaelwecode @powerful23 AYYYY I got everything working! Including continue sign in :)
So
1) Commented out line 541, as the if/else seems to apply it anyway.
(https://github.com/aws/amazon-cognito-auth-js/blob/808e304aac9f61b4a1e8d9af0f8f5196d32f3ce8/src/CognitoAuth.js#L541)
2) (https://github.com/aws/amazon-cognito-auth-js/blob/808e304aac9f61b4a1e8d9af0f8f5196d32f3ce8/src/CognitoAuth.js#L273
this generates the https:// for the AppWebDomain that is set in authData. This means your AppWebDomain cannot include the https://, and should just be my-app-.auth.us-west-2.amazoncognito.com, which is very unclear in the README.
Not sure if 2 is intended, in our config we need the https for various other calls we make, so I just slice this off for the react native app.
(ps setting this.cognitoAuthClient.useCodeGrantFlow(); after instantiation seems optional, as code grant flow works if I remove it
@avichopra1989 Yes, the above adds them to cognito, and all works as expected :D
@VicFrolov Hi, can you share a simple project using this.
@VicFrolov can you help with the sample, our team is stuck in the same phase.
I think i got it. Just one question.
If i add the storage to cognitoAuthParams the token are saved in the @MemoryStorage
But Auth.currentAuthenticatedUser()
.then(user => console.log(user))
.catch(err => console.log(err))
Is still showing not authenticated
But I guess I can use this.cognitoAuthClient.isUserSignedIn() instead to check if user is logedIn or not.
So when doing api calls with amplify will now use the tokens saved in MemoryStorage? (Did not have time to try now, but will later!)
I also had to add this the code
if (!global.atob) {
global.atob = require('base-64').decode
}
it crash if i dont have it. Dont know why, but found solution from here https://github.com/SocketCluster/socketcluster-client/issues/41
And one more thing. I use the latest version of amazon-cognito-auth-js 1.2.4
Then it is line 571 and not 541 you have to comment out.
Yes, I'll be able to do it later this weekend.
@mikaelwecode
1) yes you are correct about the line, my apologies was using an old link.
2) It should set it to the same place in memory (AsyncStorage), but if it doesn't, I would erase all contents and settings in simulator, and then log a user in via Amplify. I'd then print out where it's being saved, and compare to amazon-cognito-auth-js. I didn't need to tamper with Amplify's storage, but you can definitely do that if it doesn't work for you.
Here's my fork that I want to PR, btw, if you guys want to try it out:
https://github.com/VicFrolov/amazon-cognito-auth-js
Haven't PR'd yet since I need to make sure this works with React. Not really sure why they added that line, but doesn't make any sense to me since it's being re-used again in the first if, and being re-assigned in the first else if.
@VicFrolov
woho!! It works! Just erased as you said and now it started to work!
Thanks! :D
Finally got it working!!!! Thanks :) @VicFrolov and @powerful23
If you don't want to switch code to the PR branch, this workaround also works for me (just place it in some location that's get run before any auth requests):
CognitoAuth.prototype.createCORSRequest = function (method, url) {
var xhr = new window.XMLHttpRequest();
xhr.open(method, url, true);
return xhr;
};
@VicFrolov, did you ever put together an example repo with this flow for mobile login with social that federates? I am starting a new RN app (using expo) and am wanting to signin/register a user with FB and allow the user to be provisioned for GraphQL calls using amplify api, but the way I have FB signin set up is calling .federatedSignIn() with the user but no user is created in Cognito leading to no user signed in when trying to call API mutations. If I could get past this Cognito hurdle I would be in a good place
@powerful23, are we still waiting for Cognito/Federated Identities on RNative? Any updates on when?
I am interested on the same information
I'm also looking for an update on if/when federated identities will work with cognito user pools and react native @powerful23 (without doing the workaround described above).
@VicFrolov
Your solution and fork are not working for me.
@mikaelwecode, @wdevon99
How did you solve the problem?
Anyone, does the above problem happen only with React Native or with Web as well?
Thanks to @VicFrolov and @powerful23 I was able to make the login/register work with react native. I had to read everything multiple times to figure out so here are the steps that I took in case anyone is wondering (I am assuming you do have the facebook account and user pool configured):
@Farhadkm I've found that the issue I'm having is that the cognitoAuthClient and Auth of amplify don't seem to be using the same store. Any insight on how to use { StorageHelper } would be appreciated. I see that you can set both in the storage key of the objects used to configure both. However what do you specify to make sure they both are using the same? Do you create an instance before referencing it and then pass it to both?
For the ampilify setup you can just give as a key in the config:
const awsConfig = {
Auth: {
identityPoolId: ``,
region: '',
userPoolId: `,
userPoolWebClientId: '',
storage: new StorageHelper().getStorage()
}
}
Then, when creating the cognitoAuthClient make sure to pass the same instance:
const cognitoAuthClient = new CognitoAuth( {
ClientId: awsConfig.Auth.userPoolWebClientId,
UserPoolId: awsConfig.Auth.userPoolId,
AppWebDomain: ..,
TokenScopesArray: ...,
RedirectUriSignIn: ...,
RedirectUriSignOut: ...,
Storage: awsConfig.Auth.storage
});
Thank you!
@Farhadkm How do you capture the custom UI URL? Also, do you have an example code? I'm having trouble with handling the callback after the login.
Is this still open???????????
I also really would like this to just work w/o the hosted UI w/o doing that wacky workaround
How is it possible that such a standard thing is so poorly supported... For all the problems cognito solves it easily creates just as many if not more. Really shitty tool.
Most helpful comment
Currently we are working with the Cognito team to provide the api to sign in the User Pool with federated providers without using the Hosted UI. We are not sure about when it will be implemented but we will try to make it ASAP.