"/auth/whoami":
    get:
        tags: [Auth]
        operationId: whoami
        summary: Bearer / IP debug echo
        description: |
            Returns a debug payload describing how the server resolved the
            caller's IP (express `req.ip`, `x-forwarded-for`, raw socket,
            `forwarded-real-ip`). Useful for verifying the
            `trust proxy` configuration in front of the service.
        security:
            - apiKey: []
        responses:
            "200":
                description: Debug payload.
                content:
                    application/json:
                        schema:
                            type: object
                            required: [result, ip, chain]
                            properties:
                                result: { type: string, example: "successfully authenticated" }
                                ip: { type: string }
                                chain:
                                    type: object
                                    properties:
                                        reqIp: { type: string }
                                        reqIps:
                                            type: array
                                            items: { type: string }
                                        socket: { type: string }
                                        xForwardedFor: { type: [string, "null"] }
                                        forwardedRealIp: { type: [string, "null"] }
            "401": { $ref: "../auth-api.yaml#/components/responses/Unauthorized" }

"/login":
    post:
        tags: [Auth]
        operationId: legacyEmailPasswordLogin
        summary: "Email / password login (deprecated alias for `/auth/login`)"
        description: |
            **Deprecated.** Behaves identically to `POST /auth/login`. Kept
            for legacy clients; new clients should use `/auth/login`.
        deprecated: true
        requestBody:
            required: true
            content:
                application/json:
                    schema: { $ref: "../components/schemas/EmailPasswordLoginRequest.yaml" }
        responses:
            "200":
                description: Logged in. Tokens are emitted via response headers — see below.
                headers:
                    x-access-token: { $ref: "../auth-api.yaml#/components/headers/AccessToken" }
                    x-refresh-token: { $ref: "../auth-api.yaml#/components/headers/RefreshToken" }
                    x-timestamp: { $ref: "../auth-api.yaml#/components/headers/Timestamp" }
                    x-nonce: { $ref: "../auth-api.yaml#/components/headers/Nonce" }
                content:
                    application/json:
                        schema: { $ref: "../components/schemas/AccessTokenBundle.yaml" }
            "401":
                description: Bad credentials.
                content:
                    application/json:
                        schema: { $ref: "../auth-api.yaml#/components/schemas/Error" }

"/renew":
    post:
        tags: [Auth]
        operationId: legacyRenewAccessToken
        summary: "Renew access token (deprecated alias for `/auth/renew`)"
        description: |
            **Deprecated.** Behaves identically to `GET /auth/renew` — the
            method is `POST` here for legacy reasons. Only the access
            token is rotated — the refresh token is reused.
        deprecated: true
        security:
            - apiKey: []
              refreshToken: []
              nonce: []
        responses:
            "200":
                description: Refreshed access token (in `x-access-token` response header).
                headers:
                    x-access-token: { $ref: "../auth-api.yaml#/components/headers/AccessToken" }
                content:
                    application/json:
                        schema: { $ref: "../components/schemas/AccessTokenBundle.yaml" }
            "401": { $ref: "../auth-api.yaml#/components/responses/Unauthorized" }

"/verify":
    post:
        tags: [Auth]
        operationId: legacyCheckAccountStatus
        summary: "Verify access token (deprecated alias for `/auth/verify`)"
        description: |
            **Deprecated.** Behaves identically to `GET /auth/verify` — the
            method is `POST` here for legacy reasons.
        deprecated: true
        responses:
            "200":
                description: Token is valid; account status payload.
                content:
                    application/json:
                        schema: { $ref: "../components/schemas/AccountStatusResponse.yaml" }
            "401": { $ref: "../auth-api.yaml#/components/responses/Unauthorized" }

"/auth/login":
    post:
        tags: [Auth]
        operationId: handleEmailPasswordLogin
        summary: Email / password login
        description: |
            Verifies the supplied credentials and emits an access /
            refresh token bundle. Tokens travel via response **headers**
            (`x-access-token`, `x-refresh-token`, `x-timestamp`,
            `x-nonce`) — the response body is a `{ result: "OK" }`
            sentinel.
        requestBody:
            required: true
            content:
                application/json:
                    schema: { $ref: "../components/schemas/EmailPasswordLoginRequest.yaml" }
        responses:
            "200":
                description: Logged in. Tokens are emitted via response headers — see below.
                headers:
                    x-access-token: { $ref: "../auth-api.yaml#/components/headers/AccessToken" }
                    x-refresh-token: { $ref: "../auth-api.yaml#/components/headers/RefreshToken" }
                    x-timestamp: { $ref: "../auth-api.yaml#/components/headers/Timestamp" }
                    x-nonce: { $ref: "../auth-api.yaml#/components/headers/Nonce" }
                content:
                    application/json:
                        schema: { $ref: "../components/schemas/AccessTokenBundle.yaml" }
            "401":
                description: Bad credentials or banned account.
                content:
                    application/json:
                        schema: { $ref: "../auth-api.yaml#/components/schemas/Error" }

"/auth/logout":
    get:
        tags: [Auth]
        operationId: handleLogout
        summary: Invalidate the current access token
        description: |
            Marks the access token as no longer valid. The refresh token
            is also retired — re-login is required to mint a new bundle.
        responses:
            "200":
                description: Logged out.
                content:
                    application/json:
                        schema:
                            type: object
                            required: [message]
                            properties:
                                message: { type: string, example: OK }
            "401": { $ref: "../auth-api.yaml#/components/responses/Unauthorized" }

"/auth/oculus/login":
    post:
        tags: [Auth]
        operationId: handleOculusLogin
        summary: Login via Oculus credentials
        description: |
            Exchanges Oculus credentials for a Bigscreen access bundle.
            Tokens travel via response headers — see `/auth/login`.
        requestBody:
            required: true
            content:
                application/json:
                    schema: { $ref: "../components/schemas/OculusLoginRequest.yaml" }
        responses:
            "200":
                description: Logged in. Tokens are emitted via response headers — see below.
                headers:
                    x-access-token: { $ref: "../auth-api.yaml#/components/headers/AccessToken" }
                    x-refresh-token: { $ref: "../auth-api.yaml#/components/headers/RefreshToken" }
                    x-timestamp: { $ref: "../auth-api.yaml#/components/headers/Timestamp" }
                    x-nonce: { $ref: "../auth-api.yaml#/components/headers/Nonce" }
                content:
                    application/json:
                        schema: { $ref: "../components/schemas/AccessTokenBundle.yaml" }
            "401":
                description: Invalid Oculus credentials.
                content:
                    application/json:
                        schema: { $ref: "../auth-api.yaml#/components/schemas/Error" }

"/auth/steam/login":
    post:
        tags: [Auth]
        operationId: handleSteamLogin
        summary: Login via Steam credentials
        description: |
            Exchanges Steam OpenID credentials for a Bigscreen access
            bundle. Tokens travel via response headers — see `/auth/login`.
        requestBody:
            required: true
            content:
                application/json:
                    schema: { $ref: "../components/schemas/SteamLoginRequest.yaml" }
        responses:
            "200":
                description: Logged in. Tokens are emitted via response headers — see below.
                headers:
                    x-access-token: { $ref: "../auth-api.yaml#/components/headers/AccessToken" }
                    x-refresh-token: { $ref: "../auth-api.yaml#/components/headers/RefreshToken" }
                    x-timestamp: { $ref: "../auth-api.yaml#/components/headers/Timestamp" }
                    x-nonce: { $ref: "../auth-api.yaml#/components/headers/Nonce" }
                content:
                    application/json:
                        schema: { $ref: "../components/schemas/AccessTokenBundle.yaml" }
            "401":
                description: Invalid Steam credentials.
                content:
                    application/json:
                        schema: { $ref: "../auth-api.yaml#/components/schemas/Error" }

"/auth/verify":
    get:
        tags: [Auth]
        operationId: checkAccountStatus
        summary: Verify access token
        description: |
            Returns `200` when the access token is valid. The presence of
            `200` already implies the account is in an admitted state —
            banned / read-only states surface as `401` / `403`, not in the
            body. The body is a `{ result: "OK" }` sentinel.
        responses:
            "200":
                description: Token valid.
                content:
                    application/json:
                        schema: { $ref: "../components/schemas/AccountStatusResponse.yaml" }
            "401": { $ref: "../auth-api.yaml#/components/responses/Unauthorized" }

"/auth/renew":
    get:
        tags: [Auth]
        operationId: renewAccessToken
        summary: Renew an access token with a refresh token
        description: |
            Exchanges a refresh token + one-time nonce for a fresh access
            token. The nonce is the value returned in the `x-nonce`
            response header on the previous expiry response. **Only the
            access token rotates** — the refresh token is reused. The
            new access token is in the `x-access-token` response header.
        security:
            - apiKey: []
              refreshToken: []
              nonce: []
        responses:
            "200":
                description: Refreshed access token (in `x-access-token` response header).
                headers:
                    x-access-token: { $ref: "../auth-api.yaml#/components/headers/AccessToken" }
                content:
                    application/json:
                        schema: { $ref: "../components/schemas/AccessTokenBundle.yaml" }
            "401": { $ref: "../auth-api.yaml#/components/responses/Unauthorized" }

"/auth/oculus/verify":
    post:
        tags: [Auth]
        operationId: checkOculusNonce
        summary: Verify an Oculus nonce (non-prod only)
        description: |
            **Non-production only.** Bound when
            `api.get("env") !== "production"` — confirms a supplied Oculus
            nonce is valid against the Oculus API. Used by integration
            tests; not registered on production deployments.
        requestBody:
            required: true
            content:
                application/json:
                    schema:
                        type: object
                        required: [oculusUserId, nonce]
                        properties:
                            oculusUserId: { type: string }
                            nonce: { type: string }
        responses:
            "200":
                description: Nonce is valid.
                content:
                    application/json:
                        schema:
                            type: object
                            required: [result]
                            properties:
                                result: { type: string, example: ok }
            "401":
                description: Nonce is invalid or expired.
                content:
                    application/json:
                        schema: { $ref: "../auth-api.yaml#/components/schemas/Error" }
            "404":
                description: Endpoint not registered (this is a production deployment).

"/auth/token/login/{singleUseLoginToken}":
    parameters:
        - in: path
          name: singleUseLoginToken
          required: true
          schema: { type: string }
    get:
        tags: [Auth]
        operationId: verifySingleUseLoginToken
        summary: Magic-link login (v1)
        description: |
            Consumes a single-use magic-link token issued by the email
            flow and emits a fresh access bundle. Token is invalidated
            on success. The body additionally carries a `redirectUrl`
            the email-flow client follows.
        responses:
            "200":
                description: Logged in. Tokens are emitted via response headers — see below.
                headers:
                    x-access-token: { $ref: "../auth-api.yaml#/components/headers/AccessToken" }
                    x-refresh-token: { $ref: "../auth-api.yaml#/components/headers/RefreshToken" }
                    x-timestamp: { $ref: "../auth-api.yaml#/components/headers/Timestamp" }
                    x-nonce: { $ref: "../auth-api.yaml#/components/headers/Nonce" }
                content:
                    application/json:
                        schema: { $ref: "../components/schemas/AccessTokenBundle.yaml" }
            "401": { $ref: "../auth-api.yaml#/components/responses/Unauthorized" }
            "404":
                description: Token unknown / consumed / expired.

"/auth/token2/login/{publicToken}":
    parameters:
        - in: path
          name: publicToken
          required: true
          schema: { type: string }
    get:
        tags: [Auth]
        operationId: verifyLoginToken2
        summary: Magic-link login (v2)
        description: |
            v2 of the magic-link flow — the request carries the *public*
            half of the token; the *private* half is supplied via
            `LoginToken2`-flavored verification. See `LoginToken2` schema.
            Tokens travel via response headers; the body additionally
            carries a `redirectUrl`.
        responses:
            "200":
                description: Logged in. Tokens are emitted via response headers — see below.
                headers:
                    x-access-token: { $ref: "../auth-api.yaml#/components/headers/AccessToken" }
                    x-refresh-token: { $ref: "../auth-api.yaml#/components/headers/RefreshToken" }
                    x-timestamp: { $ref: "../auth-api.yaml#/components/headers/Timestamp" }
                    x-nonce: { $ref: "../auth-api.yaml#/components/headers/Nonce" }
                content:
                    application/json:
                        schema: { $ref: "../components/schemas/AccessTokenBundle.yaml" }
            "401": { $ref: "../auth-api.yaml#/components/responses/Unauthorized" }
            "404":
                description: Token unknown / consumed / expired.
