Auth & Identity in Cratis Arc
This skill covers the full auth and identity stack in a Cratis Arc application. Read the relevant reference files below for detailed API usage.
Read the relevant instruction files first. This skill references concepts from the core copilot instructions in .github/copilot-instructions.md. If you need details on vertical slices, commands, queries, or proxy generation, consult those instructions.
Architecture Overview
Identity and auth in Arc follow a cookie-first, convention-based pattern:
Frontend (React) Backend (ASP.NET Core)
───────────────── ──────────────────────
<IdentityProvider> app.MapIdentityProvider()
└── useIdentity() hook └── GET /.cratis/me
│ │
├─ 1. Read .cratis-identity cookie │
└─ 2. If no cookie → fetch /.cratis/me │
▼
AuthenticationMiddleware
└── IAuthenticationHandler[]
│ sets HttpContext.User
▼
IIdentityProvider.Get()
└── IProvideIdentityDetails.Provide()
│
▼
IdentityProviderResult
→ JSON response
→ .cratis-identity cookie (base64)
Authorization:
[Authorize] / [Roles("Admin")] / [AllowAnonymous]
└── AuthorizationEvaluator checks per command/query
Key design decisions:
- The
.cratis-identity cookie is HttpOnly=false so the frontend JavaScript can read it directly — no extra HTTP call needed on page load.
- Identity details are base64-encoded JSON in the cookie, automatically decoded by the frontend
IdentityProvider.
- Only one
IProvideIdentityDetails implementation is allowed per application (auto-discovered). If none exists, a default provider grants access to everyone.
- Cratis has its own
[Authorize], [AllowAnonymous], and [Roles] attributes in Cratis.Arc.Authorization — these are distinct from ASP.NET Core's and are evaluated by AuthorizationEvaluator in the command and query pipeline.
Decision Tree — Which Reference to Read
Use this decision tree to determine which reference file(s) to read based on the user's task:
| User wants to... |
Read this reference |
| Add identity details to their app |
references/backend-identity.md |
Customize IProvideIdentityDetails (enrich from DB, block users, multi-tenant, preferences) |
references/backend-identity.md |
Modify identity at runtime (stateless selections, ModifyDetails) |
references/backend-identity.md |
| Use Azure AD / Entra ID / Microsoft Identity |
references/authentication.md |
| Write a custom authentication handler (API key, JWT, etc.) |
references/authentication.md |
| Protect commands or queries with roles |
references/authorization.md |
Set up [Authorize], [AllowAnonymous], or [Roles] |
references/authorization.md |
| Consume identity in React |
references/frontend.md |
| Use identity in MVVM or vanilla TypeScript |
references/frontend.md |
| Test identity locally without Azure |
references/local-development.md |
| Full-stack setup (backend + frontend) |
Read all references in order |
For full-stack tasks, read in this order:
references/backend-identity.md — identity provider and startup
references/authentication.md — how users are authenticated
references/authorization.md — protecting commands and queries
references/frontend.md — consuming identity in the UI
references/local-development.md — testing without real infrastructure
Quick-Start: Full-Stack Identity Setup
This is the minimum checklist for an application with identity. Read the reference files for details on each step.
Backend
- Details record: Define a C# record for application-specific user information
- Identity provider: Implement
IProvideIdentityDetails<TDetails> (auto-discovered, one per app)
- Startup: Call
app.MapIdentityProvider() to register GET /.cratis/me
- Authentication: Add
AddMicrosoftIdentityPlatformIdentityAuthentication() or implement IAuthenticationHandler
- Authorization: Add
[Authorize] / [Roles] / [AllowAnonymous] attributes from Cratis.Arc.Authorization to commands and queries
Frontend
- Provider: Wrap app root with
<IdentityProvider> from @cratis/arc.react/identity
- Hook: Use
useIdentity<TDetails>() to access identity anywhere in the component tree
- Roles: Use
identity.isInRole('Admin') for UI-level role gating
Proxy Generation
- Using
IProvideIdentityDetails<TDetails> (generic) enables automatic TypeScript type generation at dotnet build time — the generated types can be imported in the frontend for end-to-end type safety.
Critical Rules
These rules are frequently violated — always enforce them:
- One identity provider per app: Only one
IProvideIdentityDetails implementation is allowed. Multiple throws MultipleIdentityDetailsProvidersFound.
- Use Cratis attributes, not ASP.NET Core's:
[Authorize], [Roles], [AllowAnonymous] must come from Cratis.Arc.Authorization, not Microsoft.AspNetCore.Authorization.
- Never combine
[Authorize] and [AllowAnonymous] on the same target: This throws AmbiguousAuthorizationLevel.
- Prefer the generic interface: Use
IProvideIdentityDetails<TDetails> over IProvideIdentityDetails to enable proxy generation.
- Auto-discovery: Both
IProvideIdentityDetails and IAuthenticationHandler implementations are auto-discovered — no DI registration needed.
- Frontend role checks are UX, not security:
isInRole() on the frontend hides UI elements. The backend [Roles] attribute is the actual security boundary.
- Build before frontend: TypeScript proxy types for identity details are generated by
dotnet build. The backend must compile before the frontend can import them.
- Authorize at the boundary, secure by default: express access with
[Authorize]/[Roles] attributes on the command/query — never gate behavior with an if (identity.IsInRole(...)) inside Handle(). For app-wide protection, configure a default-deny fallback policy so a target is protected unless it explicitly opts out with [AllowAnonymous]. Cross-cutting auth that spans many commands belongs in an ICommandFilter, not duplicated per handler.
- Command-specific scope is validation, not an attribute: a rule like "may only act on resources in your own organization" belongs in the
CommandValidator<T> (inject the identity and reject with a validation error) — not in Handle(), and not expressible by a role attribute alone.
- Read the current user from the authenticated principal: in backend code resolve the current user from
IHttpContextAccessor.User (the ASP.NET principal the authentication handler populates). Do not invent a bespoke IIdentityAccessor abstraction — that is a product-specific wrapper, not part of generic Cratis.
Common Code Patterns
Protecting a command with roles
[Command]
[Roles("Admin")]
public record PromoteUser(UserId Id)
{
public void Handle(IUserService users) => users.Promote(Id);
}
Conditionally rendering UI based on roles
const identity = useIdentity();
return identity.isInRole('Admin')
? <AdminPanel />
: <AccessDenied />;
Modifying identity at runtime (stateless selections)
public class SetDepartment(IIdentityProvider identityProvider)
{
public async Task Handle(string department) =>
await identityProvider.ModifyDetails<UserDetails>(
details => details with { SelectedDepartment = department });
}
Reference Documentation
Skill references (detailed implementation guidance)
- Backend Identity Provider —
IProvideIdentityDetails, IdentityProviderContext, cookie mechanics, proxy generation, ModifyDetails
- Authentication —
IAuthenticationHandler, AuthenticationResult, Microsoft Identity Platform, combining handlers
- Authorization —
[Authorize], [Roles], [AllowAnonymous], inheritance rules, fallback policies
- Frontend Identity — React
IdentityProvider, useIdentity(), MVVM, core identity, role checking
- Local Development — Generating principals, ModHeader, cookie fallback, dev testing
1---2name: auth-and-identity3description: Use this skill for authentication, authorization, or identity in a Cratis Arc project — backend, frontend, or both. Covers identity providers (`IProvideIdentityDetails`), protecting commands/queries with authorization attributes, Microsoft Identity Platform, connecting backend identity to React, multi-tenant identity, and local-dev generated principals. Trigger on auth, login, roles, permissions, identity details, user context, or protecting endpoints.4---56# Auth & Identity in Cratis Arc78This skill covers the full auth and identity stack in a Cratis Arc application. Read the relevant reference files below for detailed API usage.910> **Read the relevant instruction files first.** This skill references concepts from the core copilot instructions in `.github/copilot-instructions.md`. If you need details on vertical slices, commands, queries, or proxy generation, consult those instructions.1112## Architecture Overview1314Identity and auth in Arc follow a cookie-first, convention-based pattern:1516```17Frontend (React) Backend (ASP.NET Core)18───────────────── ──────────────────────19<IdentityProvider> app.MapIdentityProvider()20 └── useIdentity() hook └── GET /.cratis/me21 │ │22 ├─ 1. Read .cratis-identity cookie │23 └─ 2. If no cookie → fetch /.cratis/me │24 ▼25 AuthenticationMiddleware26 └── IAuthenticationHandler[]27 │ sets HttpContext.User28 ▼29 IIdentityProvider.Get()30 └── IProvideIdentityDetails.Provide()31 │32 ▼33 IdentityProviderResult34 → JSON response35 → .cratis-identity cookie (base64)3637Authorization:38 [Authorize] / [Roles("Admin")] / [AllowAnonymous]39 └── AuthorizationEvaluator checks per command/query40```4142**Key design decisions:**43- The `.cratis-identity` cookie is `HttpOnly=false` so the frontend JavaScript can read it directly — no extra HTTP call needed on page load.44- Identity details are base64-encoded JSON in the cookie, automatically decoded by the frontend `IdentityProvider`.45- Only one `IProvideIdentityDetails` implementation is allowed per application (auto-discovered). If none exists, a default provider grants access to everyone.46- Cratis has its own `[Authorize]`, `[AllowAnonymous]`, and `[Roles]` attributes in `Cratis.Arc.Authorization` — these are distinct from ASP.NET Core's and are evaluated by `AuthorizationEvaluator` in the command and query pipeline.4748---4950## Decision Tree — Which Reference to Read5152Use this decision tree to determine which reference file(s) to read based on the user's task:5354| User wants to... | Read this reference |55|---|---|56| Add identity details to their app | [references/backend-identity.md](references/backend-identity.md) |57| Customize `IProvideIdentityDetails` (enrich from DB, block users, multi-tenant, preferences) | [references/backend-identity.md](references/backend-identity.md) |58| Modify identity at runtime (stateless selections, `ModifyDetails`) | [references/backend-identity.md](references/backend-identity.md) |59| Use Azure AD / Entra ID / Microsoft Identity | [references/authentication.md](references/authentication.md) |60| Write a custom authentication handler (API key, JWT, etc.) | [references/authentication.md](references/authentication.md) |61| Protect commands or queries with roles | [references/authorization.md](references/authorization.md) |62| Set up `[Authorize]`, `[AllowAnonymous]`, or `[Roles]` | [references/authorization.md](references/authorization.md) |63| Consume identity in React | [references/frontend.md](references/frontend.md) |64| Use identity in MVVM or vanilla TypeScript | [references/frontend.md](references/frontend.md) |65| Test identity locally without Azure | [references/local-development.md](references/local-development.md) |66| Full-stack setup (backend + frontend) | Read all references in order |6768**For full-stack tasks, read in this order:**691. `references/backend-identity.md` — identity provider and startup702. `references/authentication.md` — how users are authenticated713. `references/authorization.md` — protecting commands and queries724. `references/frontend.md` — consuming identity in the UI735. `references/local-development.md` — testing without real infrastructure7475---7677## Quick-Start: Full-Stack Identity Setup7879This is the minimum checklist for an application with identity. Read the reference files for details on each step.8081### Backend82831. **Details record**: Define a C# record for application-specific user information842. **Identity provider**: Implement `IProvideIdentityDetails<TDetails>` (auto-discovered, one per app)853. **Startup**: Call `app.MapIdentityProvider()` to register `GET /.cratis/me`864. **Authentication**: Add `AddMicrosoftIdentityPlatformIdentityAuthentication()` or implement `IAuthenticationHandler`875. **Authorization**: Add `[Authorize]` / `[Roles]` / `[AllowAnonymous]` attributes from `Cratis.Arc.Authorization` to commands and queries8889### Frontend90916. **Provider**: Wrap app root with `<IdentityProvider>` from `@cratis/arc.react/identity`927. **Hook**: Use `useIdentity<TDetails>()` to access identity anywhere in the component tree938. **Roles**: Use `identity.isInRole('Admin')` for UI-level role gating9495### Proxy Generation96979. Using `IProvideIdentityDetails<TDetails>` (generic) enables automatic TypeScript type generation at `dotnet build` time — the generated types can be imported in the frontend for end-to-end type safety.9899---100101## Critical Rules102103These rules are frequently violated — always enforce them:1041051. **One identity provider per app**: Only one `IProvideIdentityDetails` implementation is allowed. Multiple throws `MultipleIdentityDetailsProvidersFound`.1062. **Use Cratis attributes, not ASP.NET Core's**: `[Authorize]`, `[Roles]`, `[AllowAnonymous]` must come from `Cratis.Arc.Authorization`, not `Microsoft.AspNetCore.Authorization`.1073. **Never combine `[Authorize]` and `[AllowAnonymous]` on the same target**: This throws `AmbiguousAuthorizationLevel`.1084. **Prefer the generic interface**: Use `IProvideIdentityDetails<TDetails>` over `IProvideIdentityDetails` to enable proxy generation.1095. **Auto-discovery**: Both `IProvideIdentityDetails` and `IAuthenticationHandler` implementations are auto-discovered — no DI registration needed.1106. **Frontend role checks are UX, not security**: `isInRole()` on the frontend hides UI elements. The backend `[Roles]` attribute is the actual security boundary.1117. **Build before frontend**: TypeScript proxy types for identity details are generated by `dotnet build`. The backend must compile before the frontend can import them.1128. **Authorize at the boundary, secure by default**: express access with `[Authorize]`/`[Roles]` attributes on the command/query — **never** gate behavior with an `if (identity.IsInRole(...))` inside `Handle()`. For app-wide protection, configure a default-deny fallback policy so a target is protected unless it explicitly opts out with `[AllowAnonymous]`. Cross-cutting auth that spans many commands belongs in an `ICommandFilter`, not duplicated per handler.1139. **Command-specific scope is validation, not an attribute**: a rule like "may only act on resources in your own organization" belongs in the `CommandValidator<T>` (inject the identity and reject with a validation error) — not in `Handle()`, and not expressible by a role attribute alone.11410. **Read the current user from the authenticated principal**: in backend code resolve the current user from `IHttpContextAccessor.User` (the ASP.NET principal the authentication handler populates). Do not invent a bespoke `IIdentityAccessor` abstraction — that is a product-specific wrapper, not part of generic Cratis.115116---117118## Common Code Patterns119120### Protecting a command with roles121122```csharp123[Command]124[Roles("Admin")]125public record PromoteUser(UserId Id)126{127 public void Handle(IUserService users) => users.Promote(Id);128}129```130131### Conditionally rendering UI based on roles132133```tsx134const identity = useIdentity();135136return identity.isInRole('Admin')137 ? <AdminPanel />138 : <AccessDenied />;139```140141### Modifying identity at runtime (stateless selections)142143```csharp144public class SetDepartment(IIdentityProvider identityProvider)145{146 public async Task Handle(string department) =>147 await identityProvider.ModifyDetails<UserDetails>(148 details => details with { SelectedDepartment = department });149}150```151152---153154## Reference Documentation155156### Skill references (detailed implementation guidance)157158- [Backend Identity Provider](references/backend-identity.md) — `IProvideIdentityDetails`, `IdentityProviderContext`, cookie mechanics, proxy generation, `ModifyDetails`159- [Authentication](references/authentication.md) — `IAuthenticationHandler`, `AuthenticationResult`, Microsoft Identity Platform, combining handlers160- [Authorization](references/authorization.md) — `[Authorize]`, `[Roles]`, `[AllowAnonymous]`, inheritance rules, fallback policies161- [Frontend Identity](references/frontend.md) — React `IdentityProvider`, `useIdentity()`, MVVM, core identity, role checking162- [Local Development](references/local-development.md) — Generating principals, ModHeader, cookie fallback, dev testing