I am using Spring Boot, AngularJS and REST for my web application. I am also using JWT/oAuth token for user authentication and authorization, implying that the session is stateless.
We have a requirement wherein a duplicate session should not be allowed for a user.
I have implemented a way by which if the user logs in from any other browser of the same machine or any other machine, then a session override confirmation message will be shown.
If the session is overridden, then the first session will terminate whenever user tries to perform any activity.
For this the last token generation time for that user is maintained in the server memory.
If the incoming token generation time matches the time in memory, then access is granted, else session expired error is thrown.
When a user performs a successful logout, then the entry for that user is flushed out from the memory and the new authentication request doesn't throw the session override error.
Now what is happening is that whenever a user is not doing a successful logout and is simply closing the browser window, then the server value is not cleared.
And when user tries to log in again, then session override message is thrown, even though, (from user's perspective), there is no duplicate session.
My question is: Is there any way by which we can avoid showing the session override message when the above scenario happens but at the same time show override message if the user is really trying a duplicate session?
Are there any applications/designs available to handle such cases?
Any suggestion/idea is welcome.
I have similar case when i need to detect if user is logged out manually or by session timeout (usually they forgot to logout by just closing the browser).
What i do is utilizing the spring ApplicationListener
public class SessionAuditListener implements ApplicationListener<ApplicationEvent> {
#Override
public void onApplicationEvent(ApplicationEvent applicationEvent) {
if (applicationEvent instanceof HttpSessionDestroyedEvent) {
// do whatever it needs here
HttpSession httpSession = ((HttpSessionDestroyedEvent) applicationEvent).getSession();
}
}
}
Related
Want to logout from all other session(s) when user logs in other browser.
I am able to delete the PersistedGrants but cookies are still present.
When user refreshes the page a new access_token is generated due to refresh_token.
So basically we want only one concurrent session of user.
Any help would be appreciated.
By default IdSrv persists user session in a cookie. You can change that by implementing IUserSession and registering in DI. Doing so you get access to logged in clients within one session. Having that knowledge, you can register your custom middleware with the check: when authenticated, i.e. has auth cookie, and no other session for the same user id then ok, else handle the collision: the one who logged in earlier logs out. Just an idea, but should work. See my customization of the DefaultUserSession - backing it to Redis, used for another purpose, but should be enough to demonstrate the approach.
I have built a solution based on Identity Server 4 for a customer, and almost everything works great :) However, I have one problem that is not consistent; sometimes it works, and sometimes it fails. This is when a user is logged in, accesses the settings page and then tries to link an external login, (e.g. Facebook or Google) to the account.
The controller gets called, and the following code is executed without any problem:
public async Task<IActionResult> LinkLogin(string provider, string cancelUrl = null)
{
// Clear the existing external cookie to ensure a clean login process
await this.HttpContext.SignOutAsync(IdentityConstants.ExternalScheme);
// Request a redirect to the external login provider to link a login for the current user
var redirectUrl = this.Url.Action(nameof(this.LinkLoginCallback));
var properties = this.signInManager.ConfigureExternalAuthenticationProperties(
provider,
redirectUrl,
this.userManager.GetUserId(this.User));
return new ChallengeResult(provider, properties);
}
But the callback is not always called, and when it fails I get the following error in the logs:
Identity.Application was not authenticated. Failure message: Unprotect ticket failed
Do you have more that one server in play? If so it’s neccessary for them to use the same data protection keys and thus must have a shared data protection store.
Client app is redirecting to IdentityServer4 instance - "/connect/authorize/callback"?client_id=...".
If the user is not logged in(has no cookie) he is redirected to the Login page and on postback I can execute random logic(subscribe the user to this client - add claim to the user's claims collection with the client_id).
If the user has valid cookie there is no login screen and IdentityServer4 redirects him back to the Client app with the requested tokens in the query string (the consent is turned off).
How can I execute custom code when this event occurs?
A "login success" event is available from the Built-In Events. Unfortunately, Identity Server events are logging-oriented, so you must implement IEventSink to capture events, and you probably can't do things like show UI without interrupting the rest of the OIDC flow. This also means you can't log events unless you manually handle it in your IEventSink implementation (see the DefaultEventSink, it just serializes the event to whatever logger is registered for injection). The event is raised by the TokenRequestValidator.
First, enable "success" events in the configuration...
services.AddIdentityServer(options =>
{
options.Events.RaiseSuccessEvents = true;
});
...then look for the UserLoginSuccessEvent.
public class LoginEventSink : IEventSink
{
public Task PersistAsync(Event evt)
{
if(evt.Id.Equals(EventIds.UserLoginSuccess))
{
// do stuff
}
}
}
One of the overloads tells you whether the login was interactive by setting Event.Endpoint to UI but I don't see a way to get that from IEventSink, not really sure how it's used. You might be able to check the OIDC "Prompt" mode for None if you need that.
Depending on your setup and requirements, you could also capture an event on the client side when the challenge returns, but it sounds like you want to keep this in Identity Server.
I have a web app which is MVC 5 using OWIN cookie authentication. It also uses AngularJS from which web api calls are made. The Angular code is only invoked after the user has successfully logged in and been authenticated and everything works as you would expect.
However, can someone please explain what component (Chrome, OWIN, IIS, ...) is responsible for eventually issuing a 401 if the user has been idle for 20 or so minutes. In MVC this is not a problem because the redirect automatically re-authenticates but with web api my only option is to get the user to log back in again.
Ok so thats the first thing, who is responsible for timing the session out and giving the 401 (and can i alter it) but ALSO is there any way to get web api calls to keep the session alive so that even if the user is idle the api calls stop it timing out?
Specifically, I have implemented SignalR in a way that the signal to the client results in the client issuing an api call to refresh its data. I realise i could push the refresh but at the moment thats a bit tricky. So whats happening is the updates are occuring nicely but eventually this results in a 401 which i want to avoid.
below is an ideal flow
server logs in => creates cookie sets expiration time, passes it to client
client saves cookies & passes it with each request
server checks the cookies for validity including expiration time
if invalid or expired, server responds with 401
the cookie expiration time can be configured in the CookieAuthenticationOptions. See file ~/App_Start/Startup.Auth.cs
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
ExpireTimeSpan=TimeSpan.FromDays(365),
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/Account/Login"),
Provider = new CookieAuthenticationProvider
{
// Enables the application to validate the security stamp when the user logs in.
// This is a security feature which is used when you change a password or add an external login to your account.
OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<ApplicationUserManager, ApplicationUser>(
validateInterval: TimeSpan.FromMinutes(30),
regenerateIdentity: (manager, user) => user.GenerateUserIdentityAsync(manager))
}
});
Set your ExpireTimeSpan to the desired TimeSpan.
For web api calls that fail authentication you could catch 401's and send back a response that tells the browser it's logged out - you could pop up a login dialog or redirect to login url.
A scenario:
A user tries to login into the application, the credentials than passed to node to check if he exists, if so, some auth credential is placed to true, and the account of the user is than returned to the site, and with angular i have a service that does the following:
angular.module(myapp).service('accountService',function($http){
var account= null;
return {
authUser = function(credentials){
....
};
setaccount = function(accountObj){
account = accountObj;
...
}
}
Now it's ok if no refresh is going on, but when a refresh does happen, what is the best way to determine if a user is still logged in or not?
For example: i took a look into mean stack to see their approch, and what they do is like make the userObject exposed so i can do window.loggedinUser (That comes from a session storage of some kind?)
There are other examples i assume that deals with the run method doing something, but my question is this:
every time a user clicks refresh should the program do something in the run to determine if the user is still logged in or not (meaning dealing with server request in every refresh)?
What is the better solution to do so, a simple example will be appreciated....
For most AngularJS applications the user credentials are fetched from the server via an asynchronous HTTP request. This creates a timing problem of when AngularJS starts up and when the application knows if there is a user session.
You'll have to set the user flag to false at startup, and when the asynchronous call finishes you'll have to change it's state (if the user is logged in).
This becomes a problem if the current route is restricted to signed in users only.
The best recommended approach is to use route promises. Your accountService should provide a method that will return a promise that fetches the current user's session. If it already has it, then great use that one, otherwise it has to perform a HTTP request to get. Either way, the result should always be a promise.
This is how you should make the user session availible in your application. Via a promise on the route.