Hi,
I'm in a scenario where our SSO (identity server) has several clients. Different clients requires to be authenticated using different authentication methods.
QUESTION/TL;DR:
Can we force identity server to run our login logic when in an already authenticated state, without having the user include _prompt="login"_ in their auth request?
Background:
To make this work right now, we are doing the following:
IdentityProviderRestrictions property)User.GetAuthenticationMethods()) to see if the we get a match. If we do, the authentication is fine and we just redirect back to the return url. Else, we force the user to login again, with a valid authentication method for the current clientIt looks something like this (maybe the code explains it better than my words..):
```c#
var usedAuthenticatedMethods = User.GetAuthenticationMethods().Select(c => c.Value);
if (client.IsAuthenticatedWithAllowedMethod(usedAuthenticatedMethods))
return Redirect(returnUrl);
return GoToLogin(returnUrl, client);
3. When we login again, with a second authentication method, we do a new `SignInAsync`. We merge the already used with the new authentication method into the `authenticationMethods` parameter. This allows us to add more authentication methods to the current SSO session
**Forcing login on request to authorization endpoint**
To make this work, we obviously need to be able to run our login logic with every authorization request. Right now, the only solution we have found for this is to tell all our clients to include _prompt="login"_ in their auth requests.
We would really like to find a way to trigger this behavior without relying on our clients having to include the _prompt_ flag.
**`IdentityProviderRestrictions` and why we can't use it**
Identity server already supports a similar scenario with the `IdentityProviderRestrictions` property on the `Client` class. Though the logic for checking that property is to see if the _currentIdp_ is in the _IdentityProviderRestrictions list_. This is not what we want, since the `currentIdp` is not the list of used authentication methods, but just a single instance of the idp used. From the source code ([link to code](https://github.com/IdentityServer/IdentityServer4/blob/becb2206826b1039687b3ec895fad5e1b26ef0c5/src/IdentityServer4/ResponseHandling/AuthorizeInteractionResponseGenerator.cs#L195)):
```c#
// check current idp
var currentIdp = request.Subject.GetIdentityProvider();
....
// check external idp restrictions if user not using local idp
else if (request.Client.IdentityProviderRestrictions != null &&
request.Client.IdentityProviderRestrictions.Any() &&
!request.Client.IdentityProviderRestrictions.Contains(currentIdp))
{
Logger.LogInformation("Showing login: User is logged in with idp: {idp}, but idp not in client restriction list.", currentIdp);
return new InteractionResponse { IsLogin = true };
}
You can derive from our default authorize interaction generator - and always trigger login. Don't have a sample - but search for an interface with Interaction in it ;)
Thanks for a very quick response and a great product!
Following your suggestion worked fine for us. Here is a really quick explanation of we solved it, if someone finding the issue in the future would be interested :)
AuthorizeInteractionResponseGenerator and overrode the ProcessLoginAsync method```c#
public class ValidateAuthenticationMethodsAuthorizeInteractionResponseGenerator : AuthorizeInteractionResponseGenerator
{
public ValidateAuthenticationMethodsAuthorizeInteractionResponseGenerator(
ILogger
IdentityServerOptions options,
IConsentService consent,
IProfileService profile,
IConfiguration configuration)
: base(logger, options, consent, profile)
{ }
protected override Task<InteractionResponse> ProcessLoginAsync(ValidatedAuthorizeRequest request)
{
if (!request.Subject.IsAuthenticated())
{
return Task.FromResult(new InteractionResponse { IsLogin = true });
}
return ValidateUsedAuthenticationMethods(request); // Custom logic
}
....
}
2. Replace the default Identity Server implementation of `IAuthorizeInteractionResponseGenerator` with your custom implementation in DI
```c#
services.AddIdentityServer(...);
var validateAuthenticationMethodsResponseGenerator = new ServiceDescriptor(
typeof(IAuthorizeInteractionResponseGenerator),
typeof(ValidateAuthenticationMethodsAuthorizeInteractionResponseGenerator),
ServiceLifetime.Transient);
services.Replace(validateAuthenticationMethodsResponseGenerator);
@fredrik-lundin
Hi, I implemented your code in your second post but it doesnt seems like ProcessLoginAsync gets executed.
This thread has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.