After implementing openidconnect, where does blazor store the access token? how to retrieve it?
How to add OpenIdConnect via IdentityServer4 to ASP.NET Core ServerSide Blazor web app?
After implementing openidconnect, where does blazor store the access token? how to retrieve it?
How to add OpenIdConnect via IdentityServer4 to ASP.NET Core ServerSide Blazor web app?
I resolved this issue by adding the code listed in the link below.
Note: The steps listed in the link above work fine, however, I made some small modifications that make more sense to me. I also modified the order that make more sense to me.
Steps:
1. Create the TokenProvider class
public class TokenProvider
{
public string AccessToken { get; set; }
}
2. Update the _Host.cshtml file with the following:
@using Microsoft.AspNetCore.Authentication
@{
var accessToken = await HttpContext.GetTokenAsync("access_token");
}
<body>
<app>
<component type="typeof(App)" param-AccessToken="accessToken" render-mode="ServerPrerendered" />
</app>
3. Update StartUp.cs with DI:
public void ConfigureServices(IServiceCollection services)
{
services.AddRazorPages();
services.AddScoped<TokenProvider>();
4. Update App.razor with the following:
@inject TokenProvider TokenProvider
@code
{
[Parameter]
public string AccessToken { get; set; }
protected override void OnInitialized()
{
//Accept the parameter from _Host.cshtml and move into the Token Provider
TokenProvider.AccessToken = AccessToken;
base.OnInitialized();
}
}
5. Create an instance of the _tokenProvider in the constructor and use it to get the Access Token
Note: Below I get the access token NOT in the @code block or the code behind of the Blazor page, but I am using a Service class. (The page calls the Service class). I hope this makes sense. Again, I would suggest reviewing the link above.
private readonly TokenProvider _tokenProvider;
//Create tokenProvider using constructor Dependency Injection
public HttpClientUtility(TokenProvider tokenProvider)
{
_tokenProvider = tokenProvider;
}
var accessToken = _tokenProvider.AccessToken;
if (accessToken != null)
{
_httpClient.DefaultRequestHeaders.Add("Authorization", "Bearer " + accessToken);
}
Hopefully the above steps will help someone else.
The following code snippets provide a way to retrieve the access token issued when a user is authenticated with IdentityServer4 provider. In order to get the access token you can use the HttpContext object, but since Blazor is SignalR-based, you'll have to do it the only time the HttpContext object is available, when the connection to your application is an HTTP connection, and not a WebSocket connection.
After retrieving the access token, you need to pass it to your Blazor app, and store it in a local storage. My code also provide a way to parse the access token, if necessary.
Add a file to the Pages folder and name it _Host.cshtml.cs
Add this code to the file:
public class HostAuthenticationModel : PageModel
{
public async Task<IActionResult> OnGet()
{
if (User.Identity.IsAuthenticated)
{
var token = await HttpContext.GetTokenAsync("access_token");
AccessToken = token;
}
return Page();
}
public string AccessToken { get; set; }
}
Note: I've name the the PageModel class: HostAuthenticationModel
You'll need some of these:
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using System;
using System.Linq;
using System.Threading.Tasks;
In the _Host.cshtml file add the model directive at the top portion of the file:
@model HostAuthenticationModel
Add a new attribute to the component Tag Helper like this:
param-AccessToken="Model.AccessToken"
Final result:
<app>
<component type="typeof(App)" render-mode="ServerPrerendered"
param-AccessToken="Model.AccessToken"/>
</app>
The param-AccessToken
attribute requires you to define a property named AccessToken in the App component which will get the access token from the page model.
And then override the OnAfterRenderAsync method from which we call a method to store the access token in the local storage.
@code{
[Parameter]
public string AccessToken { get; set; }
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
await tokenStorage.SetTokenAsync(AccessToken);
}
}
}
Also place the following at the top of the App component:
@inject AccessTokenStorage tokenStorage
Next you'll have to create the AccessTokenStorage service like this:
Create a class named AccessTokenStorage at the root of your app, and add the following code:
public class AccessTokenStorage { private readonly IJSRuntime _jsRuntime;
public AccessTokenStorage(IJSRuntime jsRuntime)
{
_jsRuntime = jsRuntime;
}
public async Task<string> GetTokenAsync()
=> await _jsRuntime.InvokeAsync<string>("localStorage.getItem", "accessToken");
public async Task SetTokenAsync(string token)
{
if (token == null)
{
await _jsRuntime.InvokeAsync<object>("localStorage.removeItem",
"accessToken");
}
else
{
await _jsRuntime.InvokeAsync<object>("localStorage.setItem",
"accessToken", token);
}
}
}
I guess no explanation is needed here... Here's some using directives you may need
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Security.Claims;
using Microsoft.AspNetCore.Components.Authorization;
using Microsoft.JSInterop;
Add the following to the Startup.ConfigureServices
services.AddHttpClient();
services.AddScoped<AccessTokenStorage>();
Note: the above code should be used with the code I provide in my answer here
I used the following way
Startup.cs
services.AddHttpContextAccessor();
Razor page
@using Microsoft.AspNetCore.Http
@using Microsoft.AspNetCore.Authentication
@inject IHttpContextAccessor httpContextAccessor
@code {
private async Task<string> GetToken()
=> await httpContextAccessor.HttpContext.GetTokenAsync("access_token");
}