Connecting Blazor Wasm to Identity Server 4 - Register and account management links not working

Viewed 1502

Hopefully someone can guide me in the right direction, because I've been working on this for a while now.

I've create a Blazor WASM hosted in .Net Core. However instead of using the identity in the Server (API) project, I wanted to use for authentication an Identity Server hosted in a different project (so that I can use a standalone Identity Server).

I've create an Identity Server 4 project and also scaffold-ed the Identity Razor pages (so that I have the full flow with registration, account management, password recovery etc.) instead of the basic 3 or so pages that Identity Server generates.

In my Blazor Client project I've added the following inside the Main method:

{
    // Bind to the oidc section in the appsettings.
    builder.Configuration.Bind("oidc", options.ProviderOptions);
     options.UserOptions.RoleClaim = JwtClaimTypes.Role;
})

Also, in my appsettings file I have the following oidc section:

  "oidc": {
    "Authority": "https://localhost:5001",
    "ClientId": "ProjectAllocation.Client",
    "DefaultScopes": [
      "openid",
      "profile",
      "roles",
      "offline_access"
    ],
    "PostLogoutRedirectUri": "/",
    "ResponseType": "code"
  }
}

In Identity Server, in the Startup ConfigureServices I am redirecting the Login \ Logout to use the pages in the scaffolded Identity Area:

var builder = services.AddIdentityServer(options =>
   {
      ...
      options.UserInteraction.LoginUrl = "/Identity/Account/Login";
      options.UserInteraction.LogoutUrl = "/Identity/Account/Logout";
      options.Authentication = new IdentityServer4.Configuration.AuthenticationOptions
      {
         CookieLifetime = TimeSpan.FromHours(10), // ID server cookie timeout set to 10 hours
         CookieSlidingExpiration = true
      };
})

Now the login and logout work and it seems I am getting the right token data in the client; I haven't implemented the API on the server side yet.

My problem is that the register and the account management links from the Client project are not working. If you would use the template from the VS with integrated identity server then you would be able to click on the register link inside the client app and be taken to the Account\Register page in the Identity area; also once you logged in you can click on the "Hello ...." link and be taken to the Account management in the Identity area.

However this doesn't work in my case. If I navigate from the browser directly to those areas then it works (i.e.: browse to https://localhost:5001/Identity/Account/Register: it works). When I click on Register button in the Client app it just reloads the app with the following link: https://localhost:44395/?returnUrl=%2Fauthentication%2Flogin : it looks as if the app is being asked to login, even though the Register page in the Identity Server is marked to allow anonymous access.

I am really puzzled as to why this doesn't work. I can't figure out which settings in the Blazor project sets the links to navigate to via the RemoteAuthenticatorView. I am considering replacing the register button so that it doesn't navigate via the RemoteAuthenticatorView anymore and instead it uses a regular link directly to the Identity Server Register page, but I am not sure what the implications are; also it's really annoying that I cannot get this to work properly.

I've even tried to change the path to the register page so that instead of Identity/Account/Register is Account/Register via the ConfigureServices in the Startup file in the Identity Server 4:

services.AddRazorPages(options => {
                options.Conventions.AddAreaPageRoute("Identity", "/Account/Register", "Account/Register");
            });

which works from the browser (https://localhost:5001/Account/Register), but it still didn't work from the WASM Blazor Client.

Any ideas?

Thanks, Raz

2 Answers

I am leaving this here in case someone else stumbles upon this. I've looked through the Blazor WASM code and for registration and account management it uses the NavigationManager to navigate to the paths supplied in RemoteRegisterPath and RemoteProfilePath properties of the AuthenticationPaths.

For some strange reason though it looks like you cannot navigate to an outside url: the NavigationManager will ignore the supplied base address and use the base address of the project. So even though I've tried to provide an address like https://localhost:5001/Identity/Account/Register, the application actually navigates to https://localhost:44395/Identity/Account/Register.

As a workaround I've created a controller called Account with two methods Register and Manage which will redirect to the address of the Identity Server. So the Blazor client will call the corresponding method in the Account controller in the Blazor API server project which will redirect to the corresponding Identity Server page.

I've modified the call to AddOidcAuthentication() in the Main method inside the Blazor Client project:

builder.Services.AddOidcAuthentication(options =>
   {
      // Bind to the oidc section in the appsettings.
                
      builder.Configuration.Bind("oidc", options.ProviderOptions);
      options.AuthenticationPaths.RemoteRegisterPath = "Account/Register";
      options.AuthenticationPaths.RemoteProfilePath = "Account/Manage";
      options.UserOptions.RoleClaim = JwtClaimTypes.Role;
})

I've also created an AccountController in the Controllers folder in the Blazor API server project:

[Route("[controller]")]
[ApiController]
public class AccountController : ControllerBase
{
   [AllowAnonymous]
   [HttpGet("Register")]
   public IActionResult Register(string returnUrl)
   {
      return Redirect(@"https://localhost:5001/Identity/Account/Register?returnUrl=https://localhost:44395/" + returnUrl);
   }

   [AllowAnonymous]
   [HttpGet("Manage")]
   public IActionResult Manage(string returnUrl)
   {
      return Redirect(@"https://localhost:5001/Identity/Account/Manage?returnUrl=https://localhost:44395/" + returnUrl);
   }
}

Might be a good idea to authorize the Manage method. Also the returnUrl would probably need some additional parameters (at least for the login \ logout there are more parameters).

In addition, I needed to make some small changes in the Identity files scaffolded in the Identity Server project to allow redirection to non-local paths.

There are certain things that can be improved and it does feel like a hack, but the solution works for now and I couldn't find any better alternatives.

Related