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).
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.
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.
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.
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
});
The first step/entry point of the login workflow should be mapped to GET /account/login
(as of IdentityServer4 1.0.0-rc2
).
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With