Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 15 additions & 2 deletions Documentation/aspire/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,7 @@ custom selection page and the full flow.

---

## Invites and lobby
## Invites, registration and lobby

### Core invite configuration

Expand Down Expand Up @@ -256,5 +256,18 @@ authproxy
`WithLobbyFrontend` and `WithLobbyBackend` both accept an optional `endpointName` parameter
(defaults to `"http"`).

See [Invites & Lobby](../configuration/invites.md) for the full invite flow walkthrough.
### Registration

To send users through the AuthProxy registration bootstrap flow, configure a lobby registration URL:

```csharp
authproxy.WithLobbyRegistration(lobbyResource, "/register");

// or use a raw URL
authproxy.WithLobbyRegistration("https://lobby.example.com/register");
```

This sets `Cratis:AuthProxy:Invite:Lobby:Registration:BaseUrl`. Users who visit `/register`
authenticate through AuthProxy and are then redirected to that registration URL.

See [Lobby](../configuration/lobby/index.md) for the onboarding flow walkthroughs.
2 changes: 1 addition & 1 deletion Documentation/configuration/error-pages.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ indicating that the authenticated user's subject is already associated with an e
The user should sign in with their existing account rather than completing the invitation again.

If you prefer to redirect users to a custom URL instead of serving this page, configure
`Invite.SubjectAlreadyExistsUrl` (see [Invites & Lobby](invites.md#invite-properties)).
`Invite.SubjectAlreadyExistsUrl` (see [Invitation for Creating Organization](lobby/invitation-for-creating-organization.md#configuration)).

---

Expand Down
2 changes: 1 addition & 1 deletion Documentation/configuration/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,5 +25,5 @@ Cratis AuthProxy is configured entirely through the `Cratis:AuthProxy` section o
| [Tenancy](tenancy.md) | How the auth proxy resolves the current tenant from each request, and how to verify tenant existence. |
| [Tenant Selection Page](tenant-selection.md) | How selection-based tenant resolution works and how to build/override `select-tenant.html`. |
| [Services](services.md) | Routing requests to backend and frontend services. |
| [Invites & Lobby](invites.md) | Invite-based onboarding and the lobby service. |
| [Lobby](lobby/index.md) | Invite and registration flows that hand users off to the lobby experience. |
| [Well-Known Pages](well-known-pages.md) | Built-in HTML pages (provider selection, errors, tenant not found) and how to override them via a mounted volume. |
176 changes: 0 additions & 176 deletions Documentation/configuration/invites.md

This file was deleted.

49 changes: 49 additions & 0 deletions Documentation/configuration/lobby/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# Lobby

AuthProxy uses the lobby to finish onboarding when a user cannot enter the application directly
after authentication.

Use the lobby documentation based on the onboarding outcome you need:

- [Invitation for Creating Organization](invitation-for-creating-organization.md) — invite a user
who should create a new organization after they sign in.
- [Invitation to Organization](invitation-to-organization.md) — invite a user directly into an
existing organization.
- [Registration](registration.md) — let a user start a self-serve registration flow that ends in
organization creation. If the user should join an existing organization, invite them instead.

## Shared configuration

All lobby-related settings live under `Cratis:AuthProxy:Invite:Lobby`:

```json
{
"Cratis": {
"AuthProxy": {
"Invite": {
"Lobby": {
"Frontend": { "BaseUrl": "http://lobby-service:3000/" },
"Backend": { "BaseUrl": "http://lobby-service:8080/" },
"Registration": { "BaseUrl": "http://lobby-service:3000/register" }
}
}
}
}
}
```

The `Lobby` object uses the standard [`Service`](../services.md) shape.

| Property | Type | Description |
|----------|------|-------------|
| `Frontend.BaseUrl` | `string` | URL used for lobby redirects after onboarding or when tenant resolution fails. |
| `Backend.BaseUrl` | `string` | Optional backend API URL for the lobby service. |
| `Registration.BaseUrl` | `string` | Optional URL used after `GET /register` completes successfully. |

## Lobby fallback

Tenant resolution runs before invite and registration handling.

When AuthProxy cannot resolve a tenant and a lobby frontend is configured, it redirects the user to
`Lobby.Frontend.BaseUrl` unless the request is already an onboarding bootstrap path such as
`/invite/<token>` or `/register`.
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
# Invitation for Creating Organization

Use this flow when you invite a user who should create a new organization after they authenticate.
AuthProxy validates the invite, exchanges it after sign-in, and then sends the user to the lobby
frontend to finish onboarding.

## Flow

1. The user opens `https://your-authproxy/invite/<token>`.
2. AuthProxy validates the signed JWT invite token.
3. If the token is valid, AuthProxy stores it in a short-lived HTTP-only cookie.
- With one configured identity provider, AuthProxy challenges that provider immediately.
- With multiple providers, AuthProxy serves `invitation-select-provider.html` so the user can
choose how to sign in.
4. After a successful login, AuthProxy calls `Invite.ExchangeUrl` with the invite token and the
authenticated user's subject.
5. If the exchange succeeds, AuthProxy redirects the user to `Invite.Lobby.Frontend.BaseUrl`.

This flow is the right fit when the invited user is not entering an already-resolved tenant.

## Configuration

```json
{
"Cratis": {
"AuthProxy": {
"Invite": {
"PublicKeyPem": "-----BEGIN PUBLIC KEY-----\n...\n-----END PUBLIC KEY-----",
"Issuer": "https://studio.example.com",
"Audience": "authproxy",
"ExchangeUrl": "https://studio.example.com/internal/invites/exchange",
"SubjectAlreadyExistsUrl": "https://app.example.com/errors/account-already-exists",
"AppendInvitationIdToQueryString": true,
"InvitationIdQueryStringKey": "invitationId",
"ClaimsToForward": [
{ "FromClaimType": "organization_id", "ToClaimType": "organization" },
{ "FromClaimType": "invited_by" }
],
"Lobby": {
"Frontend": { "BaseUrl": "http://lobby-service:3000/" },
"Backend": { "BaseUrl": "http://lobby-service:8080/" }
}
}
}
}
}
```

| Property | Type | Description |
|----------|------|-------------|
| `PublicKeyPem` | `string` | PEM-encoded RSA public key used to verify invite token signatures. |
| `Issuer` | `string` | Expected `iss` claim. Leave empty to skip issuer validation. |
| `Audience` | `string` | Expected `aud` claim. Leave empty to skip audience validation. |
| `ExchangeUrl` | `string` | Absolute URL of the invite exchange endpoint. |
| `SubjectAlreadyExistsUrl` | `string` | Redirect target when the exchange endpoint returns HTTP 409. Leave empty to serve `invitation-subject-already-exists.html`. |
| `AppendInvitationIdToQueryString` | `bool` | Appends `jti` from the invite token to the lobby redirect URL when enabled. |
| `InvitationIdQueryStringKey` | `string` | Query-string key used when appending the invitation ID. |
| `ClaimsToForward` | `InviteClaimForwarding[]` | Claim mappings forwarded from the invite token into the identity details request. |
| `Lobby.Frontend.BaseUrl` | `string` | Lobby URL used after a successful invite exchange. |

## Invite claim forwarding

When `ClaimsToForward` is configured and a pending invite cookie exists, AuthProxy reads the
configured invite-token claims and adds them to the principal payload sent to each `/.cratis/me`
identity details endpoint.

- Existing identity-provider claims are preserved.
- Mapped invite claims are appended if present.
- If `ToClaimType` is empty, AuthProxy uses `FromClaimType`.

## Invite token format

Invite tokens are JWTs signed with an RSA private key held by the issuing service. AuthProxy only
needs the matching public key to validate the signature.

Recommended claims:

| Claim | Description |
|-------|-------------|
| `iss` | Issuer. Must match `Invite.Issuer` when configured. |
| `aud` | Audience. Must match `Invite.Audience` when configured. |
| `exp` | Expiry time. Expired tokens are rejected. |
| `sub` | Subject for the invited user. AuthProxy includes it in the exchange call. |

## Error handling

AuthProxy serves dedicated pages for each invitation error:

| Page file | Condition | HTTP status |
|-----------|-----------|-------------|
| `invitation-expired.html` | The token signature is valid, but the `exp` claim is in the past. | 401 |
| `invitation-invalid.html` | The token is malformed or has an invalid signature. | 401 |
| `invitation-select-provider.html` | The token is valid and multiple identity providers are configured. | 200 |
| `invitation-subject-already-exists.html` | The authenticated subject is already associated with an existing account. | 409 |

See [Error pages](../error-pages.md) for customization details and
[Custom Invitation Provider-Selection Page](../invitation-provider-selection.md) for a full branded
provider-selection walkthrough.
Loading
Loading