Skip to content

User Service API

Overview

This page consolidates the implemented HTTP surface for user-service. It replaces the older split between authentication, flow, and sequence pages and focuses only on contracts that are visible in code today.

Base Paths and API Surface

Aspect Value
Base path families /tyche-wealth/user-service/v1/auth, /tyche-wealth/user-service/v1/user, /tyche-wealth/user-service/v1/user/me
Source of truth *Api.java contracts plus DTOs, service helpers, and centralized error handling
Detected APIs AuthApi.java, UserApi.java

Endpoint Summary

AuthApi.java

Method Path Purpose Operational Note
POST /tyche-wealth/user-service/v1/auth/register Creates a new user account and returns the created user representation. Persists a new active user record and is subject to dedicated registration rate limiting and uniqueness checks.
POST /tyche-wealth/user-service/v1/auth/login Authenticates a user and returns tokenType, accessToken, refreshToken, expiresIn, and the mapped user representation. Validates credentials, revokes any previously active refresh tokens for the user, issues a new access token and refresh token, and records auth metrics.
POST /tyche-wealth/user-service/v1/auth/refresh Validates the submitted refresh token, rotates refresh-token state, and returns tokenType, accessToken, expiresIn, and a replacement refresh token. Revokes the submitted active refresh token, persists a replacement refresh token, returns a new access token, and fails with 401 when the provided refresh token is invalid, expired, or already revoked.
POST /tyche-wealth/user-service/v1/auth/logout Accepts a refresh token request body and logs the user out by revoking the submitted active refresh token. Requires a valid refresh-token request body, revokes the submitted active refresh token, and returns 204 No Content; it does not implement server-side access-JWT invalidation or cache cleanup.

UserApi.java

Method Path Purpose Operational Note
GET /tyche-wealth/user-service/v1/user/me Returns the authenticated active user's id, email, username, and createdAt; sensitive fields such as password, deletedAt, and related collections are omitted from the response DTO. Requires a valid Authorization: Bearer <token> header for an active non-deleted user and returns only the mapped user DTO fields.
PATCH /tyche-wealth/user-service/v1/user/me Updates the authenticated active user's profile fields and returns the updated id, email, username, and createdAt values from the response DTO. Requires a valid bearer token for an active non-deleted user, enforces username availability checks, persists the update, and returns the updated user DTO.
PATCH /tyche-wealth/user-service/v1/user/me/password Changes the authenticated active user's password after validating the current password and returns no response body. Requires a valid bearer token for an active non-deleted user, validates the current password, updates the stored password hash, revokes active refresh tokens, and returns 204 No Content.
DELETE /tyche-wealth/user-service/v1/user/me Soft-deletes the authenticated active user by setting deletedAt, preserves the stored record, revokes active refresh tokens, and returns no response body. Requires a valid bearer token for an active non-deleted user, revokes active refresh tokens, performs a soft delete by setting deletedAt, and returns 204 No Content.

Implemented Endpoints

POST /tyche-wealth/user-service/v1/auth/register

Purpose

Creates a new user account and returns the created user representation.

Contract

Contract Item Value
Success status 201 Created
Source API AuthApi.java
Request DTO RegisterRequestDto
Response DTO UserResponseDto

Validation Snapshot

Input Rules
email Must not be blank.; Must be a valid email address.; Length must be at most 254 characters.; Value is normalized before downstream validation and persistence checks.
username Must not be blank.; Length must be between 3 and 30 characters.; Value is normalized before downstream validation and persistence checks.
password Must not be blank.; Length must be at least 8 characters.; Must match the configured format policy.

Runtime Constraints

  • Service-layer checks: email and username are normalized and must remain unique before user creation proceeds.
  • Dedicated rate limiting: registration requests are intercepted through the register rate-limit configuration.
  • Validation failures are aggregated by the centralized ErrorHandler instead of being returned ad hoc from each controller method.

Error Behavior

Status When it happens
400 Bad Request DTO validation fails, request JSON is malformed, or an auth-specific password format rule rejects the payload.
409 Conflict Registration collides with an existing email or username, either during pre-checks or at the persistence layer.
429 Too Many Requests The endpoint-specific rate-limit interceptor blocks the request because the active window has been exceeded.
Error payload shape Centralized through ErrorHandler, which maps validation, auth, rate-limit, and generic failures to the API response contract.

POST /tyche-wealth/user-service/v1/auth/login

Purpose

Authenticates a user and returns tokenType, accessToken, refreshToken, expiresIn, and the mapped user representation.

Contract

Contract Item Value
Success status 200 OK
Source API AuthApi.java
Request DTO LoginRequestDto
Response DTO LoginResponseDto

Validation Snapshot

Input Rules
email Must not be blank.; Must be a valid email address.; Length must be at most 254 characters.; Value is normalized before downstream validation and persistence checks.
password Must not be blank.; Length must be at least 8 characters.; Must match the configured format policy.

Runtime Constraints

  • Service-layer checks: email is normalized before lookup and the password must satisfy the login password policy before credential matching.
  • Dedicated rate limiting: login requests are intercepted through the login rate-limit configuration.
  • Validation failures are aggregated by the centralized ErrorHandler instead of being returned ad hoc from each controller method.

Error Behavior

Status When it happens
400 Bad Request DTO validation fails, request JSON is malformed, or an auth-specific password format rule rejects the payload.
401 Unauthorized Email does not resolve to a user or the provided password does not match the stored hash.
429 Too Many Requests The endpoint-specific rate-limit interceptor blocks the request because the active window has been exceeded.
Error payload shape Centralized through ErrorHandler, which maps validation, auth, rate-limit, and generic failures to the API response contract.

POST /tyche-wealth/user-service/v1/auth/refresh

Purpose

Validates the submitted refresh token, rotates refresh-token state, and returns tokenType, accessToken, expiresIn, and a replacement refresh token.

Contract

Contract Item Value
Success status 200 OK
Source API AuthApi.java
Request DTO RefreshTokenRequestDto
Response DTO RefreshTokenResponseDto

Validation Snapshot

Input Rules
refreshToken Must not be blank.

Runtime Constraints

  • Service-layer checks: the refresh token must be present, resolvable, not revoked, and still within its validity window before rotation succeeds.
  • Dedicated rate limiting: refresh requests are intercepted through the refresh rate-limit configuration.
  • Validation failures are aggregated by the centralized ErrorHandler instead of being returned ad hoc from each controller method.

Error Behavior

Status When it happens
400 Bad Request DTO validation fails, request JSON is malformed, or an auth-specific password format rule rejects the payload.
401 Unauthorized Refresh token is missing, invalid, revoked, expired, or otherwise rejected during refresh-token validation.
429 Too Many Requests The endpoint-specific rate-limit interceptor blocks the request because the active window has been exceeded.
Error payload shape Centralized through ErrorHandler, which maps validation, auth, rate-limit, and generic failures to the API response contract.

POST /tyche-wealth/user-service/v1/auth/logout

Purpose

Accepts a refresh token request body and logs the user out by revoking the submitted active refresh token.

Contract

Contract Item Value
Success status 204 No Content
Source API AuthApi.java
Request DTO RefreshTokenRequestDto
Response DTO Void

Validation Snapshot

Input Rules
refreshToken Must not be blank.

Runtime Constraints

  • Validation failures are aggregated by the centralized ErrorHandler instead of being returned ad hoc from each controller method.

Error Behavior

Status When it happens
400 Bad Request DTO validation fails, request JSON is malformed, or an auth-specific password format rule rejects the payload.
Error payload shape Centralized through ErrorHandler, which maps validation, auth, rate-limit, and generic failures to the API response contract.

GET /tyche-wealth/user-service/v1/user/me

Purpose

Returns the authenticated active user's id, email, username, and createdAt; sensitive fields such as password, deletedAt, and related collections are omitted from the response DTO.

Contract

Contract Item Value
Success status 200 OK
Source API UserApi.java
Request DTO N/A
Response DTO UserResponseDto

Validation Snapshot

Input Rules
Request body No request DTO is associated with this endpoint.

Runtime Constraints

  • Validation failures are aggregated by the centralized ErrorHandler instead of being returned ad hoc from each controller method.

Error Behavior

Status When it happens
400 Bad Request DTO validation fails, request JSON is malformed, or an auth-specific password format rule rejects the payload.
Error payload shape Centralized through ErrorHandler, which maps validation, auth, rate-limit, and generic failures to the API response contract.

PATCH /tyche-wealth/user-service/v1/user/me

Purpose

Updates the authenticated active user's profile fields and returns the updated id, email, username, and createdAt values from the response DTO.

Contract

Contract Item Value
Success status 200 OK
Source API UserApi.java
Request DTO UserUpdateRequestDto
Response DTO UserResponseDto

Validation Snapshot

Input Rules
username Must not be blank.; Length must be between 3 and 30 characters.; Value is normalized before downstream validation and persistence checks.

Runtime Constraints

  • Validation failures are aggregated by the centralized ErrorHandler instead of being returned ad hoc from each controller method.

Error Behavior

Status When it happens
400 Bad Request DTO validation fails, request JSON is malformed, or an auth-specific password format rule rejects the payload.
Error payload shape Centralized through ErrorHandler, which maps validation, auth, rate-limit, and generic failures to the API response contract.

PATCH /tyche-wealth/user-service/v1/user/me/password

Purpose

Changes the authenticated active user's password after validating the current password and returns no response body.

Contract

Contract Item Value
Success status 204 No Content
Source API UserApi.java
Request DTO UserPasswordUpdateRequestDto
Response DTO Void

Validation Snapshot

Input Rules
currentPassword Must not be blank.; Length must be at least 8 characters.
newPassword Must not be blank.; Length must be at least 8 characters.; Must match the configured format policy.
confirmNewPassword Must not be blank.

Runtime Constraints

  • Validation failures are aggregated by the centralized ErrorHandler instead of being returned ad hoc from each controller method.

Error Behavior

Status When it happens
400 Bad Request DTO validation fails, request JSON is malformed, or an auth-specific password format rule rejects the payload.
Error payload shape Centralized through ErrorHandler, which maps validation, auth, rate-limit, and generic failures to the API response contract.

DELETE /tyche-wealth/user-service/v1/user/me

Purpose

Soft-deletes the authenticated active user by setting deletedAt, preserves the stored record, revokes active refresh tokens, and returns no response body.

Contract

Contract Item Value
Success status 204 No Content
Source API UserApi.java
Request DTO N/A
Response DTO Void

Validation Snapshot

Input Rules
Request body No request DTO is associated with this endpoint.

Runtime Constraints

  • Validation failures are aggregated by the centralized ErrorHandler instead of being returned ad hoc from each controller method.

Error Behavior

Status When it happens
400 Bad Request DTO validation fails, request JSON is malformed, or an auth-specific password format rule rejects the payload.
Error payload shape Centralized through ErrorHandler, which maps validation, auth, rate-limit, and generic failures to the API response contract.

Flows and Sequence Diagrams

Register Flow

%%{init: {
  "theme": "base",
  "themeVariables": {
    "background": "#1c2c29",
    "primaryColor": "#24443d",
    "primaryTextColor": "#ffcc66",
    "primaryBorderColor": "#73e5c6",
    "lineColor": "#ffb347",
    "secondaryColor": "#203934",
    "tertiaryColor": "#294c44",
    "mainBkg": "#24443d",
    "secondBkg": "#203934",
    "tertiaryBkg": "#294c44",
    "clusterBkg": "#17312d",
    "clusterBorder": "#57c8a8",
    "nodeBkg": "#24443d",
    "nodeBorder": "#73e5c6",
    "defaultLinkColor": "#ffb347",
    "titleColor": "#ffcc66",
    "textColor": "#ffcc66",
    "edgeLabelBackground": "#17312d",
    "actorBkg": "#24443d",
    "actorBorder": "#73e5c6",
    "actorTextColor": "#ffcc66",
    "actorLineColor": "#ffb347",
    "signalColor": "#ffb347",
    "signalTextColor": "#ffcc66",
    "labelBoxBkgColor": "#17312d",
    "labelBoxBorderColor": "#57c8a8",
    "labelTextColor": "#ffcc66",
    "loopTextColor": "#ffcc66",
    "noteBkgColor": "#294c44",
    "noteBorderColor": "#82e7cb",
    "noteTextColor": "#ffcc66",
    "activationBkgColor": "#2c5a50",
    "activationBorderColor": "#73e5c6",
    "sectionBkgColor": "#1b3531",
    "altSectionBkgColor": "#22413b",
    "gridColor": "rgba(255, 179, 71, 0.22)"
  },
  "themeCSS": ".messageText, .messageText tspan, .label text, .label tspan, .edgeLabel text, .edgeLabel tspan, .nodeLabel, .nodeLabel p, .nodeLabel span, .label foreignObject, .label foreignObject div, .cluster-label text, .cluster-label tspan, .actor text, .actor tspan, .loopText, .noteText, .er text, .er tspan, .er.entityLabel, .er.attributeText, .er.relationshipLabel, .er foreignObject div, .er foreignObject span, .er foreignObject td, .er foreignObject th { fill: #ffcc66 !important; color: #ffcc66 !important; } .messageLine0, .messageLine1, .flowchart-link, .edgePath path, .actor-line, .er.relationshipLine { stroke: #ffb347 !important; fill: none !important; } .arrowheadPath, marker path { stroke: #ffb347 !important; fill: #ffb347 !important; } .er rect, .er .entityBox, .er .attributeBoxEven, .er .attributeBoxOdd { fill: #24443d !important; stroke: #73e5c6 !important; } .er .relationshipLabelBox { fill: #17312d !important; stroke: #57c8a8 !important; } .er foreignObject, .er foreignObject div, .er foreignObject table { background: #24443d !important; } .er foreignObject tr:nth-child(odd), .er foreignObject tr:nth-child(odd) td { background: #24443d !important; } .er foreignObject tr:nth-child(even), .er foreignObject tr:nth-child(even) td { background: #2b4f47 !important; } .er foreignObject td, .er foreignObject th { border-color: #73e5c6 !important; }"
}}%%
flowchart LR
  A[Client<br/>submits register request] --> B[API<br/>receives request]
  B --> C[Validate DTO<br/>and business rules]
  C --> D[Create user<br/>and initial state]
  D --> E[Persist user<br/>record]
  E --> F[Return created<br/>user response]

Login Sequence

%%{init: {
  "theme": "base",
  "themeVariables": {
    "background": "#1c2c29",
    "primaryColor": "#24443d",
    "primaryTextColor": "#ffcc66",
    "primaryBorderColor": "#73e5c6",
    "lineColor": "#ffb347",
    "secondaryColor": "#203934",
    "tertiaryColor": "#294c44",
    "mainBkg": "#24443d",
    "secondBkg": "#203934",
    "tertiaryBkg": "#294c44",
    "clusterBkg": "#17312d",
    "clusterBorder": "#57c8a8",
    "nodeBkg": "#24443d",
    "nodeBorder": "#73e5c6",
    "defaultLinkColor": "#ffb347",
    "titleColor": "#ffcc66",
    "textColor": "#ffcc66",
    "edgeLabelBackground": "#17312d",
    "actorBkg": "#24443d",
    "actorBorder": "#73e5c6",
    "actorTextColor": "#ffcc66",
    "actorLineColor": "#ffb347",
    "signalColor": "#ffb347",
    "signalTextColor": "#ffcc66",
    "labelBoxBkgColor": "#17312d",
    "labelBoxBorderColor": "#57c8a8",
    "labelTextColor": "#ffcc66",
    "loopTextColor": "#ffcc66",
    "noteBkgColor": "#294c44",
    "noteBorderColor": "#82e7cb",
    "noteTextColor": "#ffcc66",
    "activationBkgColor": "#2c5a50",
    "activationBorderColor": "#73e5c6",
    "sectionBkgColor": "#1b3531",
    "altSectionBkgColor": "#22413b",
    "gridColor": "rgba(255, 179, 71, 0.22)"
  },
  "themeCSS": ".messageText, .messageText tspan, .label text, .label tspan, .edgeLabel text, .edgeLabel tspan, .nodeLabel, .nodeLabel p, .nodeLabel span, .label foreignObject, .label foreignObject div, .cluster-label text, .cluster-label tspan, .actor text, .actor tspan, .loopText, .noteText, .er text, .er tspan, .er.entityLabel, .er.attributeText, .er.relationshipLabel, .er foreignObject div, .er foreignObject span, .er foreignObject td, .er foreignObject th { fill: #ffcc66 !important; color: #ffcc66 !important; } .messageLine0, .messageLine1, .flowchart-link, .edgePath path, .actor-line, .er.relationshipLine { stroke: #ffb347 !important; fill: none !important; } .arrowheadPath, marker path { stroke: #ffb347 !important; fill: #ffb347 !important; } .er rect, .er .entityBox, .er .attributeBoxEven, .er .attributeBoxOdd { fill: #24443d !important; stroke: #73e5c6 !important; } .er .relationshipLabelBox { fill: #17312d !important; stroke: #57c8a8 !important; } .er foreignObject, .er foreignObject div, .er foreignObject table { background: #24443d !important; } .er foreignObject tr:nth-child(odd), .er foreignObject tr:nth-child(odd) td { background: #24443d !important; } .er foreignObject tr:nth-child(even), .er foreignObject tr:nth-child(even) td { background: #2b4f47 !important; } .er foreignObject td, .er foreignObject th { border-color: #73e5c6 !important; }"
}}%%
sequenceDiagram
  participant Client
  participant Api as API
  participant Auth as AuthSvc
  participant Token as Token
  participant Repo as Repo
  participant DB as DB
  Client->>Api: POST /auth/login
  Api->>Auth: login(request)
  Auth->>Auth: validate payload
  Auth->>Auth: authenticate user
  Auth->>Auth: load user context
  Auth->>Token: issue access + refresh
  Auth->>Repo: save token
  Repo->>DB: insert token row
  DB-->>Repo: row stored
  Repo-->>Auth: token persisted
  Token-->>Auth: token pair ready
  Auth-->>Api: login response
  Api-->>Client: 200 OK

Refresh Sequence

%%{init: {
  "theme": "base",
  "themeVariables": {
    "background": "#1c2c29",
    "primaryColor": "#24443d",
    "primaryTextColor": "#ffcc66",
    "primaryBorderColor": "#73e5c6",
    "lineColor": "#ffb347",
    "secondaryColor": "#203934",
    "tertiaryColor": "#294c44",
    "mainBkg": "#24443d",
    "secondBkg": "#203934",
    "tertiaryBkg": "#294c44",
    "clusterBkg": "#17312d",
    "clusterBorder": "#57c8a8",
    "nodeBkg": "#24443d",
    "nodeBorder": "#73e5c6",
    "defaultLinkColor": "#ffb347",
    "titleColor": "#ffcc66",
    "textColor": "#ffcc66",
    "edgeLabelBackground": "#17312d",
    "actorBkg": "#24443d",
    "actorBorder": "#73e5c6",
    "actorTextColor": "#ffcc66",
    "actorLineColor": "#ffb347",
    "signalColor": "#ffb347",
    "signalTextColor": "#ffcc66",
    "labelBoxBkgColor": "#17312d",
    "labelBoxBorderColor": "#57c8a8",
    "labelTextColor": "#ffcc66",
    "loopTextColor": "#ffcc66",
    "noteBkgColor": "#294c44",
    "noteBorderColor": "#82e7cb",
    "noteTextColor": "#ffcc66",
    "activationBkgColor": "#2c5a50",
    "activationBorderColor": "#73e5c6",
    "sectionBkgColor": "#1b3531",
    "altSectionBkgColor": "#22413b",
    "gridColor": "rgba(255, 179, 71, 0.22)"
  },
  "themeCSS": ".messageText, .messageText tspan, .label text, .label tspan, .edgeLabel text, .edgeLabel tspan, .nodeLabel, .nodeLabel p, .nodeLabel span, .label foreignObject, .label foreignObject div, .cluster-label text, .cluster-label tspan, .actor text, .actor tspan, .loopText, .noteText, .er text, .er tspan, .er.entityLabel, .er.attributeText, .er.relationshipLabel, .er foreignObject div, .er foreignObject span, .er foreignObject td, .er foreignObject th { fill: #ffcc66 !important; color: #ffcc66 !important; } .messageLine0, .messageLine1, .flowchart-link, .edgePath path, .actor-line, .er.relationshipLine { stroke: #ffb347 !important; fill: none !important; } .arrowheadPath, marker path { stroke: #ffb347 !important; fill: #ffb347 !important; } .er rect, .er .entityBox, .er .attributeBoxEven, .er .attributeBoxOdd { fill: #24443d !important; stroke: #73e5c6 !important; } .er .relationshipLabelBox { fill: #17312d !important; stroke: #57c8a8 !important; } .er foreignObject, .er foreignObject div, .er foreignObject table { background: #24443d !important; } .er foreignObject tr:nth-child(odd), .er foreignObject tr:nth-child(odd) td { background: #24443d !important; } .er foreignObject tr:nth-child(even), .er foreignObject tr:nth-child(even) td { background: #2b4f47 !important; } .er foreignObject td, .er foreignObject th { border-color: #73e5c6 !important; }"
}}%%
sequenceDiagram
  participant Client
  participant Api as API
  participant Auth as AuthSvc
  participant Token as Token
  participant Repo as Repo
  participant DB as DB
  Client->>Api: POST /auth/refresh
  Api->>Auth: refresh(request)
  Auth->>Auth: validate payload
  Auth->>Repo: find token
  Repo->>DB: select token row
  DB-->>Repo: token row
  Repo-->>Auth: current token
  Auth->>Auth: validate token state
  Auth->>Auth: resolve token owner
  Auth->>Token: issue new access
  Auth->>Repo: save replacement
  Repo->>DB: update token state
  DB-->>Repo: row updated
  Token-->>Auth: new access ready
  Auth-->>Api: refresh response
  Api-->>Client: 200 OK

Refresh Token Lifecycle

%%{init: {
  "theme": "base",
  "themeVariables": {
    "background": "#1c2c29",
    "primaryColor": "#24443d",
    "primaryTextColor": "#ffcc66",
    "primaryBorderColor": "#73e5c6",
    "lineColor": "#ffb347",
    "secondaryColor": "#203934",
    "tertiaryColor": "#294c44",
    "mainBkg": "#24443d",
    "secondBkg": "#203934",
    "tertiaryBkg": "#294c44",
    "clusterBkg": "#17312d",
    "clusterBorder": "#57c8a8",
    "nodeBkg": "#24443d",
    "nodeBorder": "#73e5c6",
    "defaultLinkColor": "#ffb347",
    "titleColor": "#ffcc66",
    "textColor": "#ffcc66",
    "edgeLabelBackground": "#17312d",
    "actorBkg": "#24443d",
    "actorBorder": "#73e5c6",
    "actorTextColor": "#ffcc66",
    "actorLineColor": "#ffb347",
    "signalColor": "#ffb347",
    "signalTextColor": "#ffcc66",
    "labelBoxBkgColor": "#17312d",
    "labelBoxBorderColor": "#57c8a8",
    "labelTextColor": "#ffcc66",
    "loopTextColor": "#ffcc66",
    "noteBkgColor": "#294c44",
    "noteBorderColor": "#82e7cb",
    "noteTextColor": "#ffcc66",
    "activationBkgColor": "#2c5a50",
    "activationBorderColor": "#73e5c6",
    "sectionBkgColor": "#1b3531",
    "altSectionBkgColor": "#22413b",
    "gridColor": "rgba(255, 179, 71, 0.22)"
  },
  "themeCSS": ".messageText, .messageText tspan, .label text, .label tspan, .edgeLabel text, .edgeLabel tspan, .nodeLabel, .nodeLabel p, .nodeLabel span, .label foreignObject, .label foreignObject div, .cluster-label text, .cluster-label tspan, .actor text, .actor tspan, .loopText, .noteText, .er text, .er tspan, .er.entityLabel, .er.attributeText, .er.relationshipLabel, .er foreignObject div, .er foreignObject span, .er foreignObject td, .er foreignObject th { fill: #ffcc66 !important; color: #ffcc66 !important; } .messageLine0, .messageLine1, .flowchart-link, .edgePath path, .actor-line, .er.relationshipLine { stroke: #ffb347 !important; fill: none !important; } .arrowheadPath, marker path { stroke: #ffb347 !important; fill: #ffb347 !important; } .er rect, .er .entityBox, .er .attributeBoxEven, .er .attributeBoxOdd { fill: #24443d !important; stroke: #73e5c6 !important; } .er .relationshipLabelBox { fill: #17312d !important; stroke: #57c8a8 !important; } .er foreignObject, .er foreignObject div, .er foreignObject table { background: #24443d !important; } .er foreignObject tr:nth-child(odd), .er foreignObject tr:nth-child(odd) td { background: #24443d !important; } .er foreignObject tr:nth-child(even), .er foreignObject tr:nth-child(even) td { background: #2b4f47 !important; } .er foreignObject td, .er foreignObject th { border-color: #73e5c6 !important; }"
}}%%
flowchart LR
  Issue[Login issues<br/>token] --> Store[Persist token]
  Store --> Use[Client sends<br/>token]
  Use --> Validate[Validate token]
  Validate --> Rotate[Generate<br/>replacement]
  Rotate --> Revoke[Old token<br/>revoked]
  Revoke --> Persist[Persist new<br/>token state]

Notes

  • Treat routes not listed here as not implemented unless a concrete controller or API contract is added.
  • Use controller interfaces, DTOs, service implementations, and error handlers as the source of truth for API behavior.