From e7bd47d75a5635ccb9b7200c4f07310cc4a94cd3 Mon Sep 17 00:00:00 2001 From: Fayaz G <5818912+fayazg@users.noreply.github.com> Date: Thu, 4 Jun 2026 17:39:51 -0600 Subject: [PATCH 1/9] docs(auth-flows): correct M2M and self-update token scopes Address review feedback from emsearcy on PR #20: - access_token_mgmt_self uses update:current_user_metadata (self-service), not update:users; removed the orphan asterisk https://github.com/linuxfoundation/lfx-v2-auth-service/pull/20#discussion_r3357350844 - Auth Service M2M client now holds create/read/update/delete:users; updated the client description, security consideration, and Flow A diagram https://github.com/linuxfoundation/lfx-v2-auth-service/pull/20#discussion_r3357386945 Issue: LFXV2-888 Co-Authored-By: Claude Opus 4.8 Signed-off-by: Fayaz G <5818912+fayazg@users.noreply.github.com> --- docs/auth-flows/A-auth-service-m2m-profile-lookup.md | 2 +- docs/auth-flows/README.md | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/auth-flows/A-auth-service-m2m-profile-lookup.md b/docs/auth-flows/A-auth-service-m2m-profile-lookup.md index c8fdc73..6f11532 100644 --- a/docs/auth-flows/A-auth-service-m2m-profile-lookup.md +++ b/docs/auth-flows/A-auth-service-m2m-profile-lookup.md @@ -23,7 +23,7 @@ sequenceDiagram alt Token not in cache or expired AuthSvc->>Auth0: A1: POST /oauth2/token
[client_credentials grant]
w/ Auth Service M2M client credentials
aud=auth0_mgmt - Auth0-->>AuthSvc: A2: access_token_m2m_read
(read:users)
*NOT update:users* + Auth0-->>AuthSvc: A2: access_token_m2m_read
(read:users used in Flow A) end AuthSvc->>Auth0: A3: Check emails,
read profiles using
access_token_m2m_read diff --git a/docs/auth-flows/README.md b/docs/auth-flows/README.md index da1b80d..65817db 100644 --- a/docs/auth-flows/README.md +++ b/docs/auth-flows/README.md @@ -29,7 +29,7 @@ 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 @@ -37,7 +37,7 @@ A **machine-to-machine (M2M)** client named "LFX V2 Auth Service" that uses the |-------|----------|-------|----------| | `access_token_m2m_read` | `auth0_mgmt` | `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` | User updating their own profile (Flow C, 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) | @@ -68,7 +68,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 From 0805d894856d9761618a554b7ac8245d47bf577d Mon Sep 17 00:00:00 2001 From: Fayaz G <5818912+fayazg@users.noreply.github.com> Date: Fri, 5 Jun 2026 10:45:06 -0600 Subject: [PATCH 2/9] docs(subjects): move subject docs under docs/subjects and fix links Relocate username_lookups, alias, password_management, and impersonation docs into docs/subjects/ alongside the other NATS subject docs. This fixes the previously broken sibling links from identity_linking.md and user_emails.md, and corrects relative paths to the internal/ READMEs. Issue: LFXV2-888 Co-Authored-By: Claude Opus 4.8 Signed-off-by: Fayaz G <5818912+fayazg@users.noreply.github.com> --- docs/{ => subjects}/alias.md | 2 +- docs/{ => subjects}/impersonation.md | 0 docs/{ => subjects}/password_management.md | 4 ++-- docs/subjects/user_emails.md | 2 +- docs/{ => subjects}/username_lookups.md | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) rename docs/{ => subjects}/alias.md (98%) rename docs/{ => subjects}/impersonation.md (100%) rename docs/{ => subjects}/password_management.md (94%) rename docs/{ => subjects}/username_lookups.md (91%) diff --git a/docs/alias.md b/docs/subjects/alias.md similarity index 98% rename from docs/alias.md rename to docs/subjects/alias.md index 7bb7e58..71c3e83 100644 --- a/docs/alias.md +++ b/docs/subjects/alias.md @@ -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) --- diff --git a/docs/impersonation.md b/docs/subjects/impersonation.md similarity index 100% rename from docs/impersonation.md rename to docs/subjects/impersonation.md diff --git a/docs/password_management.md b/docs/subjects/password_management.md similarity index 94% rename from docs/password_management.md rename to docs/subjects/password_management.md index 6bfdb22..818a76b 100644 --- a/docs/password_management.md +++ b/docs/subjects/password_management.md @@ -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 @@ -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 diff --git a/docs/subjects/user_emails.md b/docs/subjects/user_emails.md index b5bfd46..6ddb298 100644 --- a/docs/subjects/user_emails.md +++ b/docs/subjects/user_emails.md @@ -210,7 +210,7 @@ nats request lfx.auth-service.user_emails.set_primary \ - The target email must already exist as a verified alternate email on the user's account; it cannot be an arbitrary new address - The email input is normalized to lowercase before processing; matching is case-insensitive - After a successful update, the previous primary email becomes an alternate email on the account -- 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 diff --git a/docs/username_lookups.md b/docs/subjects/username_lookups.md similarity index 91% rename from docs/username_lookups.md rename to docs/subjects/username_lookups.md index 826a159..c4478a5 100644 --- a/docs/username_lookups.md +++ b/docs/subjects/username_lookups.md @@ -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) From bd7493133ae5fc898b7da2989ea5973f7a1216b6 Mon Sep 17 00:00:00 2001 From: Fayaz G <5818912+fayazg@users.noreply.github.com> Date: Fri, 5 Jun 2026 11:06:40 -0600 Subject: [PATCH 3/9] docs(subjects): document subject identifier input for user_emails.read Clarify that the user_emails.read auth_token field accepts either a verified JWT/Authelia token or a subject identifier (Auth0 sub containing "|", or an Authelia UUID) used directly via the service M2M token. Issue: LFXV2-888 Co-Authored-By: Claude Opus 4.8 Signed-off-by: Fayaz G <5818912+fayazg@users.noreply.github.com> --- docs/subjects/user_emails.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/docs/subjects/user_emails.md b/docs/subjects/user_emails.md index 6ddb298..713cdba 100644 --- a/docs/subjects/user_emails.md +++ b/docs/subjects/user_emails.md @@ -23,7 +23,9 @@ 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 (signature and required scopes) 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 via the service's M2M token (no token verification). ### Reply @@ -92,7 +94,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 From 4e6772c91d37846ddde427e6799552f3aa37c15b Mon Sep 17 00:00:00 2001 From: Fayaz G <5818912+fayazg@users.noreply.github.com> Date: Fri, 5 Jun 2026 11:33:14 -0600 Subject: [PATCH 4/9] docs(readme): correct user_emails.read supported inputs and provider note Remove the inaccurate "usernames" input type and the stale "only supported for Authelia" note; user_emails.read accepts a JWT token or a subject identifier and is exercised on the Auth0 provider. Issue: LFXV2-888 Co-Authored-By: Claude Opus 4.8 Signed-off-by: Fayaz G <5818912+fayazg@users.noreply.github.com> --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 4d71883..4a9f99d 100644 --- a/README.md +++ b/README.md @@ -82,12 +82,12 @@ Retrieve and update user profile metadata using various input types (JWT tokens, --- #### User Emails Operations -Retrieve user email addresses (primary and alternate emails) using various input types (JWT tokens, subject identifiers, or usernames). +Retrieve user email addresses (primary and alternate emails) using a JWT token or a subject identifier. **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 +**[View User Emails Documentation](docs/subjects/user_emails.md)** --- From 3806d715436e8f30db66dc9798637d2cf8799898 Mon Sep 17 00:00:00 2001 From: Fayaz G <5818912+fayazg@users.noreply.github.com> Date: Fri, 5 Jun 2026 12:01:31 -0600 Subject: [PATCH 5/9] docs: fix inaccuracies from docs-vs-code audit and de-duplicate README - email_verification: correct error string to "email already linked" - auth-flows: distinguish update:current_user_metadata (C) from update:current_user_identities (D, E); clarify Flow D link payload/endpoint and Flow E subjects; note conceptual audience labels - README: replace duplicated per-subject lists with a link directory so docs/ remain the single source of truth Issue: LFXV2-888 Co-Authored-By: Claude Opus 4.8 Signed-off-by: Fayaz G <5818912+fayazg@users.noreply.github.com> --- README.md | 75 ++++--------------- docs/auth-flows/D-social-identity-linking.md | 4 +- .../E-passwordless-email-linking.md | 4 +- docs/auth-flows/README.md | 4 +- docs/subjects/email_verification.md | 4 +- 5 files changed, 24 insertions(+), 67 deletions(-) diff --git a/README.md b/README.md index 4a9f99d..d2761a9 100644 --- a/README.md +++ b/README.md @@ -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 a JWT token or a subject identifier. - -**Subjects:** -- `lfx.auth-service.user_emails.read` - Retrieve user email addresses - -**[View User Emails Documentation](docs/subjects/user_emails.md)** - ---- - -#### 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) + +For end-to-end authentication flows, see **[Auth Flows](docs/auth-flows/README.md)**. --- diff --git a/docs/auth-flows/D-social-identity-linking.md b/docs/auth-flows/D-social-identity-linking.md index 0edd3b0..4d68ff7 100644 --- a/docs/auth-flows/D-social-identity-linking.md +++ b/docs/auth-flows/D-social-identity-linking.md @@ -35,11 +35,11 @@ sequenceDiagram else id_token_social sub is a social identity (e.g. google-oauth2|, github|) Note over SSR: SSR uses
access_token_mgmt_self from Flow C
(Management API token) - SSR->>NATS: D5: Publish link request
with access_token_mgmt_self + id_token_social + SSR->>NATS: D5: Publish to lfx.auth-service.user_identity.link
{ user.auth_token: access_token_mgmt_self,
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
using access_token_mgmt_self
w/ id_token_social claims + AuthSvc->>Auth0Mgmt: POST /api/v2/users/{id}/identities
with access_token_mgmt_self
(update:current_user_identities)
+ id_token_social Auth0Mgmt-->>AuthSvc: Identity linked successfully AuthSvc->>NATS: Publish response diff --git a/docs/auth-flows/E-passwordless-email-linking.md b/docs/auth-flows/E-passwordless-email-linking.md index 11b360b..6fd5990 100644 --- a/docs/auth-flows/E-passwordless-email-linking.md +++ b/docs/auth-flows/E-passwordless-email-linking.md @@ -19,7 +19,7 @@ sequenceDiagram Browser->>SSR: User clicks "Add Email"
and enters new email address - SSR->>NATS: E1: Publish passwordless
start request with email + SSR->>NATS: E1: Publish to lfx.auth-service.email_linking.send_verification
with email Note over NATS,AuthSvc: Auth Service subscribed to NATS subject NATS->>AuthSvc: Deliver request @@ -38,7 +38,7 @@ sequenceDiagram Note over SSR: SSR already has
access_token_mgmt_self from Flow C
(Management API token) - SSR->>NATS: E3: Publish verification request
with code + access_token_mgmt_self + SSR->>NATS: E3: Publish to lfx.auth-service.email_linking.verify
with code + access_token_mgmt_self NATS->>AuthSvc: Deliver request AuthSvc->>Auth0: E4: POST /oauth2/token
[passwordless grant]
w/ "LFX One Profile" client credentials
username=new_email@example.com
otp=verification_code
[NO audience] diff --git a/docs/auth-flows/README.md b/docs/auth-flows/README.md index 65817db..81b5794 100644 --- a/docs/auth-flows/README.md +++ b/docs/auth-flows/README.md @@ -37,7 +37,7 @@ A **machine-to-machine (M2M)** client named "LFX V2 Auth Service" that uses the |-------|----------|-------|----------| | `access_token_m2m_read` | `auth0_mgmt` | `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:current_user_metadata` | 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) | @@ -45,6 +45,8 @@ A **machine-to-machine (M2M)** client named "LFX V2 Auth Service" that uses the | `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 diff --git a/docs/subjects/email_verification.md b/docs/subjects/email_verification.md index 4a6a33f..2c0a612 100644 --- a/docs/subjects/email_verification.md +++ b/docs/subjects/email_verification.md @@ -129,7 +129,7 @@ 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" } ``` @@ -207,7 +207,7 @@ 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" } ``` From d64d474b792db9be74823382ae9a3eddc26e2aaf Mon Sep 17 00:00:00 2001 From: "Fayaz G." <5818912+fayazg@users.noreply.github.com> Date: Fri, 5 Jun 2026 12:09:46 -0600 Subject: [PATCH 6/9] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> Signed-off-by: Fayaz G. <5818912+fayazg@users.noreply.github.com> --- docs/subjects/user_emails.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/subjects/user_emails.md b/docs/subjects/user_emails.md index 713cdba..0c6f3a3 100644 --- a/docs/subjects/user_emails.md +++ b/docs/subjects/user_emails.md @@ -24,8 +24,8 @@ To retrieve user email addresses (both primary and alternate emails), send a NAT ### Request Fields - `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 (signature and required scopes) 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 via the service's M2M token (no token verification). + - 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. ### Reply From 408b5a3b063a58c2e74f429961a1bb64bb9ec242 Mon Sep 17 00:00:00 2001 From: Fayaz G <5818912+fayazg@users.noreply.github.com> Date: Tue, 16 Jun 2026 13:55:42 -0600 Subject: [PATCH 7/9] fix(review): address PR #54 review feedback Address review comments from copilot-pull-request-reviewer: - docs/subjects/user_emails.md: document authorization/trust constraints for the subject-identifier (no-token) mode of user.auth_token - README.md: confirmed subject doc links now resolve under docs/subjects/ (files were moved; no change needed) Resolves 2 review threads. Co-Authored-By: Claude Opus 4.8 Signed-off-by: Fayaz G <5818912+fayazg@users.noreply.github.com> --- docs/subjects/user_emails.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/subjects/user_emails.md b/docs/subjects/user_emails.md index 0c6f3a3..b03be20 100644 --- a/docs/subjects/user_emails.md +++ b/docs/subjects/user_emails.md @@ -27,6 +27,8 @@ To retrieve user email addresses (both primary and alternate emails), send a NAT - 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. +> **⚠️ 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 The service returns a structured reply with user email information: From bb01a03ec8b3db22a24c4e00151af78c495c8fea Mon Sep 17 00:00:00 2001 From: Fayaz G <5818912+fayazg@users.noreply.github.com> Date: Tue, 16 Jun 2026 15:49:01 -0600 Subject: [PATCH 8/9] fix(review): address PR #54 review feedback Address review comments from copilot-pull-request-reviewer: - docs/subjects/email_verification.md: the conflict error was documented as the fixed string "email already linked", but it is provider-specific (the mock provider returns "alternate email already linked"). Add a note under both "Email Already Linked" error replies that the error text is a human-readable diagnostic, not a stable contract, and consumers should branch on the success flag; note the mock variant in the Mock provider section. Resolves 2 review threads. Signed-off-by: Fayaz G <5818912+fayazg@users.noreply.github.com> --- docs/subjects/email_verification.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/subjects/email_verification.md b/docs/subjects/email_verification.md index 2c0a612..ff0c1a4 100644 --- a/docs/subjects/email_verification.md +++ b/docs/subjects/email_verification.md @@ -133,6 +133,8 @@ The service sends a one-time password (OTP) to the provided email address and re } ``` +> **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 { @@ -211,6 +213,8 @@ The returned token is an authentication token that can be used to link the verif } ``` +> **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 { @@ -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|"` — 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. + From bd1f78450cfb599eae85d6aab73a969aed3f0c95 Mon Sep 17 00:00:00 2001 From: Fayaz G <5818912+fayazg@users.noreply.github.com> Date: Tue, 16 Jun 2026 15:59:35 -0600 Subject: [PATCH 9/9] fix(review): address PR #54 review feedback Address review comment from copilot-pull-request-reviewer: - docs/auth-flows/README.md: the Token Overview listed access_token_m2m_read with only read:users, contradicting the client description and Security Considerations (which grant create/read/update/delete:users). The M2M token is requested without a scope param, so it carries all granted scopes; update the Scope cell to list them and note Flow A uses read:users, matching the Flow A diagram. Resolves 1 review thread. Signed-off-by: Fayaz G <5818912+fayazg@users.noreply.github.com> --- docs/auth-flows/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/auth-flows/README.md b/docs/auth-flows/README.md index 81b5794..0d73205 100644 --- a/docs/auth-flows/README.md +++ b/docs/auth-flows/README.md @@ -35,7 +35,7 @@ A **machine-to-machine (M2M)** client named "LFX V2 Auth Service" that uses the | 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: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) |