I have a use case where I would like to globally sign-out a user from all devices/clients if a user changes their password through the "forgot password" flow. Presumably, this wouldn't be an issue if a user were to "change password", as I could assume a user is already authenticated and thus call Auth.signOut({ global: true }).
It doesn't appear that the [ForgotPassword][1] component from aws-amplify-react does this. Opening a browser and logging in, and opening another browser and confirming a new password doesn't invalidate the session in the first browser.
I've tried hooking into the Cognito UserConfirmation trigger and calling adminUserGlobalSignOut based on the trigger source.CloudWatch logs indicate that adminUserGlobalSignOut is resolving successfully, but there is no difference client-side. Auth.currentSession() and Auth.currentAuthenticatedUser() both return without errors.
Interestingly, I see that network calls to https://cognito-idp.us-east-1.amazonaws.com/ are returning with a 400 status and {"__type":"NotAuthorizedException","message":"Access Token has been revoked"}, but I am unsure how I would hook into that.
I am wondering if anyone else has come across this issue, or have a work-around.
The issue is that when you are calling Auth.signOut({ global: true }) you are not signed in. After the forgotPasswordSubmit call, simply call signIn with the new password, and then signOut global.
Related
I am managing performing a login and the login state using Vuex. It is a standard procedure of:
User submits their login details on a form
The form dispatches a Vuex action to make an axios API call to a Node/Express endpoint /api/login with the credentials
The response from the API if successful will send back the user's data which is stored in the state.user object through a setUser mutation. A flag of state.user.isLoggedIn is also set to true.
A JWT access token is sent to the user's browser and stored in a secure cookie to use over and over again until it expires. If it expires, a refresh token is used to generate a new JWT access token.
The site's header displays the user's login information .e.g name
The above all works as expected. However if the user refreshes their browser, then Vuex state gets emptied and the site header no longer shows the user information. To overcome this I stored state.user into localStorage in the browser as persisted data and repopulate the Vuex store using the data in localStorage on page refresh. If the user logs out, the localStorage is cleared.
There are many routes that require checking if the user is logged in. I cannot rely on localStorage because it can be manipulated by the user. If the user deletes their cookies which contains a JWT, then localStorage is not being emptied and the site header still displays their log-in information. Therefore I need to do an API call on almost every page to check if the user is logged in and if not then to delete the localStorage as well.
My question is:
Where do I perform a log-in check every time a page/view is accessed? Is it whenever the Vue app is mounted or within vue-router using the beforeEach navigation guard?
If I use beforeEach in vue-router, is it going to crash the site by making an API call to /api/login every time a user changes route? Is it normal practice to do this on every route?
Are there any other alternative patterns to keeping track of if the user is logged in or not?
Application decisions like this tend to be subjective because every application is different, with different requirements and different architectures. It sounds like this is a single page application, so I'll go from there.
First, the fact that your JWT is set up and working is great - that can be a lot of hard work, so be proud that you made it that far.
If your application has certain pages that are accessible without first being logged in (like a login page, or an access denied page, etc.), then you have to consider that in the design as well. In this case, a beforeEach route guard is the perfect solution to keep a route from loading or to catch those not-logged-in users before they attempt to request content that will likely just give them an error.
You probably shouldn't have to make an API call to the server each time the user navigates to a new page. That would probably be a little slow. Because your application uses JWT, complete with refresh tokens, you should be able to rely on them, so that if a user is logged in, they will continue to be logged in until they close their browser and walk away.
This is why you should not use localStorage. When a tab closes or even after a reboot, the user will still appear to be logged in. Instead of using localStorage, use sessionStorage - Helpful MDN link.
Now, the login event to retrieve the user object should only happen once per browser tab, but stay active during the visit.
I wonder why you don't just request a new user object from the server when the browser is refreshed. It's not strange to force a re-check to the server for a full page load. Doing that would mean you wouldn't have to use anything outside of Vuex to store the user's state
Next, is there a way to check the validity of their JWT during the navigation event? Depending on the way you handle the JWT, you may have access to it, and can possibly decode it (like through the oidc-client-js library), and that way determine for yourself if the token is expired. However, if the token is expired, your token refresh system may just refresh it and not tell you about it.
You can also watch your HTTP requests and look out for 401 or 403 responses (however your back-end handles logged-out users), and direct the user back to the login screen if you see one of those. You can use Axios' global interceptors to catch them, or do it yourself if you centralized the location from where Axios is called.
Overall, you're on your way, and clearly have a good grasp on this. Great progress so far, having done probably 90% of the heavy lifting already.
I've tried following the sample code on the passport-saml site, and the advanced tutorial on the react-admin site for OAuth, but haven't been able to figure out what I need to do with the authProvider to get an authenticated session available in react-admin using SAML.
I can currently log into my app through OneLogin (clicking on the app in the panel) and write out the user's information (inside the passport.serializeUser function), so I know that piece is working, but I'm not sure how to get that information over to the authProvider.
The login function on authProvider is hit when you submit the form, so if I could replicate what OneLogin is sending over when I click on the app, I could probably make that call in authProvider.login and make a custom login page that submits on load rather than waiting for a submit, but that doesn't seem intentional.
What am I missing here, and is there a better option that I'm not considering?
I ended up writing a getUser function on the server and using the authProvider.login function to hit that endpoint, parse the user data off of the response, and store it in localStorage. Then logout removes the user from localStorage, and checkAuth just gets the user from localStorage.
This is working for my purposes at the moment, although eventually I will want to expire the users. Hope this helps anyone else trying to hook up OneLogin with React-admin.
I would like to check the user is login or not.
At the first time to develope this function, I supposed to use django is_authenticated value but there were many problem with structure with vue and django.
So now I use with crsftoken, The crsftoken is saved in cookie.
So when I click logout button, I detroy crsftoken in document cookie.
And after login, the crsftoken is added to cookie.
Therefore I can check whether the user login or not
But I am considering is it really OK with removing just while logout user.
Cloud functions doesn't accept onLoging by default, how can i "bypass" this limitation so i can run a cloud function every time a user gets "online".
My thought so far is to run a create document on logIn and listen to it, but what happens when the user is not login in but using his last time session?
Do i run the same post request on the isLogged function ? that function can be run many times when the application is running, so is not optimal.
is running the function on app init (angular 7) the solution here?
Only you can define what a "session" is for your app. There is no universal definition that you can use to trigger a Cloud Function. Firebase Authentication won't help with this either, since users are logged in "forever", until your code explicitly logs them out (it automatically refreshes the user's auth token every hour).
You're going to have to write your own code to figure this out by whatever specification you decide the user has "logged in" or "logged out".
You can create a function that triggers when a Firebase user is created using the functions.auth.user().onCreate() event handler:
exports.sendWelcomeEmail = functions.auth.user().onCreate((user) => {
// ...
});
https://firebase.google.com/docs/functions/auth-events?hl=en-419
but one alternative you have would be , to make a write operation in the client when user login.
My web application required user after registered got logged in for once, and if user logout he must activate his account by a ConfirmationToken. I found that the requireConfirmationToken:false lets you login after registration but you will loose the activation, and vise versa.
bool isLoggedIn = WebSecurity.Login(registerModel.Username, registerModel.Password);
// isLoggedIn false if requireConfirmationToken:true & true if requireConfirmationToken:false
any suggestions ?
This is rather easy to do, you simply assume the user is valid if they successfully register and issue a FormsAuthentication ticket for them with a non-persistent cookie.
FormsAuthentication.SetCookie(username, false);
This will make them authenticated after they are registered (well, after the next page refresh), until they close their browser or log out. Afterwards, logging in will fail until they activate their account.
If you set requireConfirmationToken to true you have to use email confirmation to have the user confirm that they have provided a valid email address. During the process of email confirmation you call the WebSecurity.ConfirmAccount method. Until the account is confirmed the login will fail every time. You can read more about setting up email confirmation here.