Login as different user in identity server 4 - identityserver4

I am trying to implement the LoginAs/Login as a different user feature using Identity Server 4. My Client Application is developed on top of Angular. I am trying to sign out the existing logged-in user and sign in the user using the provided userId. Maybe I am going the wrong way. Any solutions for that? Here is my code for Identity Server 4.
[HttpPost("login-as")]
public async Task<IActionResult> LoginAsAsync([FromBody] LoginAsViewModel model)
{
await HttpContext.SignOutAsync();
var user = await userManager.FindByIdAsync(model.UserId);
if (user == null)
{
return BadRequest();
}
await signInManager.SignInAsync(user,false);
return Redirect("/connect/authorize");
}

Related

Login page customized depending on client

I would like to make the login page know which client requested the login in order to display some client-specific branding: Otherwise the user may be confused as to why he's redirected to this foreign login page on a different domain. A client logo will help reassure him that he's still on the right track.
What would be the most reasonable approach to get at that information?
EDIT: Note that by "client" I'm referring to the client web applications on whose behalf the authentication happens - not the user's browser. All clients are under my control and so I'm using only the implicit workflow.
To make this even more clear: I have client web apps A and B, plus the identity server I. When the user comes to I on behalf of B, the B logo should appear as we're no longer on B's domain and that may be confusing without at least showing a B-related branding.
Some Theory
The easiest way to get the ClientId from IdSrv 4 is through a service called IIdentityServerInteractionService which is used in the Account Controller to get the AuthorizationContext. And then follow that up with the IClientStore service that allows you to get the client details given the ClientId. After you get these details then its only a matter of sending that info to the view for layout. The client model in IdSrv 4 has a LogoUri property that you can utilize to show an image at login per client.
Simple Example
// GET: /Account/Login
[HttpGet]
[AllowAnonymous]
public async Task<IActionResult> Login(string returnUrl = null)
{
var context = await _interaction.GetAuthorizationContextAsync(returnUrl);
if (context?.IdP != null)
// if IdP is passed, then bypass showing the login screen
return ExternalLogin(context.IdP, returnUrl);
if(context != null)
{
var currentClient = await _clientStore.FindClientByIdAsync(context.ClientId);
if (currentClient != null)
{
ViewData["ClientName"] = currentClient.ClientName;
ViewData["LogoUri"] = currentClient.LogoUri;
}
}
ViewData["ReturnUrl"] = returnUrl;
return View();
}

Is user has active session on IDMsrv?

How to verify IDM does it have an active session for the user signing in?
details - If user'A' has a active session on IDM from browser 'X', When the same user 'A' try to login using browser 'Y', expected behavior identify that user has active session and invalidate the browser'X' session.
Background-
IDM with aspnetIdentity
Client with Implicit grant
(30 sec identitytoken life, does kept renewing access token silently without going to login page, expected to hit some method on the IDM then I can verify user has access or not)!!
Brock has already mentioned about it, It should be at the time of login and logout
It make sense,why its not in Idm. but its definitely possible to provide this as an enhanced feature at least in the coming versions.
Profile Service, IsActive method is the one hit by authorize and
tokenvalidation end point.
so at the time of login persist session, then when the above code hits do the check as per business requirement.
as long as the session is active ( cookie life time) the silent authentication will be passed with the application logic. so this can be controlled by cookie lifetime as well.
public override async Task IsActiveAsync(IsActiveContext context)
{
var sub = context.Subject.GetSubjectId();
var user = await userManager.FindByIdAsync(sub);
//Check existing sessions
if (context.Caller.Equals("AccessTokenValidation", StringComparison.OrdinalIgnoreCase))
{
if (user != null)
context.IsActive = !appuser.VerifyRenewToken(sub, context.Client.ClientId);
else
context.IsActive = false;
}
else
context.IsActive = user != null;
}
signin
public async Task<IActionResult> Login(LoginInputModel model)
{
if (ModelState.IsValid)
{
// To enable password failures to trigger account lockout, set lockoutOnFailure: true
var result = await _signInManager.PasswordSignInAsync(model.Email, model.Password, model.RememberLogin, false);
if (result.Succeeded)
{
//Update security stamp to invalidate existing sessions
//TODO: This didn't invalidate the existing cookie from another client
//var test= _userManager.UpdateSecurityStampAsync(_userManager.FindByEmailAsync(model.Email).Result).Result;
appUser.PersistSession(new UserSession
{
CreatedOn = DateTimeOffset.Now,
DeviceUniqueId = GetDeviceId(),
UserId = _userManager.FindByNameAsync(model.Email).Result.Id,
SId = httpContext.HttpContext.Session.Id,
ClientId= httpContext.HttpContext.Request.QueryString.Value.GetClientIdFromQuery(),
ExpiresOn = DateTimeOffset.Now.AddMinutes(appSettings.SessionTimeOut)
});
_logger.LogInformation(1, "User logged in.");
return RedirectToLocal(model.ReturnUrl);
}
This method has a few drawback when IIS gets restarted and if user has not signed out properly.
there may be a better options this is not the best fit.!
Update:
refer here duplicate/similar question
idmsrv endpoints are missing security change check
Issue raised
Should be like this #tibold

identityserver4 Quickstart LoginViewModel IsExternalLogInOnly flag

I am using Identityserver with multiple external authorities(providers). The scenario which I am trying to get here is I have a client configured with "EnableLocalLogin" as false. I do have multiple external providers. The below code line in the "LoginViewModel.cs" in the quick start is not making sense.
public bool IsExternalLoginOnly => EnableLocalLogin == false && ExternalProviders?.Count() == 1;
This is returning false and I am not getting redirected to external provider. Should this be ExternalProviders?.Count() > 0
In my opinion, IsExternalLoginOnly is not well named. it is called only when you show the login page :
[HttpGet]
public async Task<IActionResult> Login(string returnUrl)
{
var vm = await _account.BuildLoginViewModelAsync(returnUrl);
if (vm.IsExternalLoginOnly)
{
// only one option for logging in
return await ExternalLogin(vm.ExternalProviders.First().AuthenticationScheme, returnUrl);
}
return View(vm);
}
It is used to directly redirect to a provider in case the user has no choice about it.
Now in your case, you have multiple external providers and you have to ask the user which one to use. You can not automaticly pass this step as long as your client allows multiple providers
You can still code your own login and try to automate this step following the returnUrl

Two factor authentication using identity server 4

How to implement a two factor authentication using Identity Server 4? The token end point returns a token with a username and password / client credentials.
Can we customize those end points?
Both the methods as per the sample does not allow to customize the end point:
> var tokenClient = new TokenClient(disco.TokenEndpoint, "ro.client", "secret");
> var tokenResponse = await tokenClient.RequestResourceOwnerPasswordAsync("brockallen#gmail.com",
> "Pass123$", "api1");
Is it possible to achieve 2 factor authentication using either asp.net identity Or EF Core implementation?
This shouldn't be a problem at all. When a user is redirected to the Identity Server for login in, if 2FA is enabled then he/she would have to enter the authenticator's code before the Identity Server returns the response back. I have created a repository and blog post series that explain in detail the related concepts. In the AccountController of the IdentityServer you have to check if 2FA is enabled and ask the user to proceed by providing an authenticator code before returning the response.
var signInResult = await _signInManager.PasswordSignInAsync(model.UserName, model.Password, true,
lockoutOnFailure: false);
if (signInResult.RequiresTwoFactor)
{
result.Status = Status.Success;
result.Message = "Enter the code generated by your authenticator app";
result.Data = new {requires2FA = true};
return result;
}
You will also need a TwoFactorAuthenticationController that supports all the 2FA tasks (enable/disable 2FA, sign in with authenticator code/recovery tokens, reset authenticator, etc...)

How do I renew the timeout of my ApplicationCookie in Identity 2.0

Is there a way to renew the authentication timeout of a cookie as part of a particular web request? I have an Angular app on top of my MVC 5 project and I need it to keep my server session alive in between requests. I've got the Angular part working, but it appears that hitting a URL on my server is not sufficient to reset the Auth timeout. I am new to Identity so I am probably missing something simple?
My Startup.Auth.cs code:
public void ConfigureAuth(IAppBuilder app)
{
// Configure the db context, user manager and signin manager to use a single instance per request
app.CreatePerOwinContext(ApplicationDbContext.Create);
app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
app.CreatePerOwinContext<ApplicationSignInManager>(ApplicationSignInManager.Create);
// Enable the application to use a cookie to store information for the signed in user
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
ExpireTimeSpan = TimeSpan.FromSeconds(30),
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(20),
regenerateIdentity: (manager, user) => user.GenerateUserIdentityAsync(manager))
}
});
}
And my simple method (authorization is set up globally for all requests that do not have [AllowAnonymous]):
[HttpGet]
public HttpResponseMessage KeepAuthAlive()
{
// Renew Auth Cookie - how?
}
Re-SignIn the User (this code assumes you have UserManager and SignInManager available as per the default Account controller and an async ActionResult). I haven't tested this, but it should hopefully work:
ApplicationUser user = await UserManager.FindByIdAsync(User.Identity.GetUserId());
if (user != null)
{
await SignInManager.SignInAsync(user, isPersistent: false, rememberBrowser: false);
}

Resources