Skip to content

.NET EndUser auth

Axowl.Sdk signs end users into a Blazor WebAssembly app. Your app never sees a password: it hands the user to Axowl’s hosted Account Portal, and gets back a signed JWT it can send to your own API.

This is the end-user plane. For your server verifying those tokens see Reference → Authentication; for permission checks inside the app see SDK → Identity.

<ProjectReference Include="..\..\..\axowl-sdk\dotnet\Axowl.Sdk\Axowl.Sdk.csproj" />

AddAxowlSdk wires the token store, an AuthenticationStateProvider (so <AuthorizeView> works with no extra code), and IAxowlAuth. It needs an HttpClient already in the container (AxowlServiceCollectionExtensions.cs:15).

builder.Services.AddScoped(_ => new HttpClient { BaseAddress = new Uri(apiBaseUrl) });
builder.Services.AddAxowlSdk(o =>
{
o.ApiBaseUrl = "https://testapi.axowl.com"; // Axowl API
o.HostedLoginUrl = "https://login.axowl.com"; // Account Portal
o.ApplicationKey = "app_…"; // your Application's key
});
OptionPurpose
ApiBaseUrlAxowl API origin — the code-for-token exchange is called here
HostedLoginUrlAccount Portal origin the user is redirected to
ApplicationKeyIdentifies your Application (app_…)
CodeParamQuery key carrying the one-time code on return (default suits the portal)
TokenStorageKeylocalStorage key for the JWT (AxowlTokenStore.cs:19)

Sign-in is a redirect: the user leaves for the portal and comes back with a one-time code in the URL. Call HandleRedirectCallbackAsync() once on app start — typically in your layout’s OnInitializedAsync — or sign-in never finishes.

@inject Axowl.Sdk.IAxowlAuth Auth
protected override async Task OnInitializedAsync()
{
await Auth.HandleRedirectCallbackAsync();
}

It exchanges the code at /api/public/apps/{ApplicationKey}/auth/session/exchange, stores the JWT, flips auth state, and strips the code from the address bar. Returns true when a sign-in completed on that call (AxowlAuth.cs:31).

For a page that an anonymous visitor has no business seeing, RequireSignInAsync forwards to the portal and tells you to stop rendering.

protected override async Task OnInitializedAsync()
{
if (await Auth.RequireSignInAsync($"{Nav.BaseUri.TrimEnd('/')}/register"))
return; // redirecting — render nothing
// signed in (or a previous attempt failed) — carry on
}
ReturnsMeaning
trueA redirect was started. Return immediately.
falseSigned in or already attempted once in this browser session.

The second case matters: if sign-in cannot complete — the user cancelled, or the return URL isn’t an allowed Callback URL — forwarding again would trap them in a loop with nothing on screen. After one attempt the SDK stops and hands control back so you can show a “sign-in required” message. The marker lives in sessionStorage, so a new tab tomorrow forwards again; if storage is blocked (private mode, embedded webview) the SDK never forwards.

var token = await Auth.GetAccessTokenAsync();
if (token is not null)
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token);

Never returns an expired token — on expiry it clears the session and flips to signed-out, so your app re-authenticates instead of sending a dead token (AxowlAuth.cs:76).

await Auth.SignOutAsync(); // your app only

SignOutAsync clears the stored JWT and flips auth state to signed-out (AxowlAuth.cs:113). That is all it does. There is no server call, and no session outside your app is touched.

“Log out” means four different things in this system, and only the first is yours by default.

CallWhat it endsYours?
IAxowlAuth.SignOutAsync()The JWT your app stored✅ this is what the SDK does
POST /api/public/apps/{key}/auth/logoutNothing, but it emits EndUserLoggedOutEventoptional
GET /api/public/orgs/{slug}/end-sessionOIDC RP-initiated logout: validates post_logout_redirect_uri against your Application’s registered list, then redirectsOIDC clients only
POST /api/auth/logoutThe Axowl dashboard cookie session❌ not your plane

An EndUser JWT is a stateless bearer token. Your API verifies it against the org’s JWKS without ever calling Axowl, so there is no checkpoint at which Axowl could withdraw one mid-life. Signing out locally does not shorten the token you just discarded.

Add every URL you pass to SignIn / RequireSignInAsync — exact match, including any trailing slash — under Applications → your app → Authentication → Callback URLs. Returning to two different pages means two entries:

https://yourapp.com/
https://yourapp.com/register

This is the most common first-integration failure, because the sign-in itself looks fine right up until the portal refuses to come back.

IAxowlAuth (IAxowlAuth.cs):

MemberPurpose
CurrentUserThe signed-in end user, or null
HandleRedirectCallbackAsync()Finish sign-in on return — call once at app start
RequireSignInAsync(returnUrl)Gate a page; true = redirecting, stop rendering
SignIn(redirectUrl)Redirect to the portal now
BuildLoginUrl(redirectUrl)Build the portal URL without navigating
GetAccessTokenAsync()JWT for Authorization: Bearer, or null
SignOutAsync()Clear the local session — your app only, see Sign out

Build the login URL with BuildLoginUrl rather than assembling it from config — the portal’s URL shape belongs to the SDK, and hand-built copies break the day it changes.