AWS Cognito + aws-amplify: session state always keep user logged in? - amazon-web-services

I'm using AWS Cognito and aws-amplify to manage user authentication. When I load up my app, I call Auth.currentSession() which seems to always return the user I was logged in as if I do not explicitly log out by calling Auth.signOut().
I'm fine with this should the user choose a "keep user logged in", but if they don't, how would I go about making sure the user gets logged out once they leave the app?
I tried adding an event listener in my login() method but that didn't work i.e. the user was still logged in when I returned to the app:
.
.
.
if (!keepSignedIn) {
window.addEventListener('unload', function(event) {
Auth.signOut();
});
}

I'm pretty sure the logout() method creates a promise - it operates asynchronously. So the page is probably being destroyed before the promise's logout code is executed.
You can confirm this by executing console.log(Auth.signOut());. If it's a promise it'll log Promise { <pending> }
There's no way to halt unloading of the page, as that would be bad if we could.
What you need is a synchronous signout function. Fortunately, you can just clear the browser local storage, which is a synchronous operation. (Local storage is where Amplify stores the auth tokens.)
if (!keepSignedIn) {
window.addEventListener('unload', function(event) {
localStorage.clear();
});
}
Depending on your situation you may need to instead find and remove individual local storage items, instead of clearing them all.

You can clear Amplify cache before Auth.signOut()
import AmplifyCache from '#aws-amplify/cache';
AmplifyCache.clear();

Related

Can't use Auth.userAttributes with federated sign in

In my app, users are only allowed to sign in using google. However, I need to keep track of which users are considered "admins" in my system, and allow admins to promote other users to also be admins. For this, I have a custom attribute in my Cognito user pool called 'admin'.
However, when I tried using Auth.userAttributes, I ran into the error that it expects a CognitoUser object, and Auth.currentAuthenticatedUser isn't returning a CognitoUser object. After some researching, it looks like it just returns whatever it gets from the federated sign in process. Looking at my user pool, it would seem users also aren't being assigned anything at all for my custom attribute.
I'm thinking I may need to use lambda triggers? But which triggers do I use, and how do I get Auth.currentAuthenticatedUser to return a CognitoUser object? I'm thinking I'll at least need a trigger to check if a user is signing in for the first time, and to set the admin attribute to 0. But then do I use Auth.signIn? Also, from https://github.com/aws-amplify/amplify-js/blob/a047ce73/packages/auth/src/Auth.ts#L1191, the source code for Auth.currentAuthenticatedUser, it looks like it only looks for the user in the user pool if the cache doesn't have any entry for 'aws-amplify-federatedInfo', which is added when I use Auth.federatedSignIn. So do I need to clear that?
Thanks in advance. Any info or advice is appreciated.
Tricky! I agree this should be easier. I'd leave the cache alone. Try bypassing the cache.
import { Auth } from 'aws-amplify';
Auth.currentAuthenticatedUser({
bypassCache: true // If set to true, this call will send a request to Cognito to get the latest user data
}).then(user => console.log(user))
.catch(err => console.log(err));
Why? currentAuthenticatedUser() is returning a FederatedUser when this is somewhat helpful because the ideal response would be a CognitoUser. bypassCache alters the response of currentAuthenticatedUser().
May your app bring great felicity!

Auth0 - session cookie delete on logout

We have to give our users the option to sign out of our application. This would require them to log back in for further use. However, it appears the auth0 session cookie is not deleted for some reason when implementing the https://YOUR_AUTH0_DOMAIN/v2/logout?returnTo=http%3A%2F%2Fwww.example.com.
Even though the redirect works, the user is automatically logged back in after calling webAuth.authorize(); while you would expect to be asked to re-enter your credentials.
Calling this function for the first time, the user is required to enter username and password. However, they are never required again until the token expires.
Unfortunately, even the examples provided (via download section) do not address this. Wondering if this is even possible but it seems like the Auth0 website itself handles it correctly.
Here is the code example:
var logoutBtn = document.getElementById('vwLogoutBtn');
logoutBtn.addEventListener('click', logout);
function setSession(authResult) {
// Set the time that the access token will expire at
var expiresAt = JSON.stringify(
authResult.expiresIn * 1000 + new Date().getTime()
);
localStorage.setItem('access_token', authResult.accessToken);
localStorage.setItem('id_token', authResult.idToken);
localStorage.setItem('expires_at', expiresAt);
}
function logout() {
// Remove tokens and expiry time from localStorage
localStorage.removeItem('access_token');
localStorage.removeItem('id_token');
localStorage.removeItem('expires_at');
webAuth.logout({
returnTo: 'http://staging.myproject.com/prototype/home.html',
client_id: AUTH0_CLIENT_ID
});
displayButtons();
}
function displayButtons() {
if (isAuthenticated()) {
getProfile();
} else {
//You are not logged in
webAuth.authorize();
}
}
handleAuthentication();
});
We have also tried using: https://YOUR_AUTH0_DOMAIN/v2/logout?returnTo=http%3A%2F%2Fwww.example.com
However, every time the user logs out and it hits the login page, the user is automatically logged back in.
Any help/guidance is greatly appreciated. Thank you,
After a lot of tests I am actually able to answer my own question LOL
Problem was that the logout (session cookie delete) in combination with the re-login happened too fast. Putting a delay on calling webAuth.authorize() showed that the user is successfully logged out.
You certainly don't want to put a delay on this function. In my case I am now forwarding to a "Logged out" page that also offers the option to log back in.

Using ember-simple-auth when the login page is on another system

The page to login to our application is a jsp hosted on another machine. I have managed to get requests firing to this machine by modifying authenticated-route-mixin by allowing window.location.replace to be called if the route start with http.
beforeModel(transition) {
if (!this.get('session.isAuthenticated')) {
Ember.assert('The route configured as Configuration.authenticationRoute cannot implement the AuthenticatedRouteMixin mixin as that leads to an infinite transitioning loop!', this.get('routeName') !== Configuration.authenticationRoute);
transition.abort();
this.set('session.attemptedTransition', transition);
debugger;
if (Configuration.authenticationRoute.startsWith('http')) {
window.location.replace(Configuration.authenticationRoute);
} else {
this.transitionTo(Configuration.authenticationRoute);
}
} else {
return this._super(...arguments);
}
}
This is working but when I am redirected back to my application, ember-simple-auth thinks I am no longer logged in and redirects be back to the remote machine, which then sends me back to the application in an infinite loop.
Obviously I need to set something to let ember-simple-auth know that it it is actually logged in. Why is it not doing this automatically? What am I doing wrong?
I am pretty new to oAuth so I could be missing some basic setting here.
Here is the URL.
ENV['ember-simple-auth'] = {
authenticationRoute: 'https://our-server.com/opensso/oauth2/authorize?client_id=test-client-1&response_type=code&redirect_uri=http%3A%2F%2Flocalhost%3A4200%2Fsecure'
};
Instead of modifying the AuthenticatedRouteMixin, I'd recommend handling your app-specific login in an Authenticator-- the key configuration primitive that Ember Simple Auth provides as part of its public API.
To the best of my understanding, on first loading the app, and checking to see if a user is authenticated, Ember Simple Auth will use the restore method, defined as part of the Authenticator API.
You can return a promise from restore that resolves or rejects to indicate whether the user is authenticated. How you check this is an implementation detail of your auth system.
I don't know how you're storing credential(s) on the client (would be great if you could provide more detail), but here's an example flow, using cookies for authentication:
Ember boots, ESA attempts to restore the session.
restore makes a simple AJAX request to a secured, "dummy" resource on your Java server-- and checks if it gets back a 200 or a 401.
We get a 401 back. The user isn't authenticated, so reject in the Promise returned from restore.
Let ESA redirect the user to your authentication route. Ideally, don't override the AuthenticatedRouteMixin-- instead, use the beforeModel hook in the authentication route to send users to your JSP login page.
The user correctly authenticates against the JSP form.
In its response, your Java server sets some kind of encrypted, signed session cookie (this is how it generally works with Rails) as a credential. In addition, it sends a redirect back to your Ember app.
Ember boots again, ESA calls restore again.
restore pings your Java server again, gets a 200 back (thanks to the cookie), and thus resolves its Promise.
ESA learns that the user's authenticated, and redirects to the 'route after authentication'.
Keep in mind that, at its core, ESA can only indicate to the client whether the backend considers it 'authenticated' or not. ESA can never be used to deny access to a resource-- only to show something different on the client, based on the last thing it heard from the backend.
Let me know if any of that was helpful.

Ember Simple Auth different redirects after authentication

I'm using Ember simple auth in my app and it's working great, but I've run into a scenario that I'm having trouble getting around.
The library lets you specify the route to redirect to after a successful authentication by overriding routeAfterAuthentication: 'index'. This is working fine, however, I'm finding myself in a situation where I want to have two different types of redirects. When a user first logs in, I want them to go to /dashboard, but when they first sign up and authenticate, I want them to go /settings.
I was hoping to be able to do something like this after successfully creating an account, but it's still trying to use the routeAfterAuthentication option for the transition:
var _this = this;
this.set('identification', _this.get('email'));
this.set('password', password);
this.send('authenticate', function() {
_this.transitionToRoute('settings');
}, function() {});
Is there a way to specify which route to transition to after authenticating on one-off basis? Maybe there's a better way to log someone after they create an account without needing to go through the authenticate() method?
You can simply override the sessionAuthenticated method in the application route and implement your own logic. Beware though that the default implementation will not always transition to routeAfterAuthentication-- if there's a previously intercepted transition stored in the session, sessionAuthenticated will retry that instead.

Emberjs redirecting after login from previous url

I have an app that you can only get in if you are log in. I would like to do a redirection after the user is log but, only if he tried to access a page from the website.
Example :
The user receive this link in his email :
http://mywebsite.com/#/enquiry/1
When he click on it, it will be redirected on the login form because he's not log in. But just after the log in is good he is automatically redirected to this last link.
I already have the redirect from anywhere to the login form if he is not log but I have no idea how to save the URL and go back there again..
I used the redirection like this :
App.MyRoute = Ember.Route.extend({
redirect: function() {
if ( not log in ) {
// TransitionToLogin
}
}
});
Someone know how I can achieve this ?
Thank you !
Quick explanation if you're familiar with ember and routing:
You can do this by using the beforeModel(transition) { ... } hook on the route to see if you are logged in. If you're not logged in, stash the transition variable somewhere, temporarily transition to your login route, then when you've successfully authenticated, .retry() the stored previous transition.
Make sure bullet proof this, as your auth code is pretty "global" on your site. Handle the case where the user goes straight to login, and thus the login route doesn't have a stashed transition available. Also handle weird cases, like if they go to the login page twice, by cleaning up your temp storage for the transition before you call .retry() on it.
An alternative, use Torii:
If you don't want to build this yourself and have to copy this code on all your protected routes, then the Torii authentication library has support for this already, using their protected route mapping function. Once you've got your session and provider set up, you simply use the this.authenticatedRoute('my-protected-route'); route mapping function instead of the normal this.route('my-route'); that you've already been using.
The Torii docs are a little challenging to get through, but once you've figured them out, that library is very useful in getting up and running quickly with multiple authentication mechanisms. It is very little work to start supporting both an email/password and a Facebook login, for example. I recommend checking it out.
Non-Torii code sample:
If you just want to use vanilla Ember, and implement this yourself, I'd provide a code sample for your example, but as ppcano mentioned in comments, this is covered in the documentation. Here's the corresponding text from the docs. It covers everything I mentioned above in the quick explanation.
STORING AND RETRYING A TRANSITION
Aborted transitions can be retried at a later time. A common use case for this is having an authenticated route redirect the user to a login page, and then redirecting them back to the authenticated route once they've logged in.
// app/routes/some-authenticated.js
export default Ember.Route.extend({
beforeModel(transition) {
if (!this.controllerFor('auth').get('userIsLoggedIn')) {
var loginController = this.controllerFor('login');
loginController.set('previousTransition', transition);
this.transitionTo('login');
}
}
});
// app/controllers/login.js
export default Ember.Controller.extend({
actions: {
login() {
// Log the user in, then reattempt previous transition if it exists.
var previousTransition = this.get('previousTransition');
if (previousTransition) {
this.set('previousTransition', null);
previousTransition.retry();
} else {
// Default back to homepage
this.transitionToRoute('index');
}
}
}
});
If you are using ember-simple-auth, then take a look at AuthenticatedRouteMixin, it forces authentication before transitioning to the target route.
It also takes care of redirecting to the previous URL after authentication.
If you are using this mixin and also overriding sessionAuthenticated(), then make sure you have this._super(...arguments); in sessionAuthenticated() to preserve the redirection logic.