{
  "openapi": "3.1.0",
  "info": {
    "title": "Localisync Authentication API",
    "version": "1.0.0",
    "description": "Same-origin Customer Panel API. Production uses Secure HttpOnly SameSite=Lax cookies. Local HTTP uses the localisync-session name only with explicit development configuration. All auth responses use Cache-Control: no-store."
  },
  "paths": {
    "/api/v1/auth/login": {
      "get": {
        "operationId": "beginLogin",
        "summary": "Begin sign-in",
        "description": "Starts a full browser navigation to Keycloak using Authorization Code + PKCE S256. State and nonce bind the callback to this browser. No caller-selected return URL is accepted.",
        "security": [],
        "responses": {
          "302": {
            "description": "Full browser navigation to the configured destination. Never accept a caller-supplied redirect.",
            "headers": {
              "Location": {
                "schema": {
                  "type": "string",
                  "format": "uri"
                }
              },
              "Cache-Control": {
                "schema": {
                  "const": "no-store"
                }
              }
            }
          },
          "400": {
            "description": "Request rejected or dependency unavailable. Responses are never shared-cacheable.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Request rejected or dependency unavailable. Responses are never shared-cacheable.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Request rejected or dependency unavailable. Responses are never shared-cacheable.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Request rejected or dependency unavailable. Responses are never shared-cacheable.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Request rejected or dependency unavailable. Responses are never shared-cacheable.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/auth/callback": {
      "get": {
        "operationId": "completeLogin",
        "summary": "Complete sign-in",
        "description": "Identity-provider callback. A browser-bound authorization code, state and nonce are validated before creating the opaque panel session. Navigate through sign-in; do not invoke this as a normal data API.",
        "security": [],
        "responses": {
          "302": {
            "description": "Full browser navigation to the configured destination. Never accept a caller-supplied redirect.",
            "headers": {
              "Location": {
                "schema": {
                  "type": "string",
                  "format": "uri"
                }
              },
              "Cache-Control": {
                "schema": {
                  "const": "no-store"
                }
              }
            }
          },
          "400": {
            "description": "Request rejected or dependency unavailable. Responses are never shared-cacheable.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Request rejected or dependency unavailable. Responses are never shared-cacheable.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Request rejected or dependency unavailable. Responses are never shared-cacheable.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Request rejected or dependency unavailable. Responses are never shared-cacheable.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Request rejected or dependency unavailable. Responses are never shared-cacheable.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/auth/me": {
      "get": {
        "operationId": "getAccount",
        "summary": "Read your account",
        "description": "Requires a valid panel session. Returns the minimal customer identity and session expiry without identity-provider tokens.",
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "200": {
            "description": "Authenticated customer; contains no provider tokens.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Account"
                }
              }
            }
          },
          "400": {
            "description": "Request rejected or dependency unavailable. Responses are never shared-cacheable.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Request rejected or dependency unavailable. Responses are never shared-cacheable.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Request rejected or dependency unavailable. Responses are never shared-cacheable.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Request rejected or dependency unavailable. Responses are never shared-cacheable.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Request rejected or dependency unavailable. Responses are never shared-cacheable.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/auth/account": {
      "get": {
        "operationId": "openAccountConsole",
        "summary": "Open Account & Security",
        "description": "Requires a valid panel session. Redirects to the configured Localisync-themed account console for device and session management.",
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "302": {
            "description": "Full browser navigation to the configured destination. Never accept a caller-supplied redirect.",
            "headers": {
              "Location": {
                "schema": {
                  "type": "string",
                  "format": "uri"
                }
              },
              "Cache-Control": {
                "schema": {
                  "const": "no-store"
                }
              }
            }
          },
          "400": {
            "description": "Request rejected or dependency unavailable. Responses are never shared-cacheable.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Request rejected or dependency unavailable. Responses are never shared-cacheable.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Request rejected or dependency unavailable. Responses are never shared-cacheable.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Request rejected or dependency unavailable. Responses are never shared-cacheable.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Request rejected or dependency unavailable. Responses are never shared-cacheable.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/auth/logout": {
      "post": {
        "operationId": "signOut",
        "summary": "Sign out",
        "description": "Requires a valid session and matching Origin/form CSRF. Revokes the local session first, then attempts provider sign-out. A partial remote result redirects with status=partial; local revocation is preserved.",
        "security": [
          {
            "session": []
          }
        ],
        "parameters": [
          {
            "name": "Origin",
            "in": "header",
            "required": true,
            "description": "Must equal configured PANEL_ORIGIN; cross-site Fetch Metadata is rejected.",
            "schema": {
              "type": "string",
              "format": "uri"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/x-www-form-urlencoded": {
              "schema": {
                "type": "object",
                "required": [
                  "csrf"
                ],
                "properties": {
                  "csrf": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 128
                  }
                },
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "303": {
            "description": "Full browser navigation to the configured destination. Never accept a caller-supplied redirect.",
            "headers": {
              "Location": {
                "schema": {
                  "type": "string",
                  "format": "uri"
                }
              },
              "Cache-Control": {
                "schema": {
                  "const": "no-store"
                }
              }
            }
          },
          "400": {
            "description": "Request rejected or dependency unavailable. Responses are never shared-cacheable.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Request rejected or dependency unavailable. Responses are never shared-cacheable.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Request rejected or dependency unavailable. Responses are never shared-cacheable.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Request rejected or dependency unavailable. Responses are never shared-cacheable.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Request rejected or dependency unavailable. Responses are never shared-cacheable.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/auth/backchannel-logout": {
      "post": {
        "operationId": "backchannelLogout",
        "summary": "Receive provider logout",
        "description": "Identity-provider notification endpoint. Validates the signed logout_token, issuer, audience, session ID, event, age and jti. No panel cookie is required; an unsigned request is not authorized. Valid repeated notifications are idempotent.",
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/x-www-form-urlencoded": {
              "schema": {
                "type": "object",
                "required": [
                  "logout_token"
                ],
                "properties": {
                  "logout_token": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 16384
                  }
                },
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "204": {
            "description": "Notice accepted, including a valid retry."
          },
          "400": {
            "description": "Request rejected or dependency unavailable. Responses are never shared-cacheable.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Request rejected or dependency unavailable. Responses are never shared-cacheable.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Request rejected or dependency unavailable. Responses are never shared-cacheable.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Request rejected or dependency unavailable. Responses are never shared-cacheable.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Request rejected or dependency unavailable. Responses are never shared-cacheable.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "Error": {
        "type": "object",
        "required": [
          "error",
          "message"
        ],
        "properties": {
          "error": {
            "type": "string"
          },
          "message": {
            "type": "string"
          },
          "requestId": {
            "type": "string"
          }
        },
        "additionalProperties": false
      },
      "Account": {
        "type": "object",
        "required": [
          "user",
          "expiresAt"
        ],
        "additionalProperties": false,
        "properties": {
          "user": {
            "type": "object",
            "required": [
              "id",
              "email"
            ],
            "additionalProperties": false,
            "properties": {
              "id": {
                "type": "string",
                "format": "uuid"
              },
              "email": {
                "type": "string",
                "format": "email"
              }
            }
          },
          "expiresAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      }
    },
    "securitySchemes": {
      "session": {
        "type": "apiKey",
        "in": "cookie",
        "name": "__Host-localisync-session"
      }
    }
  }
}
