> ## Documentation Index
> Fetch the complete documentation index at: https://docs.sagescreen.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Log in (session + JWT)

> WordPress AJAX login with reCAPTCHA/Turnstile verification.
On success, establishes a WP session cookie **and** issues a JWT
token pair stored in `$_SESSION`.

Registered as `wp_ajax_nopriv_sagescreen_login` — **public**.

**Special behaviours:**
- Hard maintenance mode blocks non-global-admin logins.
- Users with `member` role or no `council_id` are blocked (except global admins).
- If a pending Stripe order session exists, it is processed and credits are appended to the redirect URL.
- TODO(productlane): BoldDesk SSO removed from this spec — the login handler still emits a BoldDesk JWT when the request originates from BoldDesk; remove or replace with the ProductLane flow.




## OpenAPI

````yaml /openapi/private/specs/wp/auth.yml post /wp-admin/admin-ajax.php?action=sagescreen_login
openapi: 3.1.0
info:
  title: SageScreen — Auth Module
  description: >
    Authentication and session management. Handles JWT token lifecycle

    (issue, refresh, validate, revoke) and WordPress-based login/logout

    with reCAPTCHA/Turnstile protection.


    Uses RS256-signed JWTs stored in the `wp_sage_screen_jwt` table.

    Tokens have a configurable TTL (`sage_screen_jwt_expires`, default 3600 s)

    and a separate refresh window (`sage_screen_jwt_refresh_expires`, default
    604800 s / 7 days).
  version: 1.0.0
servers:
  - url: https://{domain}/wp-json/sagescreen/v1
    description: WordPress REST API
    variables:
      domain:
        default: api.sagescreen.app
security: []
tags:
  - name: Auth – REST
    description: JWT token endpoints (REST API)
  - name: Auth – AJAX
    description: Session-based login / logout / password flows (AJAX)
paths:
  /wp-admin/admin-ajax.php?action=sagescreen_login:
    post:
      tags:
        - Auth – AJAX
      summary: Log in (session + JWT)
      description: >
        WordPress AJAX login with reCAPTCHA/Turnstile verification.

        On success, establishes a WP session cookie **and** issues a JWT

        token pair stored in `$_SESSION`.


        Registered as `wp_ajax_nopriv_sagescreen_login` — **public**.


        **Special behaviours:**

        - Hard maintenance mode blocks non-global-admin logins.

        - Users with `member` role or no `council_id` are blocked (except global
        admins).

        - If a pending Stripe order session exists, it is processed and credits
        are appended to the redirect URL.

        - TODO(productlane): BoldDesk SSO removed from this spec — the login
        handler still emits a BoldDesk JWT when the request originates from
        BoldDesk; remove or replace with the ProductLane flow.
      operationId: ajaxLogin
      requestBody:
        required: true
        content:
          application/x-www-form-urlencoded:
            schema:
              $ref: '#/components/schemas/AjaxLoginRequest'
      responses:
        '200':
          description: Login result (check `success` field)
          content:
            application/json:
              schema:
                oneOf:
                  - $ref: '#/components/schemas/AjaxLoginSuccess'
                  - $ref: '#/components/schemas/AjaxErrorResponse'
              examples:
                success:
                  summary: Successful login
                  value:
                    success: true
                    data:
                      message: Login successful
                      redirect_to: /admin/dashboard
                successWithOrder:
                  summary: Login with pending order processed
                  value:
                    success: true
                    data:
                      message: Login successful! Redirecting...
                      redirect_to: >-
                        /admin/dashboard?order_complete=1&credits=100&sage_credits=5
                nonceFail:
                  summary: Nonce check failed
                  value:
                    success: false
                    data: Security check failed
                recaptchaFail:
                  summary: reCAPTCHA failed
                  value:
                    success: false
                    data: Security verification failed. Please try again.
                missingFields:
                  summary: Missing credentials
                  value:
                    success: false
                    data: Username and password are required
                invalidCreds:
                  summary: Bad username or password
                  value:
                    success: false
                    data: Invalid username or password.
                maintenance:
                  summary: Hard maintenance mode
                  value:
                    success: false
                    data: >-
                      System is currently in maintenance mode. Only
                      administrators can access the system at this time. Please
                      try again later.
                noCouncil:
                  summary: User has no council
                  value:
                    success: false
                    data:
                      message: >-
                        Your account is not currently active. Please contact
                        your administrator for assistance.
                      no_council: true
      security: []
components:
  schemas:
    AjaxLoginRequest:
      type: object
      required:
        - action
        - login_nonce
        - recaptcha_token
        - username
        - password
      properties:
        action:
          type: string
          const: sagescreen_login
        login_nonce:
          type: string
          description: WP nonce for `sagescreen_login`
        recaptcha_token:
          type: string
          description: Cloudflare Turnstile / reCAPTCHA token
        username:
          type: string
          examples:
            - john.doe
        password:
          type: string
          format: password
          examples:
            - SecurePass123
        remember_me:
          type: string
          description: '''1'' to persist session, omit otherwise'
          examples:
            - '1'
        redirect_to:
          type: string
          format: uri
          description: Post-login redirect URL (optional, auto-determined if omitted)
          examples:
            - /admin/dashboard
    AjaxLoginSuccess:
      type: object
      properties:
        success:
          type: boolean
          const: true
        data:
          type: object
          required:
            - message
            - redirect_to
          properties:
            message:
              type: string
              examples:
                - Login successful
            redirect_to:
              type: string
              description: URL to redirect the client to
              examples:
                - /admin/dashboard
    AjaxErrorResponse:
      type: object
      description: |
        Standard AJAX error. The `data` field is either a plain string
        or an object with additional context (e.g. `no_council: true`).
      properties:
        success:
          type: boolean
          const: false
        data:
          oneOf:
            - type: string
              examples:
                - Security check failed
            - type: object
              properties:
                message:
                  type: string
                no_council:
                  type: boolean

````