Do you want to request a bug or a feature? Feature
What is the current behavior?
I'm trying to build a Shopify app. The authentication process is complex and involves the browser reloading the app multiple times while it completes the OAuth process. There's nothing I can do about this, it's a requirement for an embedded Shopify app.
I have the flow sorted and believe I have developer authentication working (it requires a small change to Amplify). The problem is that the credentials are store in an AuthClass instance so each time the application loads it obtains new anonymous credentials. I need it to cache the credentials to local storage and re-use those credentials until I replace them using federatedSignIn() at which point it should continue using the authenticated credentials .
What is the expected behavior?
Store auth credentials in local storage so if the page is reloaded it case re-use the existing credentials without obtaining new ones unless they are expired.
Which versions of Amplify, and which browser / OS are affected by this issue? Did this work in previous versions?
Amplify 0.26
They are planning a plug-in system for OAuth support. See #239
@buggy thanks for the use case! Yes we are working on Auth this (and likely next) sprint. This is a great use case and I'm familiar with Shopify apps/api as well. I believe we already have this particular use case covered (caching until expire). I'll leave this one open as well towards the Auth plugin.
@jonsmirl Fancy meeting you here :)
@mlabieniec It caches the credentials in a class instance. Because the application is reloaded multiple times during the process this isn't enough. It needs to cache credentials in local storage or you get a new set of unauthenticated credentials each time it loads.
Below is a description of how I'm handling OAuth. It implements the Authorization Code Grant flow in a SPA without exposing anything to the client they shouldn't already have. It might help with the plugin architecture but it also might not as typically SPA's use the Implicit Grant flow.
/login. If there is a valid shop query string parameter then skip to step 3./callback with the usual OAuth callback parameters./callback along with the token from step 3 and calls another GrahpQL API to obtain a session token.Auth.federatedSignIn("developer", sessionToken, {});The token from the first call is a JWT that contains the OAuth nonce and any other information I need to complete the OAuth process later. It's signed with a secret so I can validate it was generated by my server when it is sent back later.
The second API uses getOpenIdTokenForDeveloperIdentity() then passes the openIdToken and token back to the application.
Finally, I needed to modify setCredentialsFromFederation() in src/Auth/Auth.ts by adding 'developer': 'cognito-identity.amazonaws.com' as a domain. <-- This appears to be the only change required to allow developer authenticated identities.
This approach keeps the interaction with Shopify inside my API so the client is never given the OAuth secret and I can get an access_token for my back end that can be refreshed. 馃帀
If I can help with the plugin architecture the please let me know.
Thanks @buggy we will review all of this as part of the feature. What I meant by "we have it covered" was we in the upcoming feature design (nothing that is actually pushed yet).
Hi @buggy I'm trying to achieve a similar thing, but with Strava.
I'm using getOpenIdTokenForDeveloperIdentity() in a lambda that returns {IdentityId: '...', Token: '...'}.
You mentioned passing in sessionToken to Auth.federatedSignIn. Do I need to do another transform after getOpenIdTokenForDeveloperIdentity(), or do I just pass in the token from the lambda response?
Any help would be greatly appreciated.
@stevepsharpe see my comment on #416
@stevepsharpe @buggy I am doing a very similar thing, also with Shopify. I got to the same spot (passing the return from getOpenIdTokenForDeveloperIdentity() to the client app, but also am not certain what to do next.
After reading the comment on #416 it seems like I would just pass the whole response object from getOpenIdTokenForDeveloperIdentity() in to Auth.federatedSignIn()
// server validates the user is authorized
// the server calls getOpenIdTokenForDeveloperIdentity()
// and sends the response back to the client
let devAuthToken = {
IdentityId: 'us-east-1:xxxxxxx',
Token: 'xxxxxxxx' // big string here
}
let userObj = {};
Auth.federatedSignIn('developer', devAuthToken, userObj);
Do I have that right?
@jordanskole did you figure this out? I'm also looking for a working solution / demo to use Developer Authenticated Identities with aws-amplify and a federatedSignIn. Is this even supported at the moment ?
@steffenmllr, I didn't. I think I reached a similar conclusion as @buggy and am now using JWTs with a custom authorizer for protected routes w serverless.
JWT verifies the token, checks the dynamo "users" table and then builds the resource policy.
This should be available now with the latest merge. Please re-open if you feel it doesn't address the issue.
I landed in this thread looking for a solution to my issue which matches the title of this thread. I am testing Amplify library with Angular 6, Cognito User Pools, aws-amplify 1.0.0 and aws-amplify-angular 1.0.0, I can signIn just fine using
this.amplifyService.auth().signIn(user, password)
But when I refresh my chrome browser, my credentials info are gone and I have to signIn again, it seems like credential information is not saved in local storage. I also tried to run these lines:
this.amplifyService.auth().currentCredentials()
.then(credentials => {
this.awsPersonalCreds = this.amplifyService.auth().essentialCredentials(credentials);
console.log(this.awsPersonalCreds);
// I get valid accessKeyId, sessionToken, secretAccessKey
this.amplifyService.auth().currentSession()
.then(currentSession => console.log('currentSession= ' + currentSession))
.catch(error => console.error(error));
// I get an error: no current user
this.amplifyService.auth().currentUserPoolUser()
.then(currentUser => console.log('currentUserPoolUser= ' + currentUser))
.catch(error => console.error(error));
// I get an error: No current user in userpool
this.amplifyService.auth().currentAuthenticatedUser()
.then(currentAuthUser => console.log('currentAuthUser= ' + currentAuthUser))
.catch(error => console.error(error));
// I get an error: not authenticated
})
.catch(error => console.error(error));
I am expecting not to have to enter credentials every time I refresh my browser until credentials expires and this is not currently happening.
Any help is greatly appreciated.
@gustavo-rios Hi in fact Amplify caches the current session which includes the tokens you get from cognito and use those tokens to get the credentials. So can you look into your localStorage when you are signed in to verify if there are tokens stored by Cognito, The key prefix of those items starts by Cognito.
@powerful23 Thanks for checking into this. They are not there, no key prefix starts with Cognito, I did another test where I am using the following snipet:
import { CognitoUser } from 'amazon-cognito-identity-js';
const cognitoUser = new CognitoUser({
Username : user,
Pool : this.getUserPool()
});
cognitoUser.authenticateUser(authenticationDetails, {
newPasswordRequired: (userAttributes, requiredAttributes) => {
callback(`User needs to set password.`, null);
},
onSuccess: (session) => {
callback(null, session);
}
With this, I can signIn and I do see CognitoIdentityServiceProvider keys in local storage one for each session key, idToken, accessToken, refreshToken, clockDrift, this doesn't happen for me with Amplify.
I was able to get the information I need from local Storage, I basically cloned my project and started over, somehow now when I sign-in session information is showing up in local storage, all good. Thanks!