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
75 changes: 15 additions & 60 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,66 +57,21 @@ The LFX v2 Auth Service operates as a NATS-based microservice that responds to r

### Available Operations

The service provides the following groups of operations:

#### Email Lookup Operations
Look up users by their email addresses to retrieve usernames or subject identifiers.

**Subjects:**
- `lfx.auth-service.email_to_username` - Look up username by email
- `lfx.auth-service.email_to_sub` - Look up subject identifier by email

**[View Email Lookup Documentation](docs/subjects/email_lookups.md)**

---

#### User Metadata Operations
Retrieve and update user profile metadata using various input types (JWT tokens, subject identifiers, or usernames).

**Subjects:**
- `lfx.auth-service.user_metadata.read` - Retrieve user metadata
- `lfx.auth-service.user_metadata.update` - Update user profile

**[View User Metadata Documentation](docs/subjects/user_metadata.md)**

---

#### User Emails Operations
Retrieve user email addresses (primary and alternate emails) using various input types (JWT tokens, subject identifiers, or usernames).

**Subjects:**
- `lfx.auth-service.user_emails.read` - Retrieve user email addresses

**[View User Emails Documentation](docs/subjects/user_emails.md)** - **Note:** Currently only supported for Authelia

---

#### Email Verification Flow
Two-step verification flow for verifying ownership of alternate email addresses.

**Subjects:**
- `lfx.auth-service.email_linking.send_verification` - Send OTP to email
- `lfx.auth-service.email_linking.verify` - Verify email with OTP

**[View Email Verification Documentation](docs/subjects/email_verification.md)** - Includes complete flow diagram

---

#### Identity Linking
Link verified identities (such as verified email addresses) to user accounts.

**Subjects:**
- `lfx.auth-service.user_identity.link` - Link verified identity to user

**[View Identity Linking Documentation](docs/subjects/identity_linking.md)**

---

#### Indexer Contract

Documents what data this service sends to the indexer service (currently none).

**[View Indexer Contract](docs/indexer-contract.md)**
The service exposes NATS request/reply operations grouped by area. Each link
has the full reference (subjects, payloads, examples):

- **[Email Lookups](docs/subjects/email_lookups.md)** — look up a user by email
- **[Username Lookups](docs/subjects/username_lookups.md)** — look up a subject identifier by username
- **[User Metadata](docs/subjects/user_metadata.md)** — read and update user profile metadata
- **[User Emails](docs/subjects/user_emails.md)** — read emails and set the primary email
- **[Email Verification](docs/subjects/email_verification.md)** — passwordless OTP verification of alternate emails
- **[Identity Linking](docs/subjects/identity_linking.md)** — link, unlink, and list identities
- **[Password Management](docs/subjects/password_management.md)** — change password and send reset links
- **[Impersonation](docs/subjects/impersonation.md)** — exchange a token to act as another user
- **[Aliases](docs/subjects/alias.md)** — claim a system-managed alias email
- **[Indexer Contract](docs/indexer-contract.md)** — data sent to the indexer service (currently none)
Comment thread
fayazg marked this conversation as resolved.

For end-to-end authentication flows, see **[Auth Flows](docs/auth-flows/README.md)**.

---

Expand Down
2 changes: 1 addition & 1 deletion docs/auth-flows/A-auth-service-m2m-profile-lookup.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ sequenceDiagram
alt Token not in cache or expired
AuthSvc->>Auth0: A1: POST /oauth2/token<br/>[client_credentials grant]<br/>w/ Auth Service M2M client credentials<br/>aud=auth0_mgmt

Auth0-->>AuthSvc: A2: access_token_m2m_read<br/>(read:users)<br/>*NOT update:users*
Auth0-->>AuthSvc: A2: access_token_m2m_read<br/>(read:users used in Flow A)
end

AuthSvc->>Auth0: A3: Check emails,<br/>read profiles using<br/>access_token_m2m_read
Expand Down
4 changes: 2 additions & 2 deletions docs/auth-flows/D-social-identity-linking.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,11 @@ sequenceDiagram
else id_token_social sub is a social identity (e.g. google-oauth2|, github|)
Note over SSR: SSR uses<br/>access_token_mgmt_self from Flow C<br/>(Management API token)

SSR->>NATS: D5: Publish link request<br/>with access_token_mgmt_self + id_token_social
SSR->>NATS: D5: Publish to lfx.auth-service.user_identity.link<br/>{ user.auth_token: access_token_mgmt_self,<br/>link_with.identity_token: id_token_social }
Note over NATS,AuthSvc: Auth Service subscribed to NATS subject
NATS->>AuthSvc: Deliver request

AuthSvc->>Auth0Mgmt: Link social identity<br/>using access_token_mgmt_self<br/>w/ id_token_social claims
AuthSvc->>Auth0Mgmt: POST /api/v2/users/{id}/identities<br/>with access_token_mgmt_self<br/>(update:current_user_identities)<br/>+ id_token_social

Auth0Mgmt-->>AuthSvc: Identity linked successfully
AuthSvc->>NATS: Publish response
Expand Down
4 changes: 2 additions & 2 deletions docs/auth-flows/E-passwordless-email-linking.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ sequenceDiagram

Browser->>SSR: User clicks "Add Email"<br/>and enters new email address

SSR->>NATS: E1: Publish passwordless<br/>start request with email
SSR->>NATS: E1: Publish to lfx.auth-service.email_linking.send_verification<br/>with email
Note over NATS,AuthSvc: Auth Service subscribed to NATS subject
NATS->>AuthSvc: Deliver request

Expand All @@ -38,7 +38,7 @@ sequenceDiagram

Note over SSR: SSR already has<br/>access_token_mgmt_self from Flow C<br/>(Management API token)

SSR->>NATS: E3: Publish verification request<br/>with code + access_token_mgmt_self
SSR->>NATS: E3: Publish to lfx.auth-service.email_linking.verify<br/>with code + access_token_mgmt_self
NATS->>AuthSvc: Deliver request

AuthSvc->>Auth0: E4: POST /oauth2/token<br/>[passwordless grant]<br/>w/ "LFX One Profile" client credentials<br/>username=new_email@example.com<br/>otp=verification_code<br/>[NO audience]
Expand Down
10 changes: 6 additions & 4 deletions docs/auth-flows/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,22 +29,24 @@ The main server-side rendering client used for user authentication. This is a re
A **regular web application** client (`app_type: regular_web`) used for Auth0 Management API access and passwordless flows. This client uses the authorization code flow for obtaining Management API access tokens that allow users to update their own profiles and link identities. It also supports the passwordless OTP grant type (`http://auth0.com/oauth/grant-type/passwordless/otp`) for email verification flows. This client implements a dual authentication pattern where users first authenticate with the main LFX One client, then use this client to obtain additional access tokens for specific audiences (Management API) or perform passwordless verification. Used in Flows C, D, and E.

### LFX V2 Auth Service M2M Client
A **machine-to-machine (M2M)** client named "LFX V2 Auth Service" that uses the client credentials grant type. This client has restricted permissions with only `read:users` scope (but **not** `update:users`) for the Auth0 Management API. It is used exclusively by the Auth Service to perform read-only operations such as profile lookups and checking email-to-username mappings. Used exclusively in Flow A.
A **machine-to-machine (M2M)** client named "LFX V2 Auth Service" that uses the client credentials grant type. This client is granted the `create:users`, `read:users`, `update:users`, and `delete:users` scopes for the Auth0 Management API. In Flow A it performs read-only operations such as profile lookups and checking email-to-username mappings; the broader scopes support additional Auth Service management use cases.

## Token Overview

| Token | Audience | Scope | Used For |
|-------|----------|-------|----------|
| `access_token_m2m_read` | `auth0_mgmt` | `read:users` | Auth Service reading user profiles (Flow A) |
| `access_token_m2m_read` | `auth0_mgmt` | `create:users`, `read:users`, `update:users`, `delete:users` (Flow A uses `read:users`) | Auth Service reading user profiles (Flow A) |
| `access_token_lfxv2` | `lfxv2` | LFX v2 API | Calling LFX v2 API endpoints (Flow B) |
| `access_token_mgmt_self` | `auth0_mgmt` | `update:users`* | User updating their own profile (Flow C, D, E) |
| `access_token_mgmt_self` | `auth0_mgmt` | `update:current_user_metadata` (Flow C) / `update:current_user_identities` (Flow D, E) | User self-service: profile update (C); identity linking (D, E) |
| `access_token_social` | (default) | N/A | Ignored - returned from social auth (Flow D) |
| `access_token_pwdless` | (default) | N/A | Ignored - returned from passwordless (Flow E) |
| `id_token_user` | N/A | N/A | User identity from main login (Flow B) |
| `id_token_mgmt` | N/A | N/A | Ignored - returned from mgmt flow (Flow C) |
| `id_token_social` | N/A | N/A | Social provider identity (Flow D) |
| `id_token_pwdless` | N/A | N/A | Passwordless email identity (Flow E) |

> **Note:** `auth0_mgmt` and `lfxv2` are conceptual audience labels used throughout these docs. The actual JWT `aud` claim is the Auth0 Management API URL (`https://{domain}/api/v2/`) for `auth0_mgmt`, and the configured LFX v2 API audience for `lfxv2`.

## Key Architecture Patterns

### NATS Pub/Sub
Expand All @@ -68,7 +70,7 @@ All Auth0 Management API calls are abstracted through the Auth Service, which co

## Security Considerations

1. **Principle of Least Privilege**: Auth Service M2M client only has `read:users`, not `update:users`
1. **M2M Client Scopes**: The Auth Service M2M client is granted full Management API user scopes (`create:users`, `read:users`, `update:users`, `delete:users`); Flow A performs only read operations
2. **Subject Validation**: Flow C validates token subjects match before allowing profile updates
3. **Email Verification**: Flow E validates the email in `id_token_pwdless` matches the requested email before linking
4. **Token Scoping**: Each access token is scoped to specific audiences and permissions
Expand Down
2 changes: 1 addition & 1 deletion docs/alias.md → docs/subjects/alias.md
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ These scopes are provisioned via the companion `auth0-terraform` repository —
- The domain is caller-supplied and validated against the server-side allow-list — there is no implicit default.
- Each user may hold at most one alias **per allowed domain**. With multiple domains in the allow-list, a user could in principle claim one alias on each (e.g. `jane@linux.com` and `jane@dev.lfx.example`).
- A successful claim is immutable from the user's perspective: only an administrator with direct Auth0 access can flip `app_metadata.system_managed` and remove the alias.
- For detailed Auth0-specific behavior and limitations, see: [`../internal/infrastructure/auth0/README.md`](../internal/infrastructure/auth0/README.md)
- For detailed Auth0-specific behavior and limitations, see: [`../../internal/infrastructure/auth0/README.md`](../../internal/infrastructure/auth0/README.md)

---

Expand Down
10 changes: 8 additions & 2 deletions docs/subjects/email_verification.md
Original file line number Diff line number Diff line change
Expand Up @@ -129,10 +129,12 @@ The service sends a one-time password (OTP) to the provided email address and re
```json
{
"success": false,
"error": "alternate email already linked"
"error": "email already linked"
}
```

> **Note:** The `error` text is a human-readable diagnostic, **not** a stable contract — the exact wording can vary by provider (for example, the mock provider returns `alternate email already linked` for this same condition). Consumers should branch on the `success: false` flag, not on the exact error string.

**Error Reply (Invalid Email):**
```json
{
Expand Down Expand Up @@ -207,10 +209,12 @@ The returned token is an authentication token that can be used to link the verif
```json
{
"success": false,
"error": "alternate email already linked"
"error": "email already linked"
}
```

> **Note:** The `error` text is a human-readable diagnostic, **not** a stable contract — the exact wording can vary by provider (for example, the mock provider returns `alternate email already linked` for this same condition). Consumers should branch on the `success: false` flag, not on the exact error string.

**Error Reply (Invalid Request):**
```json
{
Expand Down Expand Up @@ -263,3 +267,5 @@ The Mock flow is fully self-contained — no NATS KV or SMTP is involved:
2. **Verify OTP** (`email_linking.verify`): Compares the submitted code against the in-memory entry. On success, generates an **internal ID token** with `sub: "email|<email-address>"` — identical sub format to the Authelia flow.
3. **Link identity** (`user_identity.link`): Same `email|` dispatch — the verified email is appended to the user's `alternate_emails` in the in-memory store.

> The Mock provider returns `alternate email already linked` (rather than `email already linked`) for an already-linked address — see the note under the error replies above.

File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ nats request lfx.auth-service.password.update \
- Only supported for Auth0 username-password connection accounts (`auth0|` prefix); social login accounts (Google, GitHub, etc.) cannot use this endpoint
- The current password is validated via Auth0's Resource Owner Password Grant before the update is applied
- Password requirements (minimum length, complexity) are enforced by Auth0 and may cause the update to fail if not met
- For detailed Auth0-specific behavior, see: [`../internal/infrastructure/auth0/README.md`](../internal/infrastructure/auth0/README.md)
- For detailed Auth0-specific behavior, see: [`../../internal/infrastructure/auth0/README.md`](../../internal/infrastructure/auth0/README.md)

### Provider Support

Expand Down Expand Up @@ -167,7 +167,7 @@ nats request lfx.auth-service.password.reset_link \
- Email delivery is handled by Auth0 via the `/dbconnections/change_password` endpoint
- The reset link expires according to Auth0 tenant configuration (typically 24 hours)
- This operation succeeds even if the user's account uses a social login provider; Auth0 will handle the email gracefully
- For detailed Auth0-specific behavior, see: [`../internal/infrastructure/auth0/README.md`](../internal/infrastructure/auth0/README.md)
- For detailed Auth0-specific behavior, see: [`../../internal/infrastructure/auth0/README.md`](../../internal/infrastructure/auth0/README.md)

### Provider Support

Expand Down
10 changes: 9 additions & 1 deletion docs/subjects/user_emails.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,11 @@ To retrieve user email addresses (both primary and alternate emails), send a NAT

### Request Fields

- `user.auth_token` (string, required): A valid JWT token identifying the authenticated user
- `user.auth_token` (string, required): Identifies the user to read emails for. Despite the name, this field accepts either:
- a **JWT token** (Auth0) or **Authelia token**, which is validated before the subject identifier is extracted from the verified claims; or
- a **subject identifier** (canonical user ID) — an Auth0 `sub` containing `|` (e.g. `auth0|123456789`) or an Authelia UUID — used directly (no token verification). For Auth0, the service uses its M2M Management API token to read user details once the subject is known.
Comment thread
fayazg marked this conversation as resolved.

> **⚠️ Authorization:** The subject-identifier form performs **no token verification** — any caller able to publish to this subject can read any user's emails by supplying their `sub`/UUID. This operation is therefore intended for **trusted internal services only**; the NATS message bus is not exposed to end users, and the calling service is responsible for authorizing the requesting principal before invoking it. For end-user-initiated requests, pass the JWT/Authelia **token** form so the service verifies the caller from the signed claims.

### Reply

Expand Down Expand Up @@ -92,7 +96,11 @@ The `alternate_emails` array contains every email identity linked to the user fr
### Example using NATS CLI

```bash
# Using a JWT token
nats request lfx.auth-service.user_emails.read '{"user":{"auth_token":"eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9..."}}'

# Using a subject identifier (Auth0 sub with "|", or an Authelia UUID)
nats request lfx.auth-service.user_emails.read '{"user":{"auth_token":"auth0|123456789"}}'
```

### Example Response Processing
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,4 +50,4 @@ nats request lfx.auth-service.username_to_sub zephyr.stormwind
- Leading/trailing whitespace in the request payload is trimmed automatically
- The service works with Auth0, Authelia, and mock repositories based on configuration
- The returned subject identifier is the canonical user identifier used throughout the system
- For Authelia-specific SUB identifier details and how they are populated, see: [`../internal/infrastructure/authelia/README.md`](../internal/infrastructure/authelia/README.md)
- For Authelia-specific SUB identifier details and how they are populated, see: [`../../internal/infrastructure/authelia/README.md`](../../internal/infrastructure/authelia/README.md)
Loading