Skip to content

Identity SDK

dotnet add package Axowl.Sdk.Identity.Client (0.2.2 or later — see the caution below).

builder.Services.AddAxowlIdentity(opts =>
{
opts.OrganizationSlug = "my-org"; // your org slug
opts.Audience = "app_my_org_main"; // your application key
// opts.Authority defaults to https://testapi.axowl.com.
// opts.Issuer defaults to {Authority}/api/public/orgs/{OrganizationSlug} — the `iss` Axowl writes.
});
var app = builder.Build();
app.UseAuthentication();
app.UseAuthorization();
// Minimal APIs — the attribute on the handler, or the convention method
app.MapGet("/reports/monthly", [RequirePermission("report.monthly")] () => "…");
app.MapGet("/wallet", () => "…").RequirePermission("wallet.read");
// Controllers
[HttpGet("/wallet"), RequirePermission("wallet.read")]
[HttpPost("/wallet/admin"), RequirePermission("wallet.admin", ServerCheck = true)]
  • No token → 401, token without the permission → 403, on minimal APIs and controllers alike. The attribute is an authorization requirement that UseAuthorization() evaluates from endpoint metadata (RequirePermissionAttribute.cs).
  • Default (ServerCheck=false) — reads JWT claims (AxowlPrincipalAccessor.HasPermission, wildcard-aware), no round-trip. If the claims do not grant the scope, the server is asked once (the grant may be newer than the token).
  • ServerCheck=true — calls IAxowlIdentityClient.CheckPermissionAsync; fails closed (deny) on network failure.
  • ctx.User.GetAxowlPrincipal().Permissions is the list from the token’s permissions claim (a JSON-encoded string array, e.g. ["wallet.read","report.*"]).

JS/TS — usePermission / requirePermission

Section titled “JS/TS — usePermission / requirePermission”
const { can } = usePermission(); // React: can('report.view')
app.get('/api/reports', requirePermission('report.view'), handler); // Express middleware

Python — verify_token / AxowlAuth.require

Section titled “Python — verify_token / AxowlAuth.require”
ctx = verify_token(token, AxowlConfig(org_slug="my-org")) # claims fast path
has_permission(ctx.permissions, "report.view")
@app.get("/reports")
def reports(ctx: AxowlContext = Depends(auth.require("report.view"))): # FastAPI, 403 on miss
...

Server-authoritative: AxowlIdentityClient(api_key).check_permission(token, "report.view"). Full page: Python SDK.

Java — AxowlTokenVerifier / Permissions.require

Section titled “Java — AxowlTokenVerifier / Permissions.require”
AxowlContext ctx = verifier.verify(token); // claims fast path
Permissions.require(ctx, "report.view"); // throws AxowlPermissionException

Server-authoritative: new AxowlIdentityClient(apiKey, baseUrl).checkPermission(token, "report.view"). Full page: Java SDK.

The gRPC IdentityService (Introspect, CheckPermission, IdentityServiceImpl.cs:43,72) authenticates with the org API key (Bearer ah_live_…), validates the user token against the org RS256 JWKS, and reads permissions fresh from ConnectedIdPermissions (:226) so revocations apply immediately. Wildcard matching: *, x.*, exact (MatchScope, :263).

Introspect (gRPC, or REST POST /v1/identity/introspect) returns the resolved principal. The same fields surface on AxowlPrincipal in .NET:

FieldMeaning
activefalse = expired / invalid / revoked. All other fields are empty when inactive.
end_user_idThe end user’s id in this org.
organization_idThe org the token was issued for (must match your API key’s org).
connected_idThe user’s org identity badge (ConnectedId). Issued to every end user — customer or employee — one per org. This is the subject that permissions, seals, and billing attribution key on.
is_employeeServer-authoritative at introspect time: true when the badge’s subject is an internal org member. Do not infer employment from connected_id being present — it always is.
permissionsFresh ResolvedScope list (revocations already applied).