Always enter credentials without “prompt=login” in

2019-08-25 09:52发布

This is similar to IdentityServer4 Force User to re-enter credentials, but the solution there says to use prompt=login query string in the /authorize URL, which works, but also allows for sneaky users to remove it. Also, seeing as I'm not using .AddOpenIdConnect() the suggestion to use OnRedirectToIdentityProvider doesn't apply to me.

So how can we force the user to always enter credentials without relying on the prompt=login in the query string?


Here's my basic IdentityServer4 setup:

public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc();

    var builder = services.AddIdentityServer()
        .AddInMemoryIdentityResources(Config.GetIdentityResources())
        .AddInMemoryApiResources(Config.GetApis())
        .AddInMemoryClients(Config.GetClients());

    builder.AddDeveloperSigningCredential();
}

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    app.UseHttpsRedirection();
    app.UseFileServer();
    app.UseIdentityServer();
    app.UseMvc();
}

Where my login page only has an "OK" and "Cancel" button (I don't care which user logs on yet, only that authentication was OK or not), and the controller does the following when it is authenticating the user:

public async Task<IActionResult> Post(AuthenticateViewModel model, string button)
{
    var context = await _interaction.GetAuthorizationContextAsync(model.ReturnUrl);

    if (button == "authCancel")
    {
        if (context != null)
        {
            await _interaction.GrantConsentAsync(context, ConsentResponse.Denied);
            return Redirect(model.ReturnUrl);
        }

        return Redirect("~/");
    }

    if (button == "authOk" && ModelState.IsValid)
    {
        if (context != null)
        {
            await _events.RaiseAsync(new UserLoginSuccessEvent("provider", "userId", "subjectId", "name"));
            await HttpContext.SignInAsync("subject", "name", new AuthenticationProperties());
            return Redirect(model.ReturnUrl);
        }
    }

    return Redirect("~/");
}

2条回答
做个烂人
2楼-- · 2019-08-25 10:32

You should be able to achieve desired behaviour by overriding the default cookie scheme that AddIdentityServer() registers internally:

services.AddIdentityServer()...

services.AddAuthentication("CustomScheme")
    .AddCookie("CustomScheme", options =>
    {
        options.ExpireTimeSpan = ...;
    });

Make sure you add the override scheme after AddIdentityServer(), the sequence here is important due to the way ASP.Net Core DI works.

查看更多
我想做一个坏孩纸
3楼-- · 2019-08-25 10:38

An option could be to stick to prompt=login for all requests or based on some client setting, or a http header.

It is easy to look into the default request validator and implement your customization like the following:

public class YourCustomAuthorizeRequestValidator:ICustomAuthorizeRequestValidator
{
  public Task ValidateAsync(CustomAuthorizeRequestValidationContext context)
  {
    var request = context.Result.ValidatedRequest;    
    if (string.IsNullOrWhiteSpace(request.Raw["prompted"]))
    {
      request.Raw.Add("prompted", "true");
      request.PromptMode = OidcConstants.PromptModes.Login;
    }
    else if (request.Subject.IsAuthenticated())
    {
      request.PromptMode = OidcConstants.PromptModes.None;
    }
    return Task.CompletedTask;
  }
}

and then in your Identityserver startup:

services.AddIdentityServer()
  .AddCustomAuthorizeRequestValidator<YourCustomAuthorizeRequestValidator>();
查看更多
登录 后发表回答