'How do I avoid using a client secret or certificate for Blazor Server when using MSAL?
When using Blazor Server and the MSAL library you must provide either a client secret or a client certificate. Here is what a Blazor Server project uses to setup the authentication out of the box.
builder.Services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApp(builder.Configuration.GetSection("AzureAd"));
However in the Blazor WASM (Or Blazor Client) project they set things up this way
builder.Services.AddMsalAuthentication(options =>
{
builder.Configuration.Bind("AzureAd", options.ProviderOptions.Authentication);
});
Obviously this method doesn't require a Client Secret for security reasons. The AddMsalAuthentication()
method that it uses is only found in the WebAssembly MSAL library.
We don't use a key vault as our company doesn't want to pay for one at this time. So we have to manually update all the client secrets every 24 months. To avoid this we want to try to implement a public client flow in our Blazor Server apps. We already have a bunch of them so we don't want to have to move them to WebAssembly.
I did try to implement using a public client builder manually but the .AddMicrosoftIdentityWebApp()
portion would still require a client secret to allow the first login.
In Blazor Server is there a way to implement the same behavior as the WebAssembly authentication, in other words, can we avoid using Client Secrets and Certificates?
Solution 1:[1]
I would recommend to do as the following to be able to retrieve access token without client secret.
Statup class:
services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApp(o => {
o.UsePkce = true;
o.ClientId = "clientId";
o.TenantId = "tenantId";
o.Domain = "domain.onmicrosoft.com";
o.Instance = "https://login.microsoftonline.com";
o.CallbackPath = "/signin-oidc";
o.Scope.Add("scopeApi1");
o.Scope.Add("offline_access");
o.ResponseType = "code";
var defaultBackChannel = new HttpClient(); defaultBackChannel.DefaultRequestHeaders.Add("Origin", "thisismyapp");
o.Backchannel = defaultBackChannel;
});
services.Configure<OpenIdConnectOptions>(OpenIdConnectDefaults.AuthenticationScheme, options =>
{
options.SaveTokens = true;
options.Events.OnTokenValidated = async context =>
{
// Here you will be able to see the accesstoken.
var accessToken = context.TokenEndpointResponse.AccessToken;
var refreshToken = context.TokenEndpointResponse.RefreshToken;
};
});
In your case change the "service" to "builder.Services". Then in the _Host.cshtml
var accessToken = await HttpContext.GetTokenAsync("access_token");
var refreshToken = await HttpContext.GetTokenAsync("refresh_token");
<component type="typeof(App)" param-AccessToken="@accessToken" param-RefreshToken="@refreshToken" render-mode="Server" />
In App.razor:
[Parameter]
public string AccessToken { get; set; }
[Parameter]
public string RefreshToken { get; set; }
From here you will be able to do what ever you want with the accesstoken..
Some issue i have been facing with this, is how to get a new accesstoken with the refreshtoken without client secret. If you figure it out, let me know.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | ng90 |