Base solution for your next web application
Open Closed

Can we use auth0 access token to call our api's? #12346


User avatar
0
kansoftware created

Can we use auth0 access token to call our api's?

Markdown is supported
Copy & paste or drag & drop images (max 30 MB per image)

13 Answer(s)
  • User Avatar
    0
    ismcagdas created
    Support Team

    Hi @kansoftware

    I think you can use auth0 as an external auth source via OpenIdConnect. But, when a user logins via Auth0, AspNet Zero itself creates an accessToken and uses it.

    If you want to use auth0 access token, you probably need to write a custom middleware to validate this token from Auth0.

    Markdown is supported
    Copy & paste or drag & drop images (max 30 MB per image)
  • User Avatar
    0
    kansoftware created

    [ismcagdas] said: Hi @kansoftware

    I think you can use auth0 as an external auth source via OpenIdConnect. But, when a user logins via Auth0, AspNet Zero itself creates an accessToken and uses it.

    If you want to use auth0 access token, you probably need to write a custom middleware to validate this token from Auth0.

    Could you please let me know how to proceed with the custom code

    Markdown is supported
    Copy & paste or drag & drop images (max 30 MB per image)
  • User Avatar
    0
    oguzhanagir created
    Support Team

    Hi @kansoftware,

    If your goal is to use Auth0's own access token instead of issuing a new one from ASP.NET Zero, you'll need to implement custom token validation logic.

    1. Extended the AuthConfigurer class to support both ASP.NET Zero’s JWT token and Auth0 tokens.

      • If Authentication:Auth0:Enabled is true, the token is validated using Auth0’s public keys (via the /.well-known/jwks.json endpoint).
      • We also extract Auth0 specific claims (sub, email, name) and add them to the identity.
    2. Created a custom Auth0JwtMiddleware:

      • This middleware extracts the Authorization header and validates the Auth0 token using the JWKS endpoint.
      • If valid, the HttpContext.User is set directly.
    3. Added a custom [Auth0Authorize] attribute to secure endpoints that require valid Auth0 tokens and enforce the presence of the sub claim.

    Example Code:

    AuthConfigurer.Configure method

    public static void Configure(IServiceCollection services, IConfiguration configuration)
    {
        var authenticationBuilder = services.AddAuthentication();
    
        if (bool.Parse(configuration["Authentication:JwtBearer:IsEnabled"]))
        {
            authenticationBuilder.AddAbpAsyncJwtBearer(options =>
            {
                var useAuth0 = bool.Parse(configuration["Authentication:Auth0:Enabled"] ?? "false");
    
                if (useAuth0)
                {
                    options.Authority = $"https://{configuration["Authentication:Auth0:Domain"]}/";
                    options.Audience = configuration["Authentication:Auth0:Audience"];
                    options.TokenValidationParameters = new TokenValidationParameters
                    {
                        ValidateIssuerSigningKey = true,
                        ValidateIssuer = true,
                        ValidIssuer = $"https://{configuration["Authentication:Auth0:Domain"]}/",
                        ValidateAudience = true,
                        ValidAudience = configuration["Authentication:Auth0:Audience"],
                        ValidateLifetime = true,
                        ClockSkew = TimeSpan.Zero
                    };
                }
                else
                {
                    options.TokenValidationParameters = new TokenValidationParameters
                    {
                        ValidateIssuerSigningKey = true,
                        IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(configuration["Authentication:JwtBearer:SecurityKey"])),
                        ValidateIssuer = true,
                        ValidIssuer = configuration["Authentication:JwtBearer:Issuer"],
                        ValidateAudience = true,
                        ValidAudience = configuration["Authentication:JwtBearer:Audience"],
                        ValidateLifetime = true,
                        ClockSkew = TimeSpan.Zero
                    };
                    options.AsyncSecurityTokenValidators.Clear();
                    options.AsyncSecurityTokenValidators.Add(new AbpZeroTemplateAsyncJwtSecurityTokenHandler());
                }
    
                options.Events = new JwtBearerEvents
                {
                    OnMessageReceived = QueryStringTokenResolver,
                    OnTokenValidated = async context =>
                    {
                        if (useAuth0)
                        {
                            var claimsIdentity = context.Principal.Identity as ClaimsIdentity;
    
                            var auth0UserId = claimsIdentity?.FindFirst("sub")?.Value;
                            var email = claimsIdentity?.FindFirst("email")?.Value;
                            var name = claimsIdentity?.FindFirst("name")?.Value;
    
                            if (!string.IsNullOrEmpty(auth0UserId))
                            {
                                claimsIdentity?.AddClaim(new Claim("auth0_user_id", auth0UserId));
                            }
    
                            if (!string.IsNullOrEmpty(email))
                            {
                                claimsIdentity?.AddClaim(new Claim(ClaimTypes.Email, email));
                            }
    
                            if (!string.IsNullOrEmpty(name))
                            {
                                claimsIdentity?.AddClaim(new Claim(ClaimTypes.Name, name));
                            }
                        }
                    }
                };
            });
        }
    }
    

    Add Auth0JwtMiddleware

    public class Auth0JwtMiddleware
    {
        private readonly RequestDelegate _next;
        private readonly IConfiguration _configuration;
    
        public Auth0JwtMiddleware(RequestDelegate next, IConfiguration configuration)
        {
            _next = next;
            _configuration = configuration;
        }
    
        public async Task InvokeAsync(HttpContext context)
        {
            var token = ExtractTokenFromHeader(context);
    
            if (!string.IsNullOrEmpty(token))
            {
                await ValidateAuth0Token(context, token);
            }
    
            await _next(context);
        }
    
        private string ExtractTokenFromHeader(HttpContext context)
        {
            var authHeader = context.Request.Headers["Authorization"].FirstOrDefault();
            if (authHeader?.StartsWith("Bearer ") == true)
            {
                return authHeader.Substring("Bearer ".Length).Trim();
            }
            return null;
        }
    
        private async Task ValidateAuth0Token(HttpContext context, string token)
        {
            try
            {
                var domain = _configuration["Auth0:Domain"];
                var audience = _configuration["Auth0:Audience"];
    
                var tokenHandler = new JwtSecurityTokenHandler();
                var validationParameters = new TokenValidationParameters
                {
                    ValidateIssuerSigningKey = true,
                    IssuerSigningKeyResolver = (token, securityToken, kid, parameters) =>
                    {
                        var client = new HttpClient();
                        var keyUri = $"https://{domain}/.well-known/jwks.json";
                        var response = client.GetAsync(keyUri).Result;
                        var keys = response.Content.ReadAsStringAsync().Result;
                        var jwks = JsonConvert.DeserializeObject<JsonWebKeySet>(keys);
                        return jwks.Keys;
                    },
                    ValidateIssuer = true,
                    ValidIssuer = $"https://{domain}/",
                    ValidateAudience = true,
                    ValidAudience = audience,
                    ValidateLifetime = true,
                    ClockSkew = TimeSpan.Zero
                };
    
                var principal = tokenHandler.ValidateToken(token, validationParameters, out var validatedToken);
                context.User = principal;
            }
            catch (Exception ex)
            {
                context.Response.StatusCode = 401;
                await context.Response.WriteAsync("Unauthorized");
                return;
            }
        }
    }
    

    Add Auth0AuthorizeAttribute

    public class Auth0AuthorizeAttribute : Attribute, IAuthorizationFilter
    {
        public void OnAuthorization(AuthorizationFilterContext context)
        {
            var user = context.HttpContext.User;
    
            if (!user.Identity.IsAuthenticated)
            {
                context.Result = new UnauthorizedResult();
                return;
            }
    
            var auth0UserId = user.FindFirst("sub")?.Value;
            if (string.IsNullOrEmpty(auth0UserId))
            {
                context.Result = new UnauthorizedResult();
                return;
            }
        }
    }
    

    You may need to test these operations after applying them to your own project.

    Markdown is supported
    Copy & paste or drag & drop images (max 30 MB per image)
  • User Avatar
    0
    kansoftware created

    [oguzhanagir] said: Auth0AuthorizeAttribute

    Thanks for the reply. I have multi tenant application and each tenant has different auth0 domain, audience etc. stored in the database. Therefore I can't use it from app settings, need a code which will dynamically set these values. Also where will I create Auth0AuthorizeAttribute and Middleware.

    Also I have openid connect setup already done for auth0 and okta dynamically

    public static void Configure(IServiceCollection services, IConfiguration configuration)
    {
        var authenticationBuilder = services.AddAuthentication();
    services.Replace(ServiceDescriptor.Scoped<IAuthenticationHandlerProvider, MyOpenIdAuthenticationHandlerProvider>());
    
    if (bool.Parse(configuration["Authentication:OpenId:IsEnabled"]) || bool.Parse(configuration["Authentication:Auth0:IsEnabled"]))
    {
        if (bool.Parse(configuration["Authentication:AllowSocialLoginSettingsPerTenant"]))
        {
            services.AddSingleton<IOptionsMonitor<OpenIdConnectOptions>, TenantBasedOpenIdConnectOptions>();
        }
    
        authenticationBuilder.AddOpenIdConnect(options =>
        {
            options.ClientId = configuration["Authentication:OpenId:ClientId"];
            options.Authority = configuration["Authentication:OpenId:Authority"];
            options.SignedOutRedirectUri = configuration["App:WebSiteRootAddress"] + "Account/Logout";
            options.ResponseType = configuration["Authentication:OpenId:ResponseType"];
    
            options.Events.OnRedirectToIdentityProvider = context =>
            {
                context.ProtocolMessage.RedirectUri = $"https://{context.Request.Host}/signin-oidc";
                return Task.CompletedTask;
            };
    
            options.TokenValidationParameters = new TokenValidationParameters()
            {
                ValidateIssuer = bool.Parse(configuration["Authentication:OpenId:ValidateIssuer"])
            };
    
            options.Events.OnTokenValidated = context =>
            {
                var jsonClaimMappings = new List<JsonClaimMap>();
                configuration.GetSection("Authentication:OpenId:ClaimsMapping").Bind(jsonClaimMappings);
    
                context.AddMappedClaims(jsonClaimMappings);
    
                return Task.FromResult(0);
            };
            
            var clientSecret = configuration["Authentication:OpenId:ClientSecret"];
            if (!clientSecret.IsNullOrEmpty())
            {
                options.ClientSecret = clientSecret;
            }                    
        });
        authenticationBuilder.Services.Replace(ServiceDescriptor.Transient<OpenIdConnectHandler, AbpOpenIdConnectHandler>());
    }
    }
    
    Markdown is supported
    Copy & paste or drag & drop images (max 30 MB per image)
  • User Avatar
    0
    oguzhanagir created
    Support Team

    Hi @kansoftware,

    Since you have a multi tenant setup and each tenant uses a different Auth0 domain, audience, you can't rely on static appsettings.json values. Instead, you need to implement dynamic token validation based on the tenant’s configuration stored in your database.

    Extend JwtBearer Events for Dynamic Validation

    Inside your AuthConfigurer, modify the JwtBearerOptions.Events to dynamically resolve the tenant and load its Auth0 settings for validation:

    options.Events = new JwtBearerEvents
    {
        OnMessageReceived = QueryStringTokenResolver,
        OnTokenValidated = async context =>
        {
            var tenantResolver = context.HttpContext.RequestServices.GetRequiredService<ITenantResolver>();
            var tenantId = await tenantResolver.ResolveTenantIdAsync(context.HttpContext);
    
            var tenantAuthSettings = await LoadTenantAuthSettingsAsync(tenantId);
            var claimsIdentity = context.Principal.Identity as ClaimsIdentity;
    
            var auth0UserId = claimsIdentity?.FindFirst("sub")?.Value;
            var email = claimsIdentity?.FindFirst("email")?.Value;
    
            if (!string.IsNullOrEmpty(auth0UserId))
            {
                claimsIdentity?.AddClaim(new Claim("auth0_user_id", auth0UserId));
            }
    
            if (!string.IsNullOrEmpty(email))
            {
                claimsIdentity?.AddClaim(new Claim(ClaimTypes.Email, email));
            }
        },
        OnAuthenticationFailed = context =>
        {
            context.Response.StatusCode = 401;
            return context.Response.WriteAsync("Unauthorized");
        }
    };
    

    LoadTenantAuthSettingsAsync should retrieve the Auth0 Domain, Audience, and any other settings for the tenant from your database.

    Dynamic JWKS Validation Based on Tenant

    You also need to resolve the public keys from Auth0’s JWKS endpoint per tenant:

    IssuerSigningKeyResolver = (token, securityToken, kid, validationParameters) =>
    {
        var tenantId = ResolveTenantIdFromToken(token);
        var settings = LoadTenantAuthSettings(tenantId);
    
        var client = new HttpClient();
        var keyUri = $"https://{settings.Domain}/.well-known/jwks.json";
        var response = client.GetAsync(keyUri).Result;
        var keys = response.Content.ReadAsStringAsync().Result;
        var jwks = JsonConvert.DeserializeObject<JsonWebKeySet>(keys);
        return jwks.Keys;
    }
    

    Add the Auth0 Middleware Globally

    In your Startup.cs or WebCoreModule, register your Auth0JwtMiddleware to handle and validate incoming Auth0 tokens:

    app.UseMiddleware<Auth0JwtMiddleware>();
    

    This middleware should extract the token, identify the tenant, fetch the JWKS, and validate the token accordingly.

    How to Identify the Tenant

    If tenant info is not present in the token itself, you can require a custom header like X-Tenant-Id in the request to resolve tenant specific configuration.

    Suggested File Structure

    • Auth0JwtMiddleware.cs Place in Web.Core/Middleware/
    • Auth0AuthorizeAttribute.cs Place in Web.Core/Authorization/
    • AuthConfigurer.cs Use in your existing startup config logic
    Markdown is supported
    Copy & paste or drag & drop images (max 30 MB per image)
  • User Avatar
    0
    kansoftware created

    [oguzhanagir] said: Hi @kansoftware,

    Since you have a multi tenant setup and each tenant uses a different Auth0 domain, audience, you can't rely on static appsettings.json values. Instead, you need to implement dynamic token validation based on the tenant’s configuration stored in your database.

    Extend JwtBearer Events for Dynamic Validation

    Inside your AuthConfigurer, modify the JwtBearerOptions.Events to dynamically resolve the tenant and load its Auth0 settings for validation:

    options.Events = new JwtBearerEvents 
    { 
        OnMessageReceived = QueryStringTokenResolver, 
        OnTokenValidated = async context => 
        { 
            var tenantResolver = context.HttpContext.RequestServices.GetRequiredService<ITenantResolver>(); 
            var tenantId = await tenantResolver.ResolveTenantIdAsync(context.HttpContext); 
     
            var tenantAuthSettings = await LoadTenantAuthSettingsAsync(tenantId); 
            var claimsIdentity = context.Principal.Identity as ClaimsIdentity; 
     
            var auth0UserId = claimsIdentity?.FindFirst("sub")?.Value; 
            var email = claimsIdentity?.FindFirst("email")?.Value; 
     
            if (!string.IsNullOrEmpty(auth0UserId)) 
            { 
                claimsIdentity?.AddClaim(new Claim("auth0_user_id", auth0UserId)); 
            } 
     
            if (!string.IsNullOrEmpty(email)) 
            { 
                claimsIdentity?.AddClaim(new Claim(ClaimTypes.Email, email)); 
            } 
        }, 
        OnAuthenticationFailed = context => 
        { 
            context.Response.StatusCode = 401; 
            return context.Response.WriteAsync("Unauthorized"); 
        } 
    }; 
    

    LoadTenantAuthSettingsAsync should retrieve the Auth0 Domain, Audience, and any other settings for the tenant from your database.

    Dynamic JWKS Validation Based on Tenant

    You also need to resolve the public keys from Auth0’s JWKS endpoint per tenant:

    IssuerSigningKeyResolver = (token, securityToken, kid, validationParameters) => 
    { 
        var tenantId = ResolveTenantIdFromToken(token); 
        var settings = LoadTenantAuthSettings(tenantId); 
     
        var client = new HttpClient(); 
        var keyUri = $"https://{settings.Domain}/.well-known/jwks.json"; 
        var response = client.GetAsync(keyUri).Result; 
        var keys = response.Content.ReadAsStringAsync().Result; 
        var jwks = JsonConvert.DeserializeObject<JsonWebKeySet>(keys); 
        return jwks.Keys; 
    } 
    

    Add the Auth0 Middleware Globally

    In your Startup.cs or WebCoreModule, register your Auth0JwtMiddleware to handle and validate incoming Auth0 tokens:

    app.UseMiddleware<Auth0JwtMiddleware>(); 
    

    This middleware should extract the token, identify the tenant, fetch the JWKS, and validate the token accordingly.

    How to Identify the Tenant

    If tenant info is not present in the token itself, you can require a custom header like X-Tenant-Id in the request to resolve tenant specific configuration.

    Suggested File Structure

    • Auth0JwtMiddleware.cs Place in Web.Core/Middleware/
    • Auth0AuthorizeAttribute.cs Place in Web.Core/Authorization/
    • AuthConfigurer.cs Use in your existing startup config logic

    There are few concerns I have

    1. When I setting the [Auth0Authorize] on the appservice, it is not allowing to use "using CDP.Web.Startup.AuthConfigurers;". So how can I add "Auth0Authorize" on appservice

    2. When I calling https://localhost:44302/App/Welcome through postman my ExtractTokenFromHeader function is getting called in the middleware but when I am navigating in the browser it does not get called why? Is that means the web application in the browser is still using the abp jwt token instead of auth0 token

    Markdown is supported
    Copy & paste or drag & drop images (max 30 MB per image)
  • User Avatar
    0
    oguzhanagir created
    Support Team

    Hi @kansoftware

    You mentioned moving Auth0AuthorizeAttribute.cs into CDP.Web.Startup.AuthConfigurers, but I’m unable to reference that namespace in my AppService (which is in the Application layer). I believe this is due to project dependency rules — the Application layer should not depend on the Web layer.

    To avoid a circular dependency, you can move the Auth0AuthorizeAttribute.cs into a shared project that both Application and Web layers can access — for example:

    CDP.Application.Contracts/Authorization/Auth0AuthorizeAttribute.cs
    

    This way, you can reference [Auth0Authorize] from your AppServices without violating the layering principles.

    You mentioned that your custom ExtractTokenFromHeader method in Auth0JwtMiddleware gets called in Postman, but not when navigating via browser. That’s expected behavior and here’s why:

    • When your Angular frontend is loaded in the browser, it likely still uses ABP’s access token stored in localStorage or cookies.

    • If you haven’t updated the Angular frontend to use Auth0’s access token and send it with every request (usually in the Authorization: Bearer <token> header), then the backend will not trigger your custom Auth0 middleware because no Auth0 token is being sent.

    • Ensure your frontend is logging in via Auth0 (e.g., using @auth0/auth0-angular or a custom OIDC integration).

    • After successful login, store the Auth0 access token and include it in each API request using Authorization header.

    const token = await auth0Client.getTokenSilently();
    http.get('/App/Welcome', {
      headers: {
        Authorization: `Bearer ${token}`
      }
    });
    

    In hybrid applications, it’s common to support both authentication schemes side by side. Cookie Authentication – used for traditional web applications MVC where the browser maintains a session. JWT Bearer Authentication used for stateless API access (Angular, Postman). In your Startup.cs or wherever you configure authentication, you can register both schemes.

    Markdown is supported
    Copy & paste or drag & drop images (max 30 MB per image)
  • User Avatar
    0
    kansoftware created

    [oguzhanagir] said: CDP.Application.Contracts

    Instead of creating files in new folder - CDP.Application.Contracts. I have added in CDP.Application folder. I hope this will not break ay principle or cause an issue?

    Also could you please guide through steps how can I use auth0 token via browser also.

    Markdown is supported
    Copy & paste or drag & drop images (max 30 MB per image)
  • User Avatar
    0
    oguzhanagir created
    Support Team

    Hi @kansoftware

    You just need to ensure the AuthService from @auth0/auth0-angular is properly initialized.

    Create HTTP Interceptor to Add Auth0 Token

    typescript
    // auth.interceptor.ts
    import { Injectable } from '@angular/core';
    import { HttpInterceptor, HttpRequest, HttpHandler } from '@angular/common/http';
    import { AuthService } from '@auth0/auth0-angular';
    import { switchMap } from 'rxjs/operators';
    
    @Injectable()
    export class AuthInterceptor implements HttpInterceptor {
      constructor(private auth: AuthService) {}
    
      intercept(req: HttpRequest<any>, next: HttpHandler) {
        return this.auth.getAccessTokenSilently().pipe(
          switchMap(token => {
            const authReq = req.clone({
              setHeaders: {
                Authorization: `Bearer ${token}`,
                'X-Tenant-Id': this.getCurrentTenantId()
              }
            });
            return next.handle(authReq);
          })
        );
      }
    
      private getCurrentTenantId(): string {
        return localStorage.getItem('currentTenantId') || '';
      }
    }
    

    Register Interceptor in service-proxy.module.ts

    @NgModule({
      providers: [
        //...
        {
          provide: HTTP_INTERCEPTORS,
          useClass: AuthInterceptor,
          multi: true
        }
      ]
    })
    
    Markdown is supported
    Copy & paste or drag & drop images (max 30 MB per image)
  • User Avatar
    0
    kansoftware created

    [oguzhanagir] said: Hi @kansoftware

    You just need to ensure the AuthService from @auth0/auth0-angular is properly initialized.

    Create HTTP Interceptor to Add Auth0 Token

    typescript 
    // auth.interceptor.ts 
    import { Injectable } from '@angular/core'; 
    import { HttpInterceptor, HttpRequest, HttpHandler } from '@angular/common/http'; 
    import { AuthService } from '@auth0/auth0-angular'; 
    import { switchMap } from 'rxjs/operators'; 
     
    @Injectable() 
    export class AuthInterceptor implements HttpInterceptor { 
      constructor(private auth: AuthService) {} 
     
      intercept(req: HttpRequest<any>, next: HttpHandler) { 
        return this.auth.getAccessTokenSilently().pipe( 
          switchMap(token => { 
            const authReq = req.clone({ 
              setHeaders: { 
                Authorization: `Bearer ${token}`, 
                'X-Tenant-Id': this.getCurrentTenantId() 
              } 
            }); 
            return next.handle(authReq); 
          }) 
        ); 
      } 
     
      private getCurrentTenantId(): string { 
        return localStorage.getItem('currentTenantId') || ''; 
      } 
    } 
    

    Register Interceptor in service-proxy.module.ts

    @NgModule({ 
      providers: [ 
        //... 
        { 
          provide: HTTP_INTERCEPTORS, 
          useClass: AuthInterceptor, 
          multi: true 
        } 
      ] 
    }) 
    

    Sorry I just miss to mention that we are using Javascript not angular

    Markdown is supported
    Copy & paste or drag & drop images (max 30 MB per image)
  • User Avatar
    0
    oguzhanagir created
    Support Team

    Hi @kansoftware

    Since your application is using MVC with jQuery, you won’t use Angular interceptors.

    Retrieve the Auth0 Access Token in JavaScript

    After logging in via Auth0 (using Auth0.js, auth0 spa js, or a custom OIDC flow), store the token in localStorage or a JavaScript variable. For example, if you’re using the Auth0 SPA SDK:

    // Initialize Auth0
    const auth0Client = await createAuth0Client({
      domain: 'YOUR_TENANT_DOMAIN',
      client_id: 'YOUR_CLIENT_ID',
      audience: 'YOUR_API_AUDIENCE',
      cacheLocation: 'localstorage'
    });
    
    //In fact, for multi tenant ASP.NET Zero MVC + jQuery, it’s better to fetch them dynamically from the server so the right Auth0 domain, client_id, and audience are used for each tenant.
    
    
    // Get token silently after login
    const token = await auth0Client.getTokenSilently();
    localStorage.setItem('auth0_access_token', token);
    

    Attach the Token to jQuery AJAX Requests

    You can use $.ajaxSetup to automatically send the Authorization header with every request.

    $.ajaxSetup({
      beforeSend: function (xhr) {
        const token = localStorage.getItem('auth0_access_token');
        if (token) {
          xhr.setRequestHeader('Authorization', 'Bearer ' + token);
          xhr.setRequestHeader('Abp-TenantId', getCurrentTenantId());
        }
      }
    });
    
    function getCurrentTenantId() {
      return localStorage.getItem('currentTenantId') || '';
    }
    

    Now, any jQuery AJAX request like:

    $.get('/App/Welcome', function (data) {
        console.log(data);
    });
    

    will automatically include the Auth0 token and tenant header.

    On the server side, your middleware or JWT Bearer configuration should validate the incoming Auth0 token dynamically per tenant, just like we talked about.

    This way, both Postman and browser requests will trigger your Auth0JwtMiddleware because the Auth0 token will be sent in the Authorization header for every API call.

    Markdown is supported
    Copy & paste or drag & drop images (max 30 MB per image)
  • User Avatar
    0
    kansoftware created

    [oguzhanagir] said: Hi @kansoftware

    Since your application is using MVC with jQuery, you won’t use Angular interceptors.

    Retrieve the Auth0 Access Token in JavaScript

    After logging in via Auth0 (using Auth0.js, auth0 spa js, or a custom OIDC flow), store the token in localStorage or a JavaScript variable. For example, if you’re using the Auth0 SPA SDK:

    // Initialize Auth0 
    const auth0Client = await createAuth0Client({ 
      domain: 'YOUR_TENANT_DOMAIN', 
      client_id: 'YOUR_CLIENT_ID', 
      audience: 'YOUR_API_AUDIENCE', 
      cacheLocation: 'localstorage' 
    }); 
     
    //In fact, for multi tenant ASP.NET Zero MVC + jQuery, it’s better to fetch them dynamically from the server so the right Auth0 domain, client_id, and audience are used for each tenant. 
     
     
    // Get token silently after login 
    const token = await auth0Client.getTokenSilently(); 
    localStorage.setItem('auth0_access_token', token); 
    

    Attach the Token to jQuery AJAX Requests

    You can use $.ajaxSetup to automatically send the Authorization header with every request.

    $.ajaxSetup({ 
      beforeSend: function (xhr) { 
        const token = localStorage.getItem('auth0_access_token'); 
        if (token) { 
          xhr.setRequestHeader('Authorization', 'Bearer ' + token); 
          xhr.setRequestHeader('Abp-TenantId', getCurrentTenantId()); 
        } 
      } 
    }); 
     
    function getCurrentTenantId() { 
      return localStorage.getItem('currentTenantId') || ''; 
    } 
    

    Now, any jQuery AJAX request like:

    $.get('/App/Welcome', function (data) { 
        console.log(data); 
    }); 
    

    will automatically include the Auth0 token and tenant header.

    On the server side, your middleware or JWT Bearer configuration should validate the incoming Auth0 token dynamically per tenant, just like we talked about.

    This way, both Postman and browser requests will trigger your Auth0JwtMiddleware because the Auth0 token will be sent in the Authorization header for every API call.

    Is localstorage secured? Also we work on multiple web server will it be a appropriate approach?

    Markdown is supported
    Copy & paste or drag & drop images (max 30 MB per image)
  • User Avatar
    0
    oguzhanagir created
    Support Team

    Hi @kansoftware,

    For a more secure approach in ASP.NET Zero (MVC + jQuery):

    Prefer HttpOnly Cookies for Access Tokens

    • Store the Auth0 access token (or a short lived session token from your backend) in an HttpOnly, Secure cookie.
    • This prevents JavaScript from accessing it directly, reducing XSS risk.
    • ASP.NET Zero already has a pattern for storing its own JWT in cookies you can apply the same for Auth0 tokens.

    Short lived Tokens + Refresh

    • If you must store in localStorage, ensure tokens are short lived (5–10 minutes) and use a silent refresh mechanism from Auth0 to renew them.
    • This minimizes the impact if a token is stolen.

    Server side Validation

    • Even if you pass the Auth0 token from browser API, always validate it server side with the tenant’s Auth0 JWKS to ensure it’s valid and unmodified.

    Multiple Web Servers

    • Since token validation happens against Auth0’s public keys (via JWKS) and not against in memory data, this approach works fine in a load balanced multi server ASP.NET Zero deployment.
    • No need for token storage replication between servers each request is independently validated.
    Markdown is supported
    Copy & paste or drag & drop images (max 30 MB per image)