Skip to main content

Local JWT Authentication

Integration Guide


Step 1: Install the package

dotnet add package PrimusSaaS.Identity.Validator

Step 2: Configure appsettings.json

Add this section to your appsettings.json:

{
"PrimusIdentity": {
"RequireHttpsMetadata": false,
"ValidateLifetime": true,
"ClockSkew": "00:05:00",
"Issuers": [
{
"Name": "LocalDev",
"Type": "Jwt",
"Issuer": "http://localhost:5001",
"Secret": "your-32-character-minimum-secret-key-here-change-this-in-production",
"Audiences": [ "api://local-dev" ]
}
]
}
}

What each field means

Top-level fields (full reference in the Overview):

KeyTypeRequiredDefaultWhat it does
RequireHttpsMetadataboolNotrueSet to false so the app accepts plain http:// locally. Change to true before deploying.
ValidateLifetimeboolNotrueRejects tokens that have expired. Keep true unless debugging a time-related issue.
ClockSkewstringNo"00:05:00"How much clock drift is tolerated between your machine and the token's expiry time. "HH:MM:SS" format. 5 minutes is a safe default.

Issuer fields for Local JWT (Type: "Jwt"):

KeyTypeRequiredWhat it does
NamestringYesA label for this issuer — shows up in logs and error messages only.
TypestringYesMust be "Jwt" for local HMAC tokens. Other valid values: AzureAd, Auth0, Okta, Cognito, Google, GitHub.
IssuerstringYesThe iss claim expected in incoming tokens. Must exactly match the URL you use when generating tokens — a trailing slash difference will cause a 401.
SecretstringYesHMAC-SHA256 signing key. Minimum 32 characters. Must be identical in both your config and your token generator. Use an environment variable or .NET User Secrets in production — never commit a real secret.
Audiencesstring[]YesThe aud claim(s) expected in incoming tokens. A token must match at least one entry. You choose the value — it just has to be the same in both config and token.

Step 3: Update Program.cs

This is everything — no extra files needed.

using PrimusSaaS.Identity.Validator;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddPrimusIdentity(opts =>
builder.Configuration.GetSection("PrimusIdentity").Bind(opts));
builder.Services.AddAuthorization();

var app = builder.Build();
app.UseAuthentication();
app.UseAuthorization();

// Protected endpoint — returns 401 if no valid JWT is present
app.MapGet("/local", () => Results.Ok(new { validated = true }))
.RequireAuthorization();

app.Run();
That's it

No controller file. No extra class. The endpoint lives right here in Program.cs.


Step 4: Get a token and test

Use the values from your appsettings.json to generate a real signed token right here in the browser:

Run the app:

dotnet run --urls "http://localhost:5001"

Call the protected endpoint (paste the token from above):

$token = "PASTE_YOUR_TOKEN_HERE"
Invoke-RestMethod "http://localhost:5001/local" -Headers @{ Authorization = "Bearer $token" }

Expected: { "validated": true }

Confirm it blocks unauthenticated requests:

try { Invoke-RestMethod "http://localhost:5001/local" } catch { $_.Exception.Response.StatusCode }

Expected: Unauthorized