Logging out the user from other computers (that he logged in before) when he logs in from another computer - asp.net-core

I have a web application that employees log in to do stuff. What Im trying to achieve is: When a user logs in from a computer, if he is already logged in on another computer, that must be logged out. The web app is MVC Asp.Net Core 2.2 Code first. I have added a signInManager in startup and edited the PasswordSignInAsync method. I login the system from two different devices. When I click something on the screen from the first computer that I loggedin, it redirects to logout. It seems like working. But Im not sure if this is the right way of doing this. The code I added is: await UserManager.UpdateSecurityStampAsync(user); Inside PasswordSignInAsync method.
Inside the startup class ConfigureServices method I added
'services.AddIdentity<ApplicationUser, ApplicationRole>()
.AddSignInManager<SignInManagerX>()'
Then in SignInManagerX class which is inherited from SignInManager I overrided the PasswordSignInAsync
public override async Task<SignInResult>
PasswordSignInAsync(ApplicationUser user, string password,
bool isPersistent, bool lockoutOnFailure)
{
if (user == null)
{
throw new ArgumentNullException(nameof(user));
}
var attempt = await CheckPasswordSignInAsync(user, password,
lockoutOnFailure);
//Here is added
if (attempt.Succeeded)
{
await UserManager.UpdateSecurityStampAsync(user);
}
//Add end
return attempt.Succeeded
? await SignInOrTwoFactorAsync(user, isPersistent)
: attempt;
}
Is this the right way ? or I should add a table to db for logins which holds the info if the user is already logged in on another Ip. Then Logging out that user from all computers if the last and current login attempt is true ?

Yes , the primary purpose of the SecurityStamp is to enable sign out everywhere.
The basic idea is that whenever something security related is changed on the user, like a password, it is a good idea to automatically invalidate any existing sign in cookies, so if your password/account was previously compromised, the attacker no longer has access.
Reference : https://stackoverflow.com/a/19505060/5751404
You can set validateInterval to TimeSpan.Zero for immediate logout .

Related

IdentityServer4 Revoke all reference tokens for a client, except the current one

I have a single page application which is protected by IdentityServer4 reference tokens.
I expect users to login to from multiple computers/devices.
In the settings area of the app, the user can change their password. To do so, they must enter their current password, as well as the new password.
I also wish to give the user the option to "Logout all other devices and computers".
If the user ticks this option, I want to invalidate any other reference tokens that exist for this client and this user, but I do NOT want to invalidate the reference token the user is currently using.
I only want it to logout other devices and computers. The user should stay logged in on the computer they are using.
For the life of me, I cannot see a way to do this with IdentityServer4. I was thinking I could simply run a delete on the PersistedGrants table, however I have no way of knowing which of the persisted grants in this table is the one the user is currently using.
Please help!
I was finally able to solve this. Make sure you're using the latest version of IdentityServer, as it includes a session_id column on the PersistedGrants table. With that, the solution is clear.
When user changes password:
if (model.EndSessions)
{
var currentSessionId = User.FindFirst(JwtClaimTypes.SessionId).Value;
foreach (var grant in db.PersistedGrants.Where(pg => pg.ClientId == "the-client-name" && pg.SubjectId == user.Id.ToString() && pg.SessionId != currentSessionId).ToList())
{
db.PersistedGrants.Remove(grant);
}
db.SaveChanges();
await userManager.UpdateSecurityStampAsync(user);
}
The user's other tokens are now revoked.
However, the user (on their other computer/devices) will likely still have an authentication cookie, so if they were to go to the authorization endpoint they would be granted a new token without having to login again.
To prevent that, we intercept the request for a new token with a CustomProfileService, like so -
public override async Task IsActiveAsync(IsActiveContext context)
{
//only run check for cookie authentication
if (context.Subject.Identity.AuthenticationType == IdentityConstants.ApplicationScheme)
{
var validationResponse = await signInManager.ValidateSecurityStampAsync(context.Subject);
if (validationResponse == null)
{
context.IsActive = false;
return;
}
var user = await userManager.GetUserAsync(context.Subject);
context.IsActive = user.IsActive;
}
}

How to sign out previous login on new login in .net core?

How to sign out previous login when user log in through another browser in .net core?
I referred to this link but confused about how to use it.
enter link description here
You simply call UpdateSecurityStampAsync on your UserManager instance with the user in question. Then sign them in. This won't automatically log out other sessions, because there's a client-side component that must come into play. However, on the next request made from another browser, the cookie there will be invalidated because the security stamp won't match, and then the user will be effectively logged out.
It worked for me doing like:
After login done:
var loggedinUser = await _userManager.FindByEmailAsync(model.Email);
if (loggedinUser != null)
{
var Securitystamp = await _userManager.UpdateSecurityStampAsync(loggedinUser);
}
and in StartUp.cs
services.Configure<SecurityStampValidatorOptions>(options => options.ValidationInterval = TimeSpan.FromSeconds(0));

Log the logins to the various applications that identityserver manages

We've got a lot of sites with common authentication by thinktecture identityserver v2.
Now we would like to have a log of the logins to the sites. We've got a custom IUserRepository where we could log a user login in, but how would we goahead and grab the site a user is loggin into?
And when we jump from one site to another - how could that be logged
In case there's no built in support for this, where is the best place to modify the code?
It seems like it could be done in the WSFederationController and in the Issue method I could get the realm based on the uri.
public ActionResult Issue()
{
Tracing.Start("WS-Federation endpoint.");
if (!ConfigurationRepository.WSFederation.Enabled && ConfigurationRepository.WSFederation.EnableAuthentication)
{
return new HttpNotFoundResult();
}
var message = WSFederationMessage.CreateFromUri(HttpContext.Request.Url);
// sign in
var signinMessage = message as SignInRequestMessage;
if (signinMessage != null)
{
// Is this a good place to log current user and the application the user is loggin into to db???
// or does Thinktecture have some build in functionaltiy for this?
return ProcessWSFederationSignIn(signinMessage, ClaimsPrincipal.Current);
}
Larsi

Laravel 4 Auth::attempt($userdata, false) authentication is still kept

Recently I have decided to add a "remember me" feature to my Laravel 4 app.
Appropriate method with syntaxis was found:
Auth::attempt(array $credentials = array(), $remember = false)
This was adopted for my needs like so:
Auth::attempt($userdata, Input::has('remember'))
Application kept the Auth session, and the user was authenticated even after browser was closed.
Although, I have found out that now Laravel always keeps a user authenticated, no matter what state "remember" checkmark is.
I have tried to do:
Auth::attempt($userdata, false)
and
Auth::attempt($userdata,)
User was still authenticated across the browser sessions!!!
Now, since Auth::attempt($userdata) not keeping the auth session, I felt that whenever there is an indications of the second argument in Auth::attempt method, Laravel auto assumes it as "true".
Can anyone clarify that?
EDIT:
To make it a super clear to everyone, I will list the steps to recreate this behaviour:
Logout of the app Auth::logout();
Login again Auth::attempt($userdata, false)
Close and open the browser
Go to the app url.
Application is loaded authenticated
This is my first question here, so please, be patient with me :)
EDIT : OP made clear he called Auth::logout() properly, so answer is edited to include the "Real" answer.
Set lifetime value in app/config/session/php to 0 to make cookie clear on browser close.
Previous answer
This is the login method in Illuminate\Auth\Guard (Which is facaded to Auth) class, which is eventually called by Auth::attempt().
source : http://laravel.com/api/source-class-Illuminate.Auth.Guard.html#263-291
public function login(UserInterface $user, $remember = false)
{
$id = $user->getAuthIdentifier();
$this->session->put($this->getName(), $id);
// If the user should be permanently "remembered" by the application we will
// queue a permanent cookie that contains the encrypted copy of the user
// identifier. We will then decrypt this later to retrieve the users.
if ($remember)
{
$this->queuedCookies[] = $this->createRecaller($id);
}
// If we have an event dispatcher instance set we will fire an event so that
// any listeners will hook into the authentication events and run actions
// based on the login and logout events fired from the guard instances.
if (isset($this->events))
{
$this->events->fire('auth.login', array($user, $remember));
}
$this->setUser($user);
}
It is clear that even though the cookie is set when $remember is set to true, the cookie itself is not cleared when $remember is set to false or other non-truthy value.
The cookie is cleared when you call Auth::logout() function.

Drools KnowledgeAgent and Guvnor: authentication fails

This one is really driving me nuts.
I have a Guvnor inside JBoss AS (more on versions later). I have edited the components.xml to enable authentication (with JAAS, I have the users and passwords set up just fine) and role based permission. I have an 'admin' user with full privileges and an 'agent' user, with read-only permissions. So far so good, in Guvnor I can see the users and proper privileges, from browsers I can login with the 'admin' user to upload rules and such, and I can download changeset.xmls and binaries with the 'agent' user.
Now, from a Java application I set up a knowledge agent. In the UrlResource, I set up the username ('agent') and password. The changeset downloads just fine, however, the changeset.xml refers to other resources (such as PKG). Downloading them fails (HTTP 401). Seems like Drools forgets about my credentials in the way.
Editing the changeset.xml by hand, and adding enableBasicAuthentcation, username and password - it works fine. But this is not the way to go, really.
I have been looking for either solution: a) see some option panel in Guvnor, so that I can set up what to embed in changeset.xml automatically when deploying packages b) find a way, so the credentials are passed around in my Java project so everything works.
Now, I tried Drools 5.1.1, 5.2.FINAL, 5.3.CR1, looked through the documentation of these versions. The only remark I found was in the 5.3 docs: "The User ID and Password in the change-set should be consistent with the requirements of the Authenticator configured in components.xml." - thank you, I understand, but how to do that? "Please refer to the "Security - Authentication and basic access" section of the "Administration Guide" for more details." I did and found nothing.
So really, what am I missing, or what am I doing wrong? Is really the only way to solve this is not to use authentication? Or edit changeset.xmls by hand at every change?
Any help would be greatly appreciated.
There's a workaround posted here
[JBRULES-3465]
https://issues.jboss.org/browse/JBRULES-3465
with the constraint it only works for one username per agent
I already got this bug while using KnowledgeAgent, It seems that Url resources are not set correctly (i'm talking about 5.1.1 version)
As a workaround, you can set up a new Agent Event Listener so force use of username/password this way (it's an inner class in my exemple):
private static final class AuthKnowledgeAgentEventListener extends
DefaultKnowledgeAgentEventListener {
private final String username;
private final String password;
private AuthKnowledgeAgentEventListener(String username, String password) {
this.username = username;
this.password = password;
}
#Override
public void beforeChangeSetApplied(BeforeChangeSetAppliedEvent event) {
// Obliged to do this to get UrlResources done correctly...
ChangeSet changeSet = event.getChangeSet();
for (Resource res : changeSet.getResourcesAdded()) {
if (res instanceof UrlResource) {
setupUrlResource((UrlResource) res);
}
}
for (Resource res : changeSet.getResourcesModified()) {
if (res instanceof UrlResource) {
setupUrlResource((UrlResource) res);
}
}
// maybe this is needed for deleted resources, i didn't check
}
private void setupUrlResource(UrlResource resource) {
if (starter.droolsAuthenticationEnabled) {
resource.setBasicAuthentication("enabled");
resource.setUsername(username);
resource.setPassword(password);
}
}
}
and then you set this event listener to you agent:
agent.addEventListener(new AuthKnowledgeAgentEventListener("myusername","mypassword"));
And that's it!