angularasp.net-coreidentityserver4angular10oidc-client-js

How to make Angular Client to inform Identity Server which login method to use?


I am using IdentityServer 4 with an Angular 10 client that uses OIDC Client JS:

To redirect a user for signin I am calling signinRedirect on the Angular's client:

UserManager.signinRedirect(args)

That redirects to IdentityServer 4 AcccountController's Login action:

[HttpGet]
public async Task<IActionResult> Login(string returnUrl) {
  var vm = await BuildLoginViewModelAsync(returnUrl);
  if (vm.IsExternalLoginOnly) {
    // we only have one option for logging in and it's an external provider
    return RedirectToAction("Challenge", "External", new { scheme = vm.ExternalLoginScheme, returnUrl });
  }
  return View(vm);
}

Question

I have Google and Facebook External Providers configured in Identity Server.

How to use signinRedirect and inform IdentityServer to use an external provider, like Google, for login?

I would like to bypass the login page in IdentityServer where there are 3 options:

  1. Login with Username and Password
  2. Login with Google
  3. Login with Facebook

So the Angular Client would inform Identity Server which login method should be used.

Update

After some research it seems there is a way in the spec to specify which provider to user:

UserManager.signinRedirect({ acr_values: 'IdP:Google' }));

Then on the AccountController's BuildLoginViewModelAsync there is the following:

var context = await _interactionService.GetAuthorizationContextAsync(returnUrl);

if (request?.IdP != null && await _schemeProvider.GetSchemeAsync(request.IdP) != null) {

  var local = request.IdP == IdentityServer4.IdentityServerConstants.LocalIdentityProvider;

If I am not wrong the IF condition and following code is to check which Login Provider should be used: Local or one of the External providers configured.

I debugged context and got the following:

enter image description here

The AcrValues is parsed but the property IdP is not defined.

Shouldn't AcrValues be the way to solve this? What am I missing?


Solution

  • It is possible to send a message to the identity provider using oidc-client by using the args parameter of signinRedirect method.

    If you take a look at oidc-client-js/src/SigninRequest.js from their github repository, you can see that you can supply the following optional parameters to the aforementioned method:

    Later in the code, on line 75 of this file version, they have this:

    for(let key in extraQueryParams){
      url = UrlUtility.addQueryParam(url, key, extraQueryParams[key])
    }
    

    which means they allow devs to use extraQueryParams to supply custom parameters.

    Now assuming your custom message parameter might be called useExternalProvider, you should be able to call signInRedirect like this:

    UserManager.signinRedirect({ 
      extraQueryParams: {
        useExternalProvider: "google"
      }});
    

    To access this parameter on your IdentityServer, find the line which calls GetAuthorizationContextAsync and inspect the protocol message parameters like this:

    var context = await interaction.GetAuthorizationContextAsync(returnUrl);
    var externalProviderToUse = context.Parameters.Get("useExternalProvider");
    

    It is usually in BuildLoginViewModelAsync(string returnUrl) method if you used their QuickStart.

    From there you can retrieve the external login parameters from database, or from wherever you keep those parameters from, and instead of redirecting users to the login page, redirect them directly to your ExternalLogin(string provider, string returnUrl) using RedirectToAction.

    EDIT

    If you want to use the acr_values parameter for this and later in IdentityServer authorization context want to use the Idp property, you can call:

    // "idp:" is case sensitive
    UserManager.signinRedirect({ acr_values: 'idp:Google' }));