diff --git a/rfcs/088-wcstp_identity_requesting/README.md b/rfcs/088-wcstp_identity_requesting/README.md new file mode 100644 index 00000000..033d1767 --- /dev/null +++ b/rfcs/088-wcstp_identity_requesting/README.md @@ -0,0 +1,177 @@ +## Phase 1: Decouple credentials from Sierra (Auth0 native DB) + +### Goal + +Move credential storage (email + password) out of Sierra and into Auth0's native database. After this phase, Sierra is no longer involved in authentication. This phase does not require Folio to be ready. + +### Background: How it works today + +Auth0 is configured with a Custom DB connection (`Sierra-Connection`) with `import_mode = false`. This means: +- Auth0 delegates **all** credential operations to Sierra via 7 Custom DB scripts: `login`, `create`, `get_user`, `change_password`, `change_email`, `verify`, `delete` +- Sierra is the canonical store for credentials +- Auth0 never persists user password hashes itself + +The connection is used by multiple clients: Identity web app, OpenAthens SAML IDP, IIIF Image API, Identity Lambda API, and Smoke Test. + +#### Where user data lives today + +User data is split across Sierra and Auth0: + +**Sierra (canonical source for patron data):** +- First name, last name (stored in varFields) +- Email address +- Password (PIN) +- Barcode (set to Sierra record number) +- Patron type / role (determines permissions, e.g. whether they can place holds) +- Email verified status +- Hold data (items requested) + +**Auth0 (synced from Sierra on each login via DB scripts):** +- `email`, `given_name`, `family_name`, `name` — copied from Sierra by the `login`/`get_user` scripts +- `email_verified` — derived from Sierra's verified email field +- `user_id` — `auth0|p` +- `app_metadata` — system-controlled data the user cannot modify: + - `barcode` (Sierra record number as string) + - `role` (patron type from Sierra) + - `deleteRequested` (timestamp if deletion was requested) + - `terms_and_conditions_accepted` (boolean) +- `user_metadata` — currently empty (`{}`) — this is the bucket for user-editable data, but nothing is stored here today because Sierra is the source of truth for all profile fields + +The key distinction in Auth0: +- **`app_metadata`**: only modifiable via the Management API (server-side). Used for system data like role, barcode, flags. Users cannot change it. +- **`user_metadata`**: modifiable by the user (via the Management API with their token). Used for user preferences or self-reported data. Currently unused. + +Post-migration, `app_metadata` becomes more important as the place to store the Folio patron ID and any other system-level identifiers. + +#### Registration/create flow in detail + +Registration currently happens in two stages: + +1. **Auth0 Universal Login signup form** — user provides email and password only. Auth0 calls the `create` Custom DB script which: + - Creates a Sierra patron with placeholder names (`Auth0_Registration_tempFirstName` / `Auth0_Registration_tempLastName`) + - Assigns a barcode equal to the Sierra record number + - Handles duplicate detection (Sierra returns "user already exists") and Sierra-specific password validation errors (PIN too trivial, PIN too long) + +2. **Post-login redirect to full registration form** — the `redirect_to_full_registration` Auth0 Action fires after signup. It checks whether the user's `given_name`/`family_name` still have the `Auth0_Registration` prefix. If so, it: + - Encodes a session token containing the user's email + - Redirects the user to the identity webapp's `/registration` page + - The user fills in their real first name, last name, and accepts T&Cs + - The form submits to the Identity API's `PUT /users/:user_id/registration` endpoint, which updates the Sierra patron record with the real name (after verifying the record still has temp names) + - The webapp redirects back to Auth0's `/continue` endpoint to complete the login flow + - Auth0 issues tokens and the user is logged in + +It's not entirely clear why it was designed this way. Suspicion is that: +- we have to create the Sierra patron record at the point where we have their password, +- we can't/couldn't at the time pass `user_metadata` (with first/last name, T&C agreement) through the custom DB script, +- but the `create` script needs to complete for Auth0 to consider the sign up done, +- so we gather the additional data in a separate form + +Consequences of this design: +- Abandoned signups leave orphaned Sierra patrons with temp names (require periodic cleanup) +- Two Sierra API calls during signup (create + update barcode), plus a third later (update name) +- The `redirect_to_full_registration` action must fire on every login to check whether registration is complete +- Auth0 can't update `given_name`/`family_name` directly on a Custom DB connection user — names are only refreshed from Sierra on next login (hence the user is logged out after registration to force a refresh) + +### Mechanism: Auth0 Automatic Migration + +Auth0 has built-in support for gradual migration via the `import_mode` flag in `infra/scoped/auth0-connection.tf`. When set to `true`: + +- **On login (existing users):** Auth0 calls the `login` script to validate credentials against Sierra. If successful, Auth0 stores the user + password hash in its own native store. The next time the user logs in, Auth0 validates locally and never calls Sierra again. +- **On signup (new users):** Users are created directly in Auth0's native store. The `create` script is not called. +- **Password/email changes:** For already-migrated users, Auth0 handles natively. For not-yet-migrated users, the custom scripts still proxy to Sierra. + +Key insight: Auth0 doesn't copy Sierra's password hash. It receives the plaintext password from the user's login attempt, validates it via Sierra's API, and then stores its own bcrypt hash. **Hash algorithm compatibility is not required.** + +### Steps + +#### Step 1: Prepare the `login` script for migration tracking + +Before enabling import mode, update the `login` DB script (`packages/apps/auth0-database-scripts/src/login.ts`) to: +- Mark the Sierra patron record with a "migrated" flag (e.g. a varField) after successful credential validation +- This enables easy identification of stragglers later (query Sierra for patrons without the flag) + +Also consider adding any useful data to `app_metadata` in the `patronRecordToUser` return value (`packages/apps/auth0-database-scripts/src/helpers.ts`). This is already returning `barcode` and `role`. Could add `sierra_record_number` if useful for future Folio correlation. + +#### Step 2: Update the registration flow for new users + +With `import_mode = true`, new signups go directly into Auth0 — the `create` DB script no longer fires. Since patrons still need a Sierra account to place holds, the Sierra patron creation must move elsewhere. + +**Recommended approach:** Keep the existing redirect-to-registration flow (`redirect_to_full_registration` action + `/users/:user_id/registration` endpoint), but change the endpoint from "update existing Sierra patron with real name" to "create a new Sierra patron with full details". This avoids the temp-name pattern entirely. + +Alternatively, if Auth0 Universal Login custom signup fields are sufficient for UX needs, the entire registration can happen in a single form (email, password, first name, last name, T&Cs) with patron creation handled by a post-login Action or the Identity API. + +#### Step 3: Enable import mode + +Flip in Terraform (`infra/scoped/auth0-connection.tf`): +```terraform +import_mode = true +``` + +From this point: +- Every existing user who logs in is silently migrated (one-time, transparent to user) +- New users are created in Auth0 natively +- Sierra credentials become progressively stale (this is fine — they're no longer read) + +#### Step 4: Monitor migration progress + +Track: +- Auth0 dashboard: users in native store vs still requiring custom scripts +- Sierra: patrons with vs without the migration flag +- Timeline: how quickly active users are being migrated (most will migrate within weeks based on login frequency) + +#### Step 5: Handle stragglers + +For users who never log in during the migration window: +- Query Sierra for all patrons **without** the migration flag — this is the straggler list +- Options: + - **Forced password reset:** Bulk-import users into Auth0 (email + profile only, no password) and trigger password reset emails + - **Accept dormancy:** If they haven't logged in for months, they can reset when they eventually return +- Decision depends on timeline pressure from Sierra retirement + +#### Step 6: Remove Custom DB scripts + +Once all users are migrated (or stragglers are handled): +- Set `enabled_database_customization = false` in Terraform +- Remove `custom_scripts` block and Sierra `configuration` from the connection +- Delete `packages/apps/auth0-database-scripts` from the repo +- Remove the `redirect_to_full_registration` action (if registration flow has been simplified) +- Remove the `/users/:user_id/registration` endpoint from the Identity API (if no longer needed) + +### What stays working without changes + +| Component | Why it's unaffected | +|-----------|-------------------| +| Identity web app login/signup | Auth0 Universal Login handles it natively | +| OpenAthens SAML IDP | Authenticates against Auth0 — doesn't care about backing store | +| IIIF Image API | Same Auth0 login flow | +| API Authorizer Lambda | Validates Auth0 tokens — unrelated to credential store | +| `add_custom_claims` action | Reads from `app_metadata` as before | +| Identity API (get user, change password, change email) | Already calls Auth0 Management API (which today proxies to Sierra via DB scripts — but once users are migrated, Auth0 handles natively with no code change needed in the Identity API) | + +### What needs attention + +| Component | Change needed | +|-----------|--------------| +| Registration flow | New users don't get auto-created in Sierra — need new creation path | +| `login` script | Add migration flag to Sierra patron on successful validation | +| Identity API `/registration` endpoint | Changes from "update patron" to "create patron" | +| Password policy | Sierra's PIN rules no longer apply — Auth0 config is sole authority (already defined in TF: min 8 chars, no personal info, dictionary check) | +| `verify` script side-effect | Currently marks Sierra patron as verified — moves to Auth0 native email verification | +| Patron Deletion Tracker | Still works (watches Sierra deletions → removes from Auth0), but direction may reverse later | + +### Risks and mitigations + +| Risk | Mitigation | +|------|-----------| +| Migration flag update fails during login | Non-blocking: user is still migrated in Auth0, just not flagged in Sierra. Log/alert on failure. Straggler count may be slightly inflated. | +| Sierra outage during transition | Only affects not-yet-migrated users (they can't log in). Already-migrated users are unaffected. Same blast radius as today. | +| New user registration fails to create Sierra patron | Same risk as today with the `create` script, but now in your own code where you have better error handling and retry control. | +| Someone accidentally flips import_mode back to false | Add a comment in TF explaining the irreversibility. Once users are in the native store, flipping back would cause "user already exists" conflicts. | + +### Outcome + +After Phase 1: +- Auth0 is the canonical store for all credentials +- Sierra holds patron profile/hold data only (credentials are stale/irrelevant) +- All 7 Custom DB scripts can be deleted +- The system is ready for Phase 2 (move patron data from Sierra to Folio) without any credential coupling to worry about \ No newline at end of file diff --git a/rfcs/088-wcstp_identity_requesting/user_flow_post_customDB.md b/rfcs/088-wcstp_identity_requesting/user_flow_post_customDB.md new file mode 100644 index 00000000..778c32e8 --- /dev/null +++ b/rfcs/088-wcstp_identity_requesting/user_flow_post_customDB.md @@ -0,0 +1,245 @@ +# User Flows — After Custom DB Script Removal (Auth0 Native DB) + +These diagrams show the same user flows as `user_flows.md`, but with Auth0 as the native credential store. No Custom DB scripts are involved. Sierra is still used for patron profile/hold data during the transition period (until Folio replaces it). + +## Registration + +```mermaid +sequenceDiagram + participant User + participant Auth0UL as Auth0 Universal Login + participant Auth0 as Auth0 Tenant + participant Action as Action: create_patron + participant Sierra + participant ClaimsAction as Action: add_custom_claims + + User->>Auth0UL: Sign up (email, password, first name, last name, T&Cs ✓) + Auth0UL->>Auth0: Create user in native DB + Auth0->>Auth0: Store credentials (bcrypt hash) + Auth0->>Auth0: Store given_name, family_name in user profile + Auth0->>Auth0: Store T&Cs acceptance in user_metadata + + Note over Auth0,Action: Post-login trigger (first login after signup) + Auth0->>Action: onExecutePostLogin(event, api) + Action->>Action: Check app_metadata.sierra_patron_id missing? + Action->>Sierra: createPatron(firstName, lastName, email) + alt Email already exists in Sierra + Sierra-->>Action: UserAlreadyExists + Action->>Action: Look up existing patron by email + Sierra-->>Action: patronRecord + Action->>Auth0: Set app_metadata (sierra_patron_id, barcode, role) + else Success + Sierra-->>Action: recordNumber + Action->>Sierra: updatePatron(recordNumber, barcode=recordNumber) + Sierra-->>Action: OK + Action->>Auth0: Set app_metadata (sierra_patron_id, barcode, role) + end + + Auth0->>ClaimsAction: onExecutePostLogin(event, api) + ClaimsAction->>ClaimsAction: Add barcode + role claims to ID token + + Auth0-->>User: Tokens issued, logged in +``` + +**Key differences from today:** +- Single form: all data collected upfront (no redirect to separate registration page) +- No temp names — patron created with real data +- No `create` DB script — Auth0 stores credentials natively +- No orphaned patrons from abandoned signups (Sierra patron only created after Auth0 user exists) +- No forced logout to refresh name (Auth0 native DB allows direct profile updates) +- Sierra patron creation is idempotent: check `app_metadata.sierra_patron_id` before creating + +**Note:** The `create_patron` action replaces both the old `create` DB script and the `redirect_to_full_registration` action. If the 20s action timeout is a concern, patron creation could alternatively happen via the Identity API called from a post-login redirect — but the action approach is simpler. + +## Login + +```mermaid +sequenceDiagram + participant User + participant Auth0UL as Auth0 Universal Login + participant Auth0 as Auth0 Tenant + participant ClaimsAction as Action: add_custom_claims + + User->>Auth0UL: Sign in (email + password) + Auth0UL->>Auth0: Login request + Auth0->>Auth0: Validate credentials against native store (bcrypt) + + alt Invalid credentials + Auth0-->>User: "Wrong email or password" + else Valid credentials + Auth0->>ClaimsAction: onExecutePostLogin(event, api) + ClaimsAction->>ClaimsAction: Add barcode + role claims to ID token + Auth0-->>User: Tokens issued + end +``` + +**Key differences from today:** +- No `login` DB script — Auth0 validates against its own store +- No Sierra calls at all during login +- No implicit email verification logic for pre-2022 patrons (handled during migration) +- Significantly faster login (no external API calls) + +## Get User + +```mermaid +sequenceDiagram + participant Auth0 as Auth0 Tenant + + Note over Auth0: Triggered internally for password reset,
email verification, silent auth, etc. + + Auth0->>Auth0: Look up user in native store + Auth0->>Auth0: Return user profile +``` + +**Key differences from today:** +- No `get_user` DB script — Auth0 reads from its own store +- No Sierra dependency for user lookups +- No risk of "duplicate users" error (Auth0 enforces unique email natively) + +## Change Password + +```mermaid +sequenceDiagram + participant User + participant Webapp as Identity Webapp + participant API as Identity API + participant Auth0 as Auth0 Tenant + + User->>Webapp: Change password (old + new) + Webapp->>API: PUT /users/:id/password {password, newPassword} + API->>Auth0: POST /oauth/token (password grant, old password) + Auth0->>Auth0: Validate old password against native store + Auth0-->>API: Valid / Invalid + + alt Invalid old password + API-->>User: "Current password is incorrect" + else Valid + API->>Auth0: Management API PATCH /users/:id {password: newPassword} + Auth0->>Auth0: Validate against password policy + alt Password too weak + Auth0-->>API: PasswordStrengthError + API-->>User: "Please use a stronger password" + else Success + Auth0->>Auth0: Store new bcrypt hash + Auth0-->>API: 200 + API-->>User: Password updated + end + end +``` + +**Key differences from today:** +- No `change_password` DB script — Auth0 updates its own store +- No Sierra PIN update (Sierra credentials are stale/irrelevant) +- Password policy enforced by Auth0 config only (min 8 chars, no personal info, dictionary check) — no Sierra PIN rules (trivial, too long) +- No 30-character PIN truncation +- Identity API code is unchanged + +## Change Email + +```mermaid +sequenceDiagram + participant User + participant Webapp as Identity Webapp + participant API as Identity API + participant Auth0 as Auth0 Tenant + + User->>Webapp: Change email (current password + new email) + Webapp->>API: PUT /users/:id {password, email: newEmail} + API->>Auth0: POST /oauth/token (password grant, current password) + Auth0-->>API: Valid + + API->>Auth0: Management API PATCH /users/:id {email: newEmail, verify_email: true} + alt Email already exists + Auth0-->>API: 400 "email already exists" + API-->>User: "A user with this email already exists" + else Success + Auth0->>Auth0: Update email, mark unverified + Auth0->>User: Verification email sent + Auth0-->>API: 200 (updated user) + API-->>User: Email updated + end +``` + +**Key differences from today:** +- No `change_email` DB script — Auth0 updates its own store +- No Sierra email update (Sierra is no longer the source of truth for email) +- Duplicate email detection handled natively by Auth0 +- Identity API code is unchanged + +## Verify Email + +```mermaid +sequenceDiagram + participant User + participant Auth0 as Auth0 Tenant + + User->>Auth0: Click verification link in email + Auth0->>Auth0: Mark email_verified = true +``` + +**Key differences from today:** +- No `verify` DB script — Auth0 handles verification natively +- No Sierra call to mark patron email verified +- Single atomic operation + +## Delete + +```mermaid +sequenceDiagram + participant Tracker as Patron Deletion Tracker + participant Auth0 as Auth0 Tenant + participant Sierra + + Note over Tracker: Daily scheduled job (unchanged) + + Tracker->>Sierra: Query for patrons deleted yesterday + Sierra-->>Tracker: List of deleted patron IDs + loop For each deleted patron + Tracker->>Auth0: Management API DELETE /users/:id + Auth0->>Auth0: Remove user from native store + end +``` + +**Key differences from today:** +- No `delete` DB script (was already a no-op) +- Patron Deletion Tracker continues to run unchanged +- Direction remains Sierra → Auth0 during transition (reverses to Auth0/Folio → Sierra once Sierra is being retired) + +## Validate Password (via Identity API) + +```mermaid +sequenceDiagram + participant User + participant Webapp as Identity Webapp + participant API as Identity API + participant Auth0 as Auth0 Tenant + + Note over User,Webapp: Used as a "confirm identity" check
before sensitive operations + + User->>Webapp: Confirm current password + Webapp->>API: POST /users/:id/validate {password} + API->>Auth0: POST /oauth/token (password grant) + Auth0->>Auth0: Validate against native store + Auth0-->>API: 200 / 401 + API-->>User: 200 OK / Error +``` + +**Key differences from today:** +- No `login` DB script triggered — Auth0 validates natively +- No Sierra call +- Identity API code is unchanged + +--- + +## Summary of changes + +| Flow | Before (Custom DB) | After (Native DB) | Code changes needed | +|------|-------------------|-------------------|-------------------| +| Registration | `create` script → Sierra, then redirect to form | Single form, post-login action → Sierra | New action, remove `redirect_to_full_registration`, remove `/registration` endpoint | +| Login | `login` script → Sierra | Auth0 native validation | None | +| Get user | `get_user` script → Sierra | Auth0 native lookup | None | +| Change password | `change_password` script → Sierra | Auth0 native update | None (Identity API unchanged) | +| Change email | `change_email` script → Sierra | Auth0 native update | None (Identity API unchanged) | +| Verify email | `verify` script → Sierra | Auth0 native verification | None | +| Delete | `delete` script (no-op) | No script needed | None (Deletion Tracker unchanged) | +| Validate password | `login` script → Sierra | Auth0 native validation | None (Identity API unchanged) | \ No newline at end of file diff --git a/rfcs/088-wcstp_identity_requesting/user_flows.md b/rfcs/088-wcstp_identity_requesting/user_flows.md new file mode 100644 index 00000000..4cfcd94f --- /dev/null +++ b/rfcs/088-wcstp_identity_requesting/user_flows.md @@ -0,0 +1,220 @@ +# User Flows — Custom DB Script Usage + +## Registration (create) + +```mermaid +sequenceDiagram + participant User + participant Auth0UL as Auth0 Universal Login + participant Auth0 as Auth0 Tenant + participant CreateScript as DB Script: create + participant Sierra + participant Action as Action: redirect_to_full_registration + participant Webapp as Identity Webapp + participant API as Identity API + + User->>Auth0UL: Sign up (email + password) + Auth0UL->>Auth0: Create user request + Auth0->>CreateScript: create(user) + CreateScript->>Sierra: createPatron(tempLastName, tempFirstName, email, password) + alt Email already exists + Sierra-->>CreateScript: UserAlreadyExists + CreateScript-->>Auth0: ValidationError("user_exists") + Auth0-->>User: "A user with this email already exists" + else PIN too trivial / too long + Sierra-->>CreateScript: PIN validation error + CreateScript-->>Auth0: ValidationError (password message) + Auth0-->>User: "Please use a more complex/shorter password" + else Success + Sierra-->>CreateScript: recordNumber + CreateScript->>Sierra: updatePatron(recordNumber, barcode=recordNumber) + Sierra-->>CreateScript: OK + CreateScript-->>Auth0: User created + end + + Auth0->>Action: Post-login trigger + Action->>Action: Check given_name/family_name for temp prefix + Action->>User: Redirect to /registration with session_token + + User->>Webapp: Fill in first name, last name, accept T&Cs + Webapp->>API: PUT /users/:id/registration {firstName, lastName} + API->>Sierra: getPatronRecordByRecordNumber (verify temp names) + API->>Sierra: updatePatron (set real name) + Sierra-->>API: OK + API-->>Webapp: 204 + Webapp->>Auth0: Redirect to /continue + Auth0-->>User: Issue tokens, logged in +``` + +## Login (login) + +```mermaid +sequenceDiagram + participant User + participant Auth0UL as Auth0 Universal Login + participant Auth0 as Auth0 Tenant + participant LoginScript as DB Script: login + participant Sierra + + User->>Auth0UL: Sign in (email + password) + Auth0UL->>Auth0: Login request + Auth0->>LoginScript: login(email, password) + LoginScript->>Sierra: getPatronRecordByEmail(email) + alt Patron not found + Sierra-->>LoginScript: NotFound + LoginScript-->>Auth0: WrongUsernameOrPasswordError + Auth0-->>User: "We don't recognise the email and/or password" + else Patron found + Sierra-->>LoginScript: patronRecord + LoginScript->>Sierra: validateCredentials(barcode, password) + alt Invalid credentials + Sierra-->>LoginScript: Failure + LoginScript-->>Auth0: WrongUsernameOrPasswordError + Auth0-->>User: "We don't recognise the email and/or password" + else Valid credentials + Sierra-->>LoginScript: Success + LoginScript-->>Auth0: User profile (with app_metadata: barcode, role) + Auth0-->>User: Tokens issued + end + end +``` + +## Get User (get_user) + +```mermaid +sequenceDiagram + participant Auth0 as Auth0 Tenant + participant GetUserScript as DB Script: get_user + participant Sierra + + Note over Auth0: Triggered when Auth0 needs to
look up a user (e.g. password reset,
email verification, silent auth) + + Auth0->>GetUserScript: get_user(email) + GetUserScript->>Sierra: getPatronRecordByEmail(email) + alt Patron found + Sierra-->>GetUserScript: patronRecord + GetUserScript-->>Auth0: User profile {user_id, email, name, app_metadata} + else Not found + Sierra-->>GetUserScript: NotFound + GetUserScript-->>Auth0: undefined (user doesn't exist) + else Duplicate users + Sierra-->>GetUserScript: DuplicateUsers + GetUserScript-->>Auth0: ValidationError (contact Library Enquiries) + end +``` + +## Change Password (change_password) + +```mermaid +sequenceDiagram + participant User + participant Webapp as Identity Webapp + participant API as Identity API + participant Auth0 as Auth0 Tenant + participant script as DB Script: change_password + participant Sierra + + User->>Webapp: Change password (old + new) + Webapp->>API: PUT /users/:id/password {password, newPassword} + API->>Auth0: validateCredentials (password grant, old password) + Auth0->>script: login script validates old password against Sierra + script-->>Auth0: Valid + Auth0-->>API: OK + API->>Auth0: Management API PATCH /users/:id {password: newPassword} + Auth0->>script: change_password(email, newPassword) + script->>Sierra: getPatronRecordByEmail → recordNumber + script->>Sierra: updatePatron(recordNumber, {pin: truncate(newPassword, 30)}) + alt PIN trivial pattern + Sierra-->>script: PasswordTooWeak + script-->>Auth0: WrongUsernameOrPasswordError + Auth0-->>API: Error + API-->>User: "Passwords can't contain repeated characters" + else Success + Sierra-->>script: OK + script-->>Auth0: true + Auth0-->>API: 200 + API-->>User: Password updated + end +``` + +## Change Email (change_email) + +```mermaid +sequenceDiagram + participant User + participant Webapp as Identity Webapp + participant API as Identity API + participant Auth0 as Auth0 Tenant + participant script as DB Script: change_email + participant Sierra + + User->>Webapp: Change email (current password + new email) + Webapp->>API: PUT /users/:id {password, email: newEmail} + API->>Auth0: validateCredentials (password grant, current password) + Auth0-->>API: Valid + API->>Auth0: Management API PATCH /users/:id {email: newEmail, verify_email: true} + Auth0->>script: change_email(oldEmail, newEmail, verified=false) + script->>Sierra: getPatronRecordByEmail(oldEmail) → recordNumber + script->>Sierra: updatePatronEmail(recordNumber, newEmail, verified) + Sierra-->>script: OK + script-->>Auth0: true + Auth0-->>API: 200 (updated user) + API-->>User: Email updated, verification email sent +``` + +## Verify Email (verify) + +```mermaid +sequenceDiagram + participant User + participant Auth0 as Auth0 Tenant + participant VerifyScript as DB Script: verify + participant Sierra + + User->>Auth0: Click verification link in email + Auth0->>VerifyScript: verify(email) + VerifyScript->>Sierra: getPatronRecordByEmail(email) → recordNumber + VerifyScript->>Sierra: markPatronEmailVerified(recordNumber) + Sierra-->>VerifyScript: Updated patronRecord + VerifyScript-->>Auth0: User profile (verified) + Auth0->>Auth0: Mark email_verified = true +``` + +## Delete (delete) + +```mermaid +sequenceDiagram + participant Auth0 as Auth0 Tenant + participant DeleteScript as DB Script: delete + participant Sierra + + Note over Auth0: Triggered when a user is
deleted from Auth0 + + Auth0->>DeleteScript: delete(id) + DeleteScript->>DeleteScript: console.log("User deleted: ", id) + Note over DeleteScript,Sierra: No-op: does NOT delete from Sierra
(deletion handled separately by Patron Deletion Tracker) +``` + +## Validate Password (via Identity API) + +```mermaid +sequenceDiagram + participant User + participant Webapp as Identity Webapp + participant API as Identity API + participant Auth0 as Auth0 Tenant + participant LoginScript as DB Script: login + participant Sierra + + Note over User,Webapp: Used as a "confirm identity" check
before sensitive operations + + User->>Webapp: Confirm current password + Webapp->>API: POST /users/:id/validate {password} + API->>Auth0: POST /oauth/token (password grant) + Auth0->>LoginScript: login(email, password) + LoginScript->>Sierra: validateCredentials(barcode, password) + Sierra-->>LoginScript: Success/Failure + LoginScript-->>Auth0: User profile / Error + Auth0-->>API: 200 / 401 + API-->>User: 200 OK / Error +```