Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to do multiple-step login in IdentityServer4?

We were using IdentityServer3, implicit grant and the login consists of multiple screen. In IdentityServer3, there's built in support for such multiple step login workflow (for example for accepting EULA, two-factor login, etc.), The feature called "partial login" and there is even an example: https://github.com/IdentityServer/IdentityServer3.Samples/tree/master/source/CustomUserService/CustomUserService

We've recently upgraded to AspNetCore and IdentityServer4 and wondering how suppose to achieve the same? That is, check username and password in the first step, and if correct, store it securely (for example in an encrypted cookie) for the next step(s).

like image 967
balazska Avatar asked Nov 15 '16 12:11

balazska


People also ask

Is IdentityServer4 obsolete?

IdentityServer4 support will last until the end of life of . NET Core 3.1 that means till November 2022. In that way, Duende provides new documentation for the fifth service version.

Is IdentityServer paid?

The new Duende IdentityServer continues to be open source, but now has a dual license. This license allows it to be used for free for development, testing, and learning, free for non-commercial open source, and free for use in commercial settings if the entity or organization makes less than 1 million USD/year.

What is an IdentityServer?

IdentityServer is an authentication server that implements OpenID Connect (OIDC) and OAuth 2.0 standards for ASP.NET Core. It's designed to provide a common way to authenticate requests to all of your applications, whether they're web, native, mobile, or API endpoints.


1 Answers

Our solution was to replicate the IdentityServer3's partial login: use a custom cookie to persist data between steps.

First, we need to register our custom cookie authentication (at Startup.Configure)

app.UseCookieAuthentication(new CookieAuthenticationOptions
{
    AuthenticationScheme = "my-partial",
    AutomaticAuthenticate = false,
    AutomaticChallenge = false
});
  1. The first step/entry point of the login workflow should be mapped to GET /account/login (as of IdentityServer4 1.0.0-rc2).

  2. In second step, after the credentials are sent and verified, we persist the username (and eventually any other data) into a cookie.

Code:

var claims = new []
{
    new Claim("my-user", username),
    new Claim("some-attribute", someAttribute)
};

await HttpContext.Authentication
    .SignInAsync("my-partial", new ClaimsPrincipal(new ClaimsIdentity(claims)));

Important: avoid using POST /account/login as a second step. Because regardless of your result, IdentityServer's middleware will redirect you back to the authorization endpoint (as of RC2). Just pick any other path.

  1. At your last step, key parts
    • we read the persisted data from the cookie
    • remove the partial cookie
    • sign in the "real" user
    • redirect to returnUrl (this was added to the first step as a query parameter. Don't forget to send along it)

In code

var partialUser = await HttpContext.Authentication.AuthenticateAsync("my-partial");
var username = partialUser?.Claims.FirstOrDefault(c => c.Type == "dr-user")?.Value;

var claims = new [] { /* Your custom claims */};

await HttpContext.Authentication
    .SignOutAsync("my-partial");

await HttpContext.Authentication
    .SignInAsync(username, username, claims);

return Redirect(returnUrl);

In addition, you might want to validate inputs, for example return to the first step, if there is no partial cookie, etc.

like image 129
balazska Avatar answered Sep 20 '22 19:09

balazska