openapi: 3.1.0
info:
  title: Auth Service API
  description: >
    Authentication and authorization service for the True Markets platform —
    issues JWT access/refresh tokens used across the Gateway, DeFi, and other
    True Markets APIs.


    ## Base URLs


    | Environment | Base URL |

    |---|---|

    | Production | `https://api.truemarkets.co/v1/auth` |

    | UAT (sandbox) | `https://api.uat.truemarkets.co/v1/auth` |


    ## Authentication tutorial


    Programmatic clients use an ECDSA-signed challenge to mint short-lived JWTs.


    1. **Create an account** at
    [https://www.truemarkets.co](https://www.truemarkets.co) (passkey, email,
    magic link, or Sign in with Apple).

    2. **Register an API key** in your account's *API Keys* settings page.
    Generate an EC P-256 key pair locally and submit only the public key — the
    private key never leaves your machine. You'll receive a `key_id` (UUID).

    3. **Mint JWTs** by calling `POST /api-key/token` with `key_id`, a current
    `timestamp` (Unix seconds, within ±30s of server UTC time), and `signature`
    — an ES256 (ECDSA P-256 + SHA-256) signature of the message
    `{key_id}.{timestamp}`, base64url-encoded. The response returns
    `access_token` and `refresh_token`.

    4. **Call True Markets APIs** (Gateway, DeFi) with `Authorization: Bearer
    <access_token>`.

    5. **Refresh** expired access tokens via `POST /token/refresh` with the
    `refresh_token` — no re-signing required.


    ### Quick start


    ```bash

    # 1. Mint a JWT (key_id and signature computed client-side)

    curl -X POST https://api.truemarkets.co/v1/auth/api-key/token \
      -H "Content-Type: application/json" \
      -d '{"key_id":"<UUID>","timestamp":<UNIX_SECONDS>,"signature":"<BASE64URL_ES256_SIG>"}'

    # 2. Fetch JWKS to verify token signatures locally

    curl https://api.truemarkets.co/.well-known/jwks.json


    # 3. Refresh a token before expiry

    curl -X POST https://api.truemarkets.co/v1/auth/token/refresh \
      -H "Content-Type: application/json" \
      -d '{"refresh_token":"<REFRESH_TOKEN>"}'
    ```


    ## Support

    - 📧 [support@truemarkets.co](mailto:support@truemarkets.co)
  version: 1.0.0
servers:
  - url: https://api.truemarkets.co/v1/auth
    description: Production
  - url: https://api.uat.truemarkets.co/v1/auth
    description: UAT (sandbox)
paths:
  /health:
    get:
      summary: Health check
      description: Returns the health status of the auth service
      operationId: getHealth
      tags:
        - Health
      security: []
      responses:
        '200':
          description: Service is healthy
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HealthResponse'
  /.well-known/jwks.json:
    get:
      summary: Get JSON Web Key Set
      description: Returns the public keys used to verify JWT tokens
      operationId: getJWKS
      tags:
        - JWKS
      security: []
      responses:
        '200':
          description: JWKS retrieved successfully
          headers:
            Cache-Control:
              schema:
                type: string
              description: Caching directive (public, max-age=3600)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/JWKSResponse'
        '500':
          description: Failed to retrieve JWKS
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /token/refresh:
    post:
      summary: Refresh access token
      description: Exchange a valid refresh token for a new access/refresh token pair
      operationId: refreshToken
      tags:
        - Token
      security: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/RefreshTokenRequest'
      responses:
        '200':
          description: Tokens refreshed successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AccessTokensResponse'
        '400':
          description: Invalid request body or missing refresh token
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '401':
          description: Invalid or expired refresh token
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '500':
          description: Internal server error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /api-key/token:
    post:
      summary: Exchange API key for tokens
      description: >
        Verify an ECDSA P-256 signature and issue JWT tokens. The client signs

        the message `{key_id}.{unix_timestamp}` with their private key using
        ES256.

        The timestamp must be within ±30 seconds of the server time.
      operationId: exchangeAPIKeyToken
      tags:
        - API Key Authentication
      security: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/APIKeyTokenExchangeRequest'
      responses:
        '200':
          description: Tokens issued successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AccessTokensResponse'
        '400':
          description: Invalid request (missing fields)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '401':
          description: >-
            Invalid API key, expired, revoked, invalid signature, or timestamp
            out of range
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '500':
          description: Internal server error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
components:
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT
      description: JWT access token obtained from authentication endpoints
  schemas:
    HealthResponse:
      type: object
      required:
        - status
        - timestamp
        - service
      properties:
        status:
          type: string
          description: Health status
          example: healthy
        timestamp:
          type: string
          format: date-time
          description: Current server timestamp
        service:
          type: string
          description: Service name
          example: auth-service
    ErrorResponse:
      type: object
      required:
        - message
      properties:
        message:
          type: string
          description: Error message
    StatusResponse:
      type: object
      required:
        - status
      properties:
        status:
          type: string
          description: Operation status
          example: success
    AccessTokensResponse:
      type: object
      required:
        - access_token
        - refresh_token
        - expires_in
        - token_type
      properties:
        access_token:
          type: string
          description: JWT access token
        refresh_token:
          type: string
          description: JWT refresh token
        expires_in:
          type: string
          format: date-time
          description: Access token expiration timestamp
        token_type:
          type: string
          description: Token type (always "Bearer")
          example: Bearer
    JWKSResponse:
      type: object
      required:
        - keys
      properties:
        keys:
          type: array
          items:
            $ref: '#/components/schemas/JWK'
          description: Array of JSON Web Keys
    JWK:
      type: object
      required:
        - kty
        - kid
        - use
        - alg
      properties:
        kty:
          type: string
          description: Key type
          example: RSA
        kid:
          type: string
          description: Key ID
        use:
          type: string
          description: Key use (sig for signature)
          example: sig
        alg:
          type: string
          description: Algorithm
          example: RS256
        'n':
          type: string
          description: RSA modulus (base64url encoded)
        e:
          type: string
          description: RSA exponent (base64url encoded)
    RefreshTokenRequest:
      type: object
      required:
        - refresh_token
      properties:
        refresh_token:
          type: string
          description: The refresh token to exchange
    OTCRequest:
      type: object
      required:
        - email
      properties:
        email:
          type: string
          format: email
          description: Email address to send the one-time code
    VerifyOTCRequest:
      type: object
      required:
        - email
        - code
      properties:
        email:
          type: string
          format: email
          description: Email address used for verification
        code:
          type: string
          description: The one-time code received via email
          minLength: 6
          maxLength: 6
    MagicLinkRequest:
      type: object
      required:
        - email
      properties:
        email:
          type: string
          format: email
          description: Email address to send the magic link
        client_id:
          type: string
          description: >-
            Client application identifier. Determines which domain the magic
            link points to. Defaults to "true_markets" if not specified.
          example: truemarkets_ai
    MagicLinkVerifyRequest:
      type: object
      required:
        - token
      properties:
        token:
          type: string
          description: The magic link token from the email URL
    MagicLinkClaimRequest:
      type: object
      required:
        - session_id
      properties:
        session_id:
          type: string
          description: The session ID returned when creating the magic link
    MagicLinkSessionResponse:
      type: object
      required:
        - session_id
        - status
        - expires_at
      properties:
        session_id:
          type: string
          description: Unique session identifier
        status:
          type: string
          description: Session status (pending, approved, claimed)
          enum:
            - pending
            - approved
            - claimed
        expires_at:
          type: string
          format: date-time
          description: Session expiration timestamp
    GenerateAuthenticationOptionsRequest:
      type: object
      required:
        - email
      properties:
        email:
          type: string
          format: email
          description: Email address of the user to authenticate
    PasskeyRegistrationRequest:
      type: object
      required:
        - response
      properties:
        response:
          $ref: '#/components/schemas/WebAuthnCredentialCreationResponse'
    PasskeyAuthenticationRequest:
      type: object
      required:
        - email
        - response
      properties:
        email:
          type: string
          format: email
          description: Email address of the user
        response:
          $ref: '#/components/schemas/WebAuthnCredentialAssertionResponse'
    WebAuthnRegistrationOptions:
      type: object
      description: WebAuthn PublicKeyCredentialCreationOptions
      properties:
        challenge:
          type: string
          description: Base64url encoded challenge
        rp:
          type: object
          properties:
            name:
              type: string
            id:
              type: string
        user:
          type: object
          properties:
            id:
              type: string
            name:
              type: string
            displayName:
              type: string
        pubKeyCredParams:
          type: array
          items:
            type: object
            properties:
              type:
                type: string
              alg:
                type: integer
        timeout:
          type: integer
        authenticatorSelection:
          type: object
          properties:
            authenticatorAttachment:
              type: string
            residentKey:
              type: string
            userVerification:
              type: string
        attestation:
          type: string
    WebAuthnAuthenticationOptions:
      type: object
      description: WebAuthn PublicKeyCredentialRequestOptions
      properties:
        challenge:
          type: string
          description: Base64url encoded challenge
        timeout:
          type: integer
        rpId:
          type: string
        allowCredentials:
          type: array
          items:
            type: object
            properties:
              type:
                type: string
              id:
                type: string
              transports:
                type: array
                items:
                  type: string
        userVerification:
          type: string
    WebAuthnCredentialCreationResponse:
      type: object
      description: WebAuthn credential creation response from authenticator
      properties:
        id:
          type: string
        rawId:
          type: string
        type:
          type: string
        response:
          type: object
          properties:
            attestationObject:
              type: string
            clientDataJSON:
              type: string
    WebAuthnCredentialAssertionResponse:
      type: object
      description: WebAuthn credential assertion response from authenticator
      properties:
        id:
          type: string
        rawId:
          type: string
        type:
          type: string
        response:
          type: object
          properties:
            authenticatorData:
              type: string
            clientDataJSON:
              type: string
            signature:
              type: string
            userHandle:
              type: string
    RegisterAPIKeyRequest:
      type: object
      required:
        - label
        - public_key
        - fingerprint
      properties:
        label:
          type: string
          description: Human-readable label for the API key
          minLength: 1
          maxLength: 255
        public_key:
          type: object
          description: EC P-256 public key in JWK format
          required:
            - kty
            - crv
            - x
            - 'y'
          properties:
            kty:
              type: string
              enum:
                - EC
            crv:
              type: string
              enum:
                - P-256
            x:
              type: string
              description: Base64url-encoded x coordinate
            'y':
              type: string
              description: Base64url-encoded y coordinate
        fingerprint:
          type: string
          description: SHA-256 hash of the canonical JWK representation
          minLength: 1
          maxLength: 64
    RegisterAPIKeyResponse:
      type: object
      required:
        - key_id
        - label
        - fingerprint
        - created_at
      properties:
        key_id:
          type: string
          format: uuid
          description: Unique identifier for the registered API key
        label:
          type: string
          description: Human-readable label
        fingerprint:
          type: string
          description: Key fingerprint
        created_at:
          type: string
          format: date-time
          description: When the key was registered
    APIKeySummary:
      type: object
      required:
        - key_id
        - label
        - fingerprint
        - key_algorithm
        - scopes
        - created_at
      properties:
        key_id:
          type: string
          format: uuid
        label:
          type: string
        fingerprint:
          type: string
        key_algorithm:
          type: string
          example: ES256
        scopes:
          type: array
          items:
            type: string
        created_at:
          type: string
          format: date-time
        last_used_at:
          type: string
          format: date-time
          nullable: true
        expires_at:
          type: string
          format: date-time
          nullable: true
    PasskeySummary:
      type: object
      required:
        - id
        - label
        - device_type
        - backup_eligible
        - created_at
      properties:
        id:
          type: string
        label:
          type: string
        device_type:
          type: string
        backup_eligible:
          type: boolean
        created_at:
          type: string
          format: date-time
        last_used_at:
          type: string
          format: date-time
          nullable: true
    UpdateLabelRequest:
      type: object
      required:
        - label
      properties:
        label:
          type: string
          minLength: 1
          maxLength: 255
          description: The new label for the resource
    APIKeyTokenExchangeRequest:
      type: object
      required:
        - key_id
        - timestamp
        - signature
      properties:
        key_id:
          type: string
          format: uuid
          description: The API key ID
        timestamp:
          type: integer
          format: int64
          description: Current Unix timestamp (must be within ±30s of server time)
        signature:
          type: string
          description: Base64url-encoded ECDSA P-256 signature of `{key_id}.{timestamp}`
    SignInWithAppleRequest:
      type: object
      required:
        - authorization_code
      properties:
        authorization_code:
          type: string
          description: Authorization code from Apple
    AccountLinkingRequiredResponse:
      type: object
      required:
        - requires_linking
        - email
        - message
        - action
      properties:
        requires_linking:
          type: boolean
          description: Indicates account linking is required
          example: true
        email:
          type: string
          format: email
          description: Email address that requires linking
        message:
          type: string
          description: Human-readable message explaining the situation
        action:
          type: string
          description: Recommended action to take
          example: contact_support_for_linking
    DisconnectAppleResponse:
      type: object
      required:
        - status
        - message
      properties:
        status:
          type: string
          description: Operation status
          example: success
        message:
          type: string
          description: Human-readable success message
          example: Apple account disconnected successfully
    AppleWebhookRequest:
      type: object
      required:
        - payload
      properties:
        payload:
          type: string
          description: JWT payload from Apple containing the webhook event
    WebhookResponse:
      type: object
      required:
        - status
      properties:
        status:
          type: string
          description: Processing status
          example: success
        event_type:
          type: string
          description: Type of webhook event processed
        message:
          type: string
          description: Human-readable status message
