Validating JWTs with .NET

To maintain secure access to your .NET application, it's essential to validate session tokens for each request. Descope issues these session tokens as JWTs (JSON Web Tokens), which are signed and can be verified using standard .NET libraries—no need for an external SDK.

While the Descope .NET SDK provides a simple and integrated way to handle session validation, this guide walks through how to manually configure JWT validation using built-in Microsoft packages.

This guide is particularly helpful if you're already using Microsoft's JwtBearer middleware with other authentication providers and want to extend it to validate Descope JWTs.

It's also ideal for those who prefer not to use third-party SDKs and want full control over the JWT validation process by handling it manually.

Generating an OIDC-Compliant JWT

To ensure your JWTs are OAuth compliant JWT tokens, you'll need to configure an AWS API Gateway JWT template for your Descope project.

Navigate to the JWT Templates section of your Project Settings and create a new template based off of the AWS API Gateway default template:

Enable API Gateway compliant JWT in Project Settings

JwtBearer Middleware (ASP.NET Core)

If you're building an ASP.NET Core application, the Microsoft.AspNetCore.Authentication.JwtBearer package is the recommended way to validate JWTs issued by Descope. You'll also need the Microsoft.IdentityModel.Tokens package to configure token validation parameters.

Install the required packages using the following commands:

Terminal
dotnet add package Microsoft.AspNetCore.Authentication.JwtBearer
dotnet add package Microsoft.IdentityModel.Tokens

Basic Configuration with AddJwtBearer

For most applications, the default configuration provided by .AddJwtBearer() is sufficient. You can define your JWT settings in appsettings.json, including:

  • Authority: Your Descope issuer URL (https://api.descope.com/<Your Descope Project ID>)

You can find the Issuer URL under the Settings of your default Descope Federated Application or any other federated application you may be using with your application.

  • ValidIssuer: Same as the Authority
  • ValidAudiences: Your Descope Project ID
appsettings.json
{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "AllowedHosts": "*",
  "Authentication": {
    "Schemes": {
      "Bearer": {
        "Authority": "https://api.descope.com/<Your Descope Project ID>",
        "ValidAudiences": [ "<Your Descope Project ID>" ],
        "ValidIssuer": "api.descope.com/<Your Descope Project ID>"
      }
    }
  }
}

Then, enable JWT authentication in your app with the following code:

Program.cs
using Microsoft.AspNetCore.Authentication.JwtBearer;
 
var builder = WebApplication.CreateBuilder(args);
 
builder.Services.AddAuthentication().AddJwtBearer();

Advanced Configuration with TokenValidationParameters

For more granular control, you can supply custom TokenValidationParameters when configuring the JWT middleware. This is useful if you want to customize how issuer, audience, expiration, and other claims are validated.

appsettings.json
{
  "Descope": {
    "Authority": "https://api.descope.com/<Your Descope Project ID>",
    "Audience": "<Your Descope Project ID>"
  }
}

Then configure your authentication logic in code:

Program.cs
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.IdentityModel.Tokens;
 
var builder = WebApplication.CreateBuilder(args);
 
string authority = builder.Configuration["Descope:Authority"];
string audience = builder.Configuration["Descope:Audience"];
 
builder.Services.AddAuthentication()
    .AddJwtBearer("Descope", options =>
    {
        options.Audience = audience;
        options.Authority = authority;
        options.TokenValidationParameters = new TokenValidationParameters
        {
            ValidateIssuer = true,
            ValidIssuer = authority,
            ValidateAudience = true,
            ValidAudiences = new[] { audience },
            ValidateLifetime = true,
            RequireExpirationTime = true,
            ClockSkew = TimeSpan.FromMinutes(2)
        };
    });

Token Validation in .NET Framework

If you're working in a .NET Framework environment (or another non-Core framework), you can validate JWTs using the Microsoft.IdentityModel.Tokens and Microsoft.IdentityModel.Protocols.OpenIdConnect libraries.

Install the required packages:

Terminal
dotnet add package Microsoft.IdentityModel.Tokens
dotnet add package Microsoft.IdentityModel.Protocols.OpenIdConnect

Define your token settings in appsettings.json:

appsettings.json
{
  "Descope": {
    "Authority": "https://api.descope.com/<Your Descope Project ID>",
    "Audience": "<Your Descope Project ID>"
  }
}

Step 1: Fetch OIDC Signing Keys

Use Descope's OIDC discovery endpoint to retrieve the public signing keys:

Program.cs
using Microsoft.IdentityModel.Tokens;
using Microsoft.IdentityModel.Protocols.OpenIdConnect;
using Microsoft.Extensions.Configuration;
 
var builder = WebApplication.CreateBuilder(args);
 
string authority = builder.Configuration["Descope:Authority"];
string audience = builder.Configuration["Descope:Audience"];
 
var configManager = new ConfigurationManager<OpenIdConnectConfiguration>(
    $"{authority}/.well-known/openid-configuration",
    new OpenIdConnectConfigurationRetriever(),
    new HttpDocumentRetriever());
 
var discoveryDocument = await configManager.GetConfigurationAsync();
var signingKeys = discoveryDocument.SigningKeys;

Step 2: Validate JWTs Manually

Once you've retrieved the signing keys, you can validate a token manually:

TokenValidation.cs
public bool ValidateToken(
    string token,
    string issuer,
    string audience,
    ICollection<SecurityKey> signingKeys,
    out JwtSecurityToken jwt
)
{
    jwt = null;
 
    var validationParams = new TokenValidationParameters
    {
        ValidateIssuer = true,
        ValidIssuer = issuer,
        ValidateAudience = true,
        ValidAudience = audience,
        ValidateIssuerSigningKey = true,
        IssuerSigningKeys = signingKeys,
        RequireExpirationTime = true,
        ValidateLifetime = true,
        ClockSkew = TimeSpan.FromMinutes(2)
    };
 
    try
    {
        var handler = new JwtSecurityTokenHandler();
        var principal = handler.ValidateToken(token, validationParams, out SecurityToken validatedToken);
        jwt = (JwtSecurityToken)validatedToken;
        return true;
    }
    catch (SecurityTokenValidationException)
    {
        // Handle invalid token
        return false;
    }
}
Was this helpful?

On this page