Sample 7 — Identity (external login + API keys)
Derived page. The behaviour described here is specified by the
external-identitycapability underopenspec/specs/. That specification is the source; this page explains and illustrates it. Where the two disagree, the specification is right and this page is a bug.
Concept: how callers get into a Stratara app. External OpenID Connect sign-in with hardened JIT provisioning for humans, API keys / PATs for machines, JWT-bearer for API tokens — all three routed by the auth-scheme selector.
- Code:
samples/Stratara.Sample.Identity - Lines: ~135
- Read time: 10–15 min
- What it doesn't have: no UI, no real identity provider — the OpenID Connect leg needs a live IdP, the API-key leg runs offline.
What you'll see
SampleIdentityDbContext— an ordinaryIdentityDbContext<IdentityUser>that callsbuilder.ApplyIdentityDirectoryModel()inOnModelCreating. This is the recommended hosting pattern: ASP.NET Identity's tables and Stratara's directory tables (tenant_membership,active_tenant,setting_entry,api_key) share one context and one migration lineage.- The authentication chain —
AddStrataraOpenIdConnect(configuration)+AddStrataraJwtBearer(configuration)+AddStrataraApiKey(), fronted byAddStrataraAuthSchemeSelector(), which picks the scheme from the request's shape rather than from per-endpoint scheme lists. AddStrataraExternalLoginProvisioning<IdentityUser>()— invoked from the OpenID ConnectOnTicketReceivedevent, the moment the external identity is validated and before a local session is issued. The sample adds an invitation gate limiting provisioning to@example.com.- The API-key lane —
POST /admin/api-keysissues a machine key;GET /api/whoamiaccepts it as theX-Api-Keyheader and reports the resolved actor and tenant.
Running
dotnet run --project samples/Stratara.Sample.Identity
The host boots without a provider and binds to Kestrel's default port — check the launch log for
the actual address, typically http://localhost:5000. The API-key lane is the part you can drive
immediately:
KEY=$(curl -s -X POST http://localhost:5000/admin/api-keys | jq -r .apiKey)
curl -s -H "X-Api-Key: $KEY" http://localhost:5000/api/whoami
{
"actor": "019f6fa4-4ba1-7d8c-87ae-97c1e8064a93",
"tenant": "11111111-1111-1111-1111-111111111111",
"scheme": "resolved by the auth-scheme selector from the request shape"
}
A wrong key returns a flat 401. To try a real login, point Identity:OpenIdConnect in
appsettings.json at your provider and browse to /login.
Key takeaways
- The raw key (
stk_+ 32 CSPRNG bytes) is returned exactly once; storage holds only its SHA-256 digest, so a database leak yields no usable credential. - Issuing a machine key materializes a
tenant_membershiprow keyed by the key id — machines flow through the same membership → role → permission plane as humans. There is no parallel authorization path to keep in sync. - External accounts link on the issuer's
sub, never on the mutable email claim. Auto-linking to an existing local account requires the email to be verified by the provider and already confirmed locally; otherwise provisioning returnsRequiresInteractiveLinkingrather than merging. - The ticket carries the same
stratara:tenant_idclaim the session-context middleware already reads — nothing downstream changes when you add a new sign-in path.
See the API Keys and Personal Access Tokens and External Login (OpenID Connect) + JIT Provisioning guides for the full walkthrough, and Sample 8 for what an authenticated identity is then allowed to do.