Add Auth0 Authentication to Blazor Web Apps

Hi @andrea.chiarelli

I’ve found the problem - but it’s still perplexing!

So I went back over the config and checked the 3 things you mentioned. All were fine.

  1. “Enable RBAC” and “Add Permissions in the access token” is 100% saved.
  2. The user definitely has the Reader role
  3. The role definitely has the Permissions

I missed out an important bit of information in my original post to you about the Regular Web Application that I set up to talk my API… it is set up for Business Users with the 1st option Prompt for user credentials first

If I switch this back to Individual Users (the default when you set up a Regular Web Application) then I get the expected read:weather permission in my token as shown below :thinking:

{
  "iss": "https://{removed}.{removed}.auth0.com/",
  "sub": "auth0|{removed}",
  "aud": [
    "https://cloudoko-portal-api/"
  ],
  "iat": 1738402793,
  "exp": 1738489193,
  "scope": "openid profile email",
  "azp": "{removed}",
  "permissions": [
    "read:weather"
  ]
}

(Obviously the org_id claim is now removed too)

Interestingly, I also set up a separate Single Page Application, downloaded the Vanilla Js Quickstart and configured that. As with the .NET example, when I call for an access token for the same API audience we were trying before (https : // cloudoko-portal-api) with Individual Users set, then I get the read:weather permission. But again if I set that SPA application to Business Users* then I get an empty array.

So I think we’ve found the problem, but what could be the issue here? Is this a bug in the RBAC / Permissions in token feature for SPA and Regular Web Apps when you set the user type to Business Users Only* or am I driving the Auth0 SDK incorrectly both in my .NET and Js code?

Are you able to recreate my issue too?

A bit of background on our use case - we’re updating our SaaS platform to use Auth0 for authentication and we work with users that work for different organisations. We have a “tenant” concept that we wish to map on to Auth0 organisations (on paper they’re a perfect match). Also for our own “Admin” users - the people that work for us and need to support the product - we want to be able to log in under customer organisations so that we can diagnose issues etc.

Thanks
Matt

Hey @matthew.wynn,
This clarifies the reason for that behavior.

Users can have different roles based on the Organization to which they belong. A user can be an admin in one Organization but just a reader in another org.
The role you assign to a user regardless of an Organization is only considered when the user accesses as an Individual.

In your case, your user has a role assigned when the user accesses as an individual, but your configuration requires only members of an Organization, so the role associated with the user is ignored.

When you use Organizations, the roles must be assigned at the Organization level.

Here is a starting point for learning more about the login flows for Organizations.

I hope this helps clarify.

Thanks @andrea.chiarelli - you solved my issue.

The problem was that the role was assigned at the user level (and I never spotted that Auth0 gives you the ability to do that assignment at the organisation level). This makes perfect sense, when you log in as your user within a certain organisation your role may also be different.

I’m very happy to say that I now get permissions back in the access token when the application is set to required Business Users which is a big relief - as it means we can resume our SaaS platform migration to move to Auth0! :tada:

1 Like

That’s a great news, @matthew.wynn! :tada: :partying_face:
Happy to have helped you!

1 Like

I have been all over this subject trying to use Azure B2C, Identity, etc. For a Blazor WebApp that uses InteractiveAuto, not one single thing has worked.

Until this article. Flawless - right out of the box. Understandable, well written, complete and up to date. Kudos, man. Well done.

p.s. For anyone who is missing claims in webassembly, there’s an option for configuring it.

I made this change:

 .AddAuthenticationStateSerialization(
options =>
{
    options.SerializeAllClaims = true;
});

Thank you so much.

1 Like

Hey @nhwilly, thank you very much for your kind words! :pray:
That made my day! :heart:

Hey @andrea.chiarelli,

You mentioned wanting to write a follow-up (Part 2) on calling external APIs—I’m really looking forward to it! Do you have an estimated publish date in mind?

By the way, this article is top-notch. It’s rare to find such high-quality, up-to-date content.

Thanks!

Hi @benreisinger,
Thank you for appreciating my article :blush: That means a lot! Thank you :folded_hands:

Regarding the follow-up article, I already published it: Call Protected APIs from a Blazor Web App.

I hope it’s helpful for you.

Can someone please help me out , and put me on the right paht with aut0 and blazor, let me add some context , im working on a blazor webasembly app with pwa on dot net 7 cant go above 7 because of hardware limitations, and ive set up my custom auth0 universal login added some pre registration, post login actions to assign roles to users based on some conditions,all that works perfectly and when i call it from my applications client side it works to how ever i always encounter issues when i try to retrive stuff like user meta sata or jwt and print them for the sake of debugging and i also need some of the user meta data to store locally but alas it dosent work , if u need a sample of the code im using just ask . Any help will be appreciated :folded_hands:

Hi @tukurukuinamdapiaati,
To show data coming from the user profile in Blazor, you don’t need to access the ID token (JWT). The token claims are mapped to the AuthenticationState. Take a look here to get an idea of how you can access the AuthenticationState (the article is for Blazor Server, but it’s similar in Blazor WASM)

Regarding user metadata access, it would be helpful to learn how you try to get metadata and what type of error you get.
Anyway, be aware that accessing user metadata from a SPA, like a Blazor WASM app, has limitations. Learn more here.
Also, this question in the community forum may give you a hint

sorry for the delayed response i was dealing with some issues, anyway ive made some changes but they are stuff i piced together from the internet so im not to sure about them and regarding the meta dat i just want to read the user role, and auth0 id, but i belive i should provide more context into what im actually trying to do,so im working on my final year project it supposed to be a secure qr code attendance tracking application that utilizes qr code technology , and the authentication ,authorization and role based access control are one of the most crusial parts of the application, in my auth0 universal login i added some custom fields to validate and assign roles to users using a machine to machine app , and in my client side what i want is to get the user auth0 id and his role and use that information to create offline credentials(the application is a blazor wasm app with pwa on .net7) and we need the offline credentials to handle offline mode and u can onnly get those once youve logged in atleast once online,so let me provide the current files i use for the auth0 authentication flow, the current issue im having is the auth-info page it does get loaded and i get and log the information like the user id and role but i never see the auth-info page as it gets unloaded instantly and i get sent back to out index page (/) and as i said im not sure of this method as this is my first time using auth0. the below are the main scripts i use concerning auth0. → (Program.cs)

// Configure Auth0 authentication
builder.Services.AddOidcAuthentication(options =>
{
    // Bind standard Auth0 configuration values.
    builder.Configuration.Bind("Auth0", options.ProviderOptions);
    
    //  Code Flow with PKCE for improved security.
    options.ProviderOptions.ResponseType = "code";

    // Specify the intended API in the additional parameters.
    options.ProviderOptions.AdditionalProviderParameters.Add("audience", builder.Configuration["Auth0:Audience"]);

   //Our role claim namespace inserted during the whole post login flow
    options.UserOptions.RoleClaim = "https://air-code/roles";

    // Optional: Add default scopes if needed.
    options.ProviderOptions.DefaultScopes.Add("openid");
    options.ProviderOptions.DefaultScopes.Add("profile");
    options.ProviderOptions.DefaultScopes.Add("email");
  
});
// HTTP clients setup
// Base client without auth
builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) });

// HTTP client with Auth0 token
builder.Services.AddHttpClient("AirCodeAPI", 
        client => client.BaseAddress = new Uri(builder.HostEnvironment.BaseAddress))
    .AddHttpMessageHandler<BaseAddressAuthorizationMessageHandler>();

builder.Services.AddScoped(sp => sp.GetRequiredService<IHttpClientFactory>()
    .CreateClient("AirCodeAPI"));

// Clear default JWT claim mappings to preserve original claim names from Auth0
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();

// Add authorization services
builder.Services.AddAuthorizationCore();

//Auth(auth0 ish) service
builder.Services.AddScoped<IAuthService, AuthService>();
. (App.razor) @using AirCode.Layout.Main
@using Microsoft.AspNetCore.Components.Authorization

<CascadingAuthenticationState>
    <Router AppAssembly="@typeof(App).Assembly">
        <Found Context="routeData">
            <AuthorizeRouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)">
                <Authorizing>
                    <p>Determining session state, please wait...</p>
                </Authorizing>
                <NotAuthorized>
                    <h1>Sorry</h1>
                    <p>You're not authorized to reach this page. You need to log in.</p>
                </NotAuthorized>
            </AuthorizeRouteView>
            <FocusOnNavigate RouteData="@routeData" Selector="h1" />
        </Found>
        <NotFound>
            <PageTitle>Not found</PageTitle>
            <LayoutView Layout="@typeof(MainLayout)">
                <p role="alert">Sorry, there's nothing at this address.</p>
            </LayoutView>
        </NotFound>
    </Router>
</CascadingAuthenticationState>
(IndexPage.razor)
            await JSRuntime.InvokeVoidAsync("alert", "You are currently offline. Please check your internet connection and try again.");
            isOnlineLoading = false;
            return;
        }

        // Use the built-in authentication mechanism
        NavigationManager.NavigateTo("authentication/login");
        
        isOnlineLoading = false;
    }

    private async Task ContinueOffline()
    {
        isOfflineLoading = true;

        // Check if offline credentials exist
        bool hasCredentials = await JSRuntime.InvokeAsync<bool>("offlineManager.hasStoredCredentials");

        if (!hasCredentials)
        {
            await JSRuntime.InvokeVoidAsync("offlineManager.showNoCredentialsMessage");
            isOfflineLoading = false;
            return;
        }

        // Validate offline credentials
        bool isValid = true;

        if (isValid)
        {
            // Redirect based on stored role
            var userRole = UserRole.SuperiorAdmin;

            switch (userRole.ToString()?.ToLower())
            {
                case "superioradmin":
                    NavigationManager.NavigateTo("/Admin/Dashboard");
                    break;
                case "lecturer":
                    NavigationManager.NavigateTo("/Admin/Dashboard");
                    break;
                case "student":
                    NavigationManager.NavigateTo("/Client/Dashboard");
                    break;
                default:
                    await JSRuntime.InvokeVoidAsync("alert", "Unable to determine user role. Please login online.");
                    break;
            }
        }
        else
        {
            await JSRuntime.InvokeVoidAsync("alert", "Your offline credentials are invalid or expired. Please login online.");
        }

        isOfflineLoading = false;
    }

    public void Dispose()
    {
        // Clean up JS interop
        JSRuntime.InvokeVoidAsync("connectivityChecker.dispose");
        objRef?.Dispose();
    }
} (Authentication.razor)@page "/authentication/{action}"
@using Microsoft.AspNetCore.Components.WebAssembly.Authentication
@using Microsoft.Extensions.Configuration
@using Microsoft.JSInterop
@using AirCode.Services.Auth

@inject NavigationManager Navigation
@inject IConfiguration Configuration
@inject IJSRuntime JSRuntime
@inject IAuthService AuthService
@inject IAccessTokenProvider TokenProvider

<RemoteAuthenticatorView Action="@Action" OnLogInSucceeded="HandleLoginSuccess">
    <LoggingIn>
        <p>Redirecting to Auth0 login...</p>
        @{
            AuthService.LogAuthenticationMessage("Starting Auth0 login redirection");
        }
    </LoggingIn>
    <CompletingLoggingIn>
        <p>Completing login process...</p>
        @{
            AuthService.LogAuthenticationMessage("Processing login callback");
        }
    </CompletingLoggingIn>
    <LogOut>
        @{
            var authority = $"https://{Configuration["Auth0:Domain"]}";
            var clientId = Configuration["Auth0:ClientId"];
            AuthService.LogAuthenticationMessage("Logging out user");
            Navigation.NavigateTo($"{authority}/v2/logout?client_id={clientId}");
        }
    </LogOut>
</RemoteAuthenticatorView>

@code{
    [Parameter] public string Action { get; set; }
    
    private async Task HandleLoginSuccess(RemoteAuthenticationState state)
    {
        // Log successful authentication
        await AuthService.LogAuthenticationMessageAsync("Authentication successful!");

        // Request the token and log its status
        var result = await TokenProvider.RequestAccessToken();

        await AuthService.LogAuthenticationMessageAsync($"Access token status: {result.Status}");

        if (result.TryGetToken(out var token))
        {
            await AuthService.LogAuthenticationMessageAsync($"JWT retrieved successfully. Length: {token.Value.Length} chars");
    
            // Store token in session for debugging purposes
            await JSRuntime.InvokeVoidAsync("authHelper.storeJwtLocally", token.Value);
    
            // Log the first few characters
            if (token.Value.Length > 10)
            {
                await AuthService.LogAuthenticationMessageAsync($"JWT preview: {token.Value.Substring(0, 10)}...");
            }
    
            // Testing a LONGER delay before navigation to ensure state updates are complete
            await Task.Delay(1000); // Increased from 100ms to 1000ms (1 second)
    
            // Use NavigateTo with forceLoad: false to preserve state
            Navigation.NavigateTo("/auth-info", forceLoad: false);
        }
        else
        {
            await AuthService.LogAuthenticationMessageAsync("Failed to retrieve JWT token");
            Navigation.NavigateTo("/"); // Fall back to homepage if token retrieval fails
        }
    }
} (AuthInfo.razor)@page "/auth-info"
@attribute [Authorize]
@using System.Security.Claims
@using Microsoft.AspNetCore.Components.Authorization
@using Microsoft.AspNetCore.Components.WebAssembly.Authentication
@using System.Text.Json
@using Microsoft.AspNetCore.Authorization
@using AirCode.Services.Auth
@using Microsoft.JSInterop
@using AirCode.Components.Auth

@inject AuthenticationStateProvider AuthenticationStateProvider
@inject IAuthService AuthService
@inject IJSRuntime JSRuntime
@inject IAccessTokenProvider TokenProvider
@inject NavigationManager Navigation

<h1>Authentication Information</h1>
<TokenDebugger />
<AuthorizeView>
    <Authorized>
        <div class="auth-info">
            <h2>User Information</h2>
            <p><strong>Name:</strong> @context.User.Identity.Name</p>
            
            <!-- Add Role Display Section -->
            <h3>User Role</h3>
            @{
                var roleClaim = context.User.Claims.FirstOrDefault(c => c.Type == "https://air-code/roles");
                var role = roleClaim?.Value ?? "No role assigned";
            }
            <div class="role-container mb-3 p-2 border border-success rounded">
                <h4>Role</h4>
                <p class="font-weight-bold">@role</p>
            </div>
            
            <h3>Claims</h3>
            <div class="claims-container">
                @foreach (var claim in context.User.Claims)
                {
                    <div class="claim-item">
                        <strong>@claim.Type:</strong> @claim.Value
                    </div>
                }
            </div>

            <h3>JWT Token</h3>
            @if (_isLoading)
            {
                <p>Loading token information...</p>
            }
            else
            {
                @if (!string.IsNullOrEmpty(_userId))
                {
                    <div class="user-id-container mb-3 p-2 border border-primary rounded">
                        <h4>Auth0 User ID</h4>
                        <p class="font-weight-bold">@_userId</p>
                    </div>
                }

                <div class="jwt-container">
                    <pre>@_jwt</pre>
                </div>
                <div class="raw-token mt-3">
                    <h4>Raw JWT Token</h4>
                    <pre class="bg-dark text-light p-2" style="max-height: 100px; overflow: auto;">@_rawJwt</pre>
                </div>

                <button class="btn btn-secondary mt-3" @onclick="CopyJwtToClipboard">Copy JWT to Clipboard</button>
            }
            <div class="mt-4">
                <button class="btn btn-primary" @onclick="GoHome">Return to Home</button>
            </div>
        </div>
    </Authorized>
    <NotAuthorized>
        <p>You need to log in to view authentication information.</p>
        <button class="btn btn-primary" @onclick="GoHome">Return to Home</button>
    </NotAuthorized>
</AuthorizeView>

@code {
    private string _jwt = "Loading...";
    private string _rawJwt = "";
    private bool _isLoading = true;
    private string _userId = string.Empty;
    private string _userRole = string.Empty;
    
    protected override async Task OnInitializedAsync()
    {
        await AuthService.LogAuthenticationMessageAsync("Loading AuthInfo page");
        
        try
        {
            // First try to get token from session storage (if it was stored during login)
            var storedToken = await JSRuntime.InvokeAsync<string>("authHelper.retrieveStoredJwt");
            
            if (!string.IsNullOrEmpty(storedToken))
            {
                await AuthService.LogAuthenticationMessageAsync("Found JWT in session storage");
                _rawJwt = storedToken;
                await ProcessJwtToken(storedToken);
            }
            else
            {
                // Try to get token from TokenProvider
                var tokenResult = await TokenProvider.RequestAccessToken();
                
                if (tokenResult.TryGetToken(out var accessToken))
                {
                    await AuthService.LogAuthenticationMessageAsync("Retrieved JWT from TokenProvider");
                    _rawJwt = accessToken.Value;
                    await ProcessJwtToken(accessToken.Value);
                }
                else
                {
                    // Fall back to AuthService
                    await AuthService.LogAuthenticationMessageAsync("Trying to get JWT from AuthService");
                    var token = await AuthService.GetJwtTokenAsync();
                    
                    if (token != "Token not found" && !token.StartsWith("Error:"))
                    {
                        _rawJwt = token;
                        await ProcessJwtToken(token);
                    }
                    else
                    {
                        _jwt = "JWT not found. Available claims are listed above.";
                        await AuthService.LogAuthenticationMessageAsync("JWT not found through any method");
                    }
                }
            }
        }
        catch (Exception ex)
        {
            _jwt = $"Error retrieving JWT: {ex.Message}";
            await AuthService.LogAuthenticationMessageAsync($"Error in AuthInfo: {ex.Message}");
        }
        finally
        {
            _isLoading = false;
        }
    }
    
    private async Task ProcessJwtToken(string token)
    {
        try
        {
            await AuthService.LogAuthenticationMessageAsync($"Processing JWT token, length: {token.Length}");
            
            // Pretty print if it's JSON wait we could add this as function to our json helper later
            var tokenParts = token.Split('.');
            if (tokenParts.Length > 1)
            {
                var payload = tokenParts[1];
                var paddedPayload = payload.PadRight(payload.Length + (4 - payload.Length % 4) % 4, '=');
                var decodedBytes = Convert.FromBase64String(paddedPayload);
                var jsonString = System.Text.Encoding.UTF8.GetString(decodedBytes);
                
                try 
                {
                    var jsonElement = System.Text.Json.JsonSerializer.Deserialize<JsonElement>(jsonString);
                    var formattedJson = System.Text.Json.JsonSerializer.Serialize(jsonElement, new JsonSerializerOptions { WriteIndented = true });
                    _jwt = formattedJson;
                    
                    // Extract and log the Auth0 user ID specifically
                    if (jsonElement.TryGetProperty("sub", out var subProperty))
                    {
                        _userId = subProperty.GetString();
                        await AuthService.LogAuthenticationMessageAsync($"Auth0 User ID: {_userId}");
                    }
                    
                    // Extract and log the user role (works but info page dsapears instantly and we get sent
                    //back to / page get help)
                    if (jsonElement.TryGetProperty("https://air-code/roles", out var roleProperty))
                    {
                        _userRole = roleProperty.ValueKind == JsonValueKind.Array 
                            ? string.Join(", ", roleProperty.EnumerateArray().Select(r => r.GetString())) 
                            : roleProperty.GetString();
                        await AuthService.LogAuthenticationMessageAsync($"User Role: {_userRole}");
                    }
                    
                    await AuthService.LogAuthenticationMessageAsync("Successfully decoded and formatted JWT payload");
                }
                catch (Exception ex)
                {
                    _jwt = jsonString;
                    await AuthService.LogAuthenticationMessageAsync($"Decoded JWT payload but couldn't format JSON: {ex.Message}");
                }
            }
            else
            {
                _jwt = "Invalid JWT format (doesn't contain expected segments)";
                await AuthService.LogAuthenticationMessageAsync("Invalid JWT format");
            }
        }
        catch (Exception ex)
        {
            _jwt = $"Error processing JWT: {ex.Message}";
            await AuthService.LogAuthenticationMessageAsync($"Error processing JWT: {ex.Message}");
        }
    }
    
    // Added a longer delay to fix the navigation issue
    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender)
        {
            await AuthService.LogAuthenticationMessageAsync("AuthInfo page rendered successfully");
            await JSRuntime.InvokeVoidAsync("console.log", "AuthInfo page rendered successfully");
            
            // Log if we have role information
            if (!string.IsNullOrEmpty(_userRole))
            {
                await JSRuntime.InvokeVoidAsync("console.log", $"User role extracted: {_userRole}");
            }
        }
    }

    private async Task CopyJwtToClipboard()
    {
        try
        {
            if (!string.IsNullOrEmpty(_rawJwt))
            {
                await JSRuntime.InvokeVoidAsync("navigator.clipboard.writeText", _rawJwt);
                await AuthService.LogAuthenticationMessageAsync("JWT copied to clipboard");
            }
        }
        catch (Exception ex)
        {
            await AuthService.LogAuthenticationMessageAsync($"Error copying JWT: {ex.Message}");
        }
    }
    
    private void GoHome()
    {
        Navigation.NavigateTo("/");
    }
}. (AuthService.cs)using Microsoft.AspNetCore.Components.Authorization;
using Microsoft.AspNetCore.Components.WebAssembly.Authentication;
using Microsoft.JSInterop;
using System;
using System.Linq;
using System.Threading.Tasks;

namespace AirCode.Services.Auth
{
    public interface IAuthService
    {
        Task<string> GetJwtTokenAsync();
        Task LogAuthenticationMessageAsync(string message);
        void LogAuthenticationMessage(string message);
        Task<AccessTokenResult> GetAccessTokenResultAsync();
    }

    public class AuthService : IAuthService
    {
        private readonly AuthenticationStateProvider _authStateProvider;
        private readonly IJSRuntime _jsRuntime;
        private readonly IAccessTokenProvider _tokenProvider;

        public AuthService(
            AuthenticationStateProvider authStateProvider, 
            IJSRuntime jsRuntime,
            IAccessTokenProvider tokenProvider)
        {
            _authStateProvider = authStateProvider;
            _jsRuntime = jsRuntime;
            _tokenProvider = tokenProvider;
        }

        public async Task<AccessTokenResult> GetAccessTokenResultAsync()
        {
            try
            {
                var tokenResult = await _tokenProvider.RequestAccessToken();
                await LogAuthenticationMessageAsync($"Access token request status: {tokenResult.Status}");
                return tokenResult;
            }
            catch (Exception ex)
            {
                await LogAuthenticationMessageAsync($"Error getting access token: {ex.Message}");
                return null;
            }
        }

        public async Task<string> GetJwtTokenAsync()
        {
            try
            {
                // Try to get token from AccessTokenProvider first
                var tokenResult = await _tokenProvider.RequestAccessToken();
                
                if (tokenResult.TryGetToken(out var accessToken))
                {
                    await LogAuthenticationMessageAsync($"Token successfully retrieved from provider");
                    return accessToken.Value;
                }
                
                // Fall back to looking in claims
                var authState = await _authStateProvider.GetAuthenticationStateAsync();
                var user = authState.User;

                if (user.Identity.IsAuthenticated)
                {
                    // Try various possible claim types for the token
                    foreach (var possibleClaimType in new[] { "access_token", "id_token", "token", "jwt" })
                    {
                        var claim = user.FindFirst(possibleClaimType);
                        if (claim != null)
                        {
                            await LogAuthenticationMessageAsync($"Token found in claim: {possibleClaimType}");
                            return claim.Value;
                        }
                    }
                    
                    // If we get here, log all claim types to help debugging
                    var claimTypes = string.Join(", ", user.Claims.Select(c => c.Type).Distinct());
                    await LogAuthenticationMessageAsync($"No token found. Available claim types: {claimTypes}");
                }
                
                return "Token not found";
            }
            catch (Exception ex)
            {
                await LogAuthenticationMessageAsync($"Error retrieving token: {ex.Message}");
                return $"Error: {ex.Message}";
            }
        }

        public void LogAuthenticationMessage(string message)
        {
            Console.WriteLine($"[Authentication] -> {message}");
        }
//i think we can use our unity debug method with the [class name] - - - [function name] -> log. method here to
        public async Task LogAuthenticationMessageAsync(string message)
        {
            LogAuthenticationMessage(message);
            await _jsRuntime.InvokeVoidAsync("console.log", $"[Authentication] -> {message}");
        }
    }
}  (Auth Helper.js) window.authHelper = {
    logAuthMessage: function (message) {
        console.log(`[Authentication] -> ${message}`);
    },

    storeJwtLocally: function (token) {
        // Store JWT in sessionStorage for retrieval elsewhere
        // Note: This is just for development/debugging purposes
        if (token) {
            sessionStorage.setItem('debug_jwt_token', token);
            console.log('[Authentication] -> JWT stored in sessionStorage for debugging');
            return true;
        }
        return false;
    },

    retrieveStoredJwt: function () {
        return sessionStorage.getItem('debug_jwt_token') || null;
    },

    clearStoredJwt: function () {
        sessionStorage.removeItem('debug_jwt_token');
        console.log('[Authentication] -> JWT cleared from sessionStorage');
    },

    // Function to extract tokens from browser storage directly
    extractAuthTokensFromStorage: function() {
        try {
            // Try to find authentication tokens in local storage
            const keys = Object.keys(localStorage);
            for (let i = 0; i < keys.length; i++) {
                const key = keys[i];
                if (key.includes('auth') || key.includes('token') || key.includes('oidc')) {
                    console.log(`[Authentication] -> Found potential token in localStorage: ${key}`);
                    try {
                        const value = localStorage.getItem(key);
                        // If it looks like a JWT (contains periods and is base64-ish)
                        if (value && value.includes('.') && value.length > 50) {
                            console.log(`[Authentication] -> This appears to be a JWT token`);
                            return value;
                        }
                    } catch (e) {
                        console.log(`[Authentication] -> Error reading localStorage key ${key}: ${e}`);
                    }
                }
            }

            // Try session storage as well
            const sessionKeys = Object.keys(sessionStorage);
            for (let i = 0; i < sessionKeys.length; i++) {
                const key = sessionKeys[i];
                if (key.includes('auth') || key.includes('token') || key.includes('oidc')) {
                    console.log(`[Authentication] -> Found potential token in sessionStorage: ${key}`);
                    try {
                        const value = sessionStorage.getItem(key);
                        // If it looks like a JWT (contains periods and is base64-ish)
                        if (value && value.includes('.') && value.length > 50) {
                            console.log(`[Authentication] -> This appears to be a JWT token`);
                            return value;
                        }
                    } catch (e) {
                        console.log(`[Authentication] -> Error reading sessionStorage key ${key}: ${e}`);
                    }
                }
            }

            return null;
        } catch (e) {
            console.log(`[Authentication] -> Error extracting tokens: ${e}`);

            return null;
        }
    }
};

there is also the route gard i got from one of the discussions but i havent got around to using it yet..

Hey @tukurukuinamdapiaati,
Focusing on the issue you are experiencing right now, i.e., the auth-info page is not showing, I’m afraid you should debug your code to understand why it’s not working as expected. Looking at the specific code, I can’t see any particular reason for the page not showing up, but as far as I can see, this is not an Auth0-related issue.

By the way, I’m not sure what the security requirements for your application are, but be aware that storing tokens on the client side is not a good idea. See here for more info. Keep in mind that SPAs are not native clients, which can benefit from secure storage to store tokens locally.

Ok i understand the problem but im unaware of the solution the thing the after login from auth0 it sends us to authentication-callback page which didn’t exist so it just returns us to index page , however when i created a dedicated authentication-callback page it did take us to the page however it said we were not authenticated . And the sotoring of the tokens was just for testing. I will be removing it later on