{"openapi":"3.0.3","info":{"title":"BSChat Auth API","version":"1.0.0","description":"REST endpoints for authentication - the **only** REST surface of BSChat. Every post-login operation (users, groups, messages, admin) runs as an RPC over a single Socket.IO connection; see `SOCKET_PROTOCOL.md`. The server is zero-trust: clients generate their keys locally and only ever upload public keys and ciphertext. Mobile developers should start from `API_GUIDE.md`."},"servers":[{"url":"https://chat.bsafegroup.be","description":"Production"},{"url":"http://localhost:3000","description":"Local development"}],"tags":[{"name":"Auth","description":"Registration, login, token lifecycle"}],"paths":{"/api/auth/register":{"post":{"tags":["Auth"],"summary":"Register a new user","description":"Generate an RSA-4096 key pair on the client first and submit only the public key (JWK JSON string). The private key never leaves the device. The first registered user becomes SUPERADMIN. No tokens are returned; call /api/auth/login next.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["email","password","displayName","publicKey"],"properties":{"email":{"type":"string","format":"email","example":"alice@bsafegroup.be"},"password":{"type":"string","format":"password","minLength":8,"example":"correct-horse-battery"},"displayName":{"type":"string","example":"Alice"},"publicKey":{"type":"string","description":"RSA-4096 public key as a JWK JSON string.","example":"{\"kty\":\"RSA\",\"n\":\"…\",\"e\":\"AQAB\"}"}}}}}},"responses":{"201":{"description":"User created.","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","example":"cmc0d1xyz0000abcd1234efgh"},"email":{"type":"string","example":"alice@bsafegroup.be"},"displayName":{"type":"string","example":"Alice"},"role":{"$ref":"#/components/schemas/UserRole"}}}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"Email already registered (EMAIL_TAKEN).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/auth/login":{"post":{"tags":["Auth"],"summary":"Log in and obtain a token pair","description":"Verifies credentials and returns a short-lived access token plus a single-use refresh token, along with the stored public key. Use the access token as `Authorization: Bearer <token>` on REST calls and as `auth.token` in the Socket.IO handshake.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["email","password"],"properties":{"email":{"type":"string","format":"email","example":"alice@bsafegroup.be"},"password":{"type":"string","format":"password","example":"correct-horse-battery"},"deviceInfo":{"type":"string","description":"Optional device label.","example":"Pixel 8 / BSChat Android 1.0"}}}}}},"responses":{"200":{"description":"Authenticated.","content":{"application/json":{"schema":{"type":"object","properties":{"accessToken":{"type":"string","description":"JWT, ~15 min lifetime."},"refreshToken":{"type":"string","description":"Single-use JWT, ~7 day lifetime."},"user":{"$ref":"#/components/schemas/AuthUser"}}}}}},"400":{"description":"Malformed request.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Invalid credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/auth/refresh":{"post":{"tags":["Auth"],"summary":"Exchange a refresh token for a new token pair","description":"Refresh tokens are single-use: the presented token is revoked and a new pair is issued (rotation). A used/revoked/expired token yields 401.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["refreshToken"],"properties":{"refreshToken":{"type":"string"}}}}}},"responses":{"200":{"description":"New token pair.","content":{"application/json":{"schema":{"type":"object","properties":{"accessToken":{"type":"string"},"refreshToken":{"type":"string"}}}}}},"400":{"description":"Malformed request.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Refresh token invalid, expired, or already used.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/auth/logout":{"post":{"tags":["Auth"],"summary":"Log out (revoke a refresh token)","description":"Revokes the provided refresh token. Idempotent - always returns 200, even for unknown tokens. Clients should also discard the access token and wipe local key material.","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"refreshToken":{"type":"string","description":"If provided, this refresh token is revoked."}}}}}},"responses":{"200":{"description":"Logged out.","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string","example":"Logged out"}}}}}}}}},"/api/auth/me":{"get":{"tags":["Auth"],"summary":"Get the authenticated user's profile","description":"Example protected endpoint - demonstrates Bearer auth for REST. After login, all application operations run over the Socket.IO RPC channel; REST stays auth-only.","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The authenticated user.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthUser"}}}},"401":{"description":"Missing or invalid access token.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}}},"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Access token from /api/auth/login. Also used as `auth.token` in the Socket.IO handshake."}},"schemas":{"UserRole":{"type":"string","enum":["SUPERADMIN","ADMIN","USER"]},"AuthUser":{"type":"object","properties":{"id":{"type":"string","example":"cmc0d1xyz0000abcd1234efgh"},"email":{"type":"string","format":"email","example":"alice@bsafegroup.be"},"displayName":{"type":"string","example":"Alice"},"role":{"$ref":"#/components/schemas/UserRole"},"publicKey":{"type":"string","nullable":true,"description":"RSA-4096 public key (JWK JSON string) for E2EE."},"themePreference":{"type":"string","enum":["light","dark","system","auto"],"description":"UI theme preference, synced across the user’s devices."}}},"ErrorResponse":{"type":"object","properties":{"error":{"type":"string","description":"Machine-readable error code.","example":"INVALID_CREDENTIALS"},"message":{"type":"string","description":"Human-readable explanation (English).","example":"Invalid email or password"}}}}}}