Skip to content

Auth API

Login via Google OAuth 2.0 plus JWT issuance and refresh. See API Conventions for shared rules.

Base path: /api/v1/auth

Method Path Summary Auth
GET /google Start Google OAuth Not required
GET /google/callback Google OAuth callback Not required
POST /refresh Refresh the access token Not required (authenticated by the refresh token)
POST /logout Log out Required
GET /me Get the current user Required

Start Google OAuth

URI

GET /api/v1/auth/google

Request

No parameters.

Response (302 Found)

Redirects to Google's consent screen. No body.

Note

Swagger UI cannot follow the redirect โ€” open this URL directly in a new tab instead.


Google OAuth Callback

Receives Google's redirect, exchanges the authorization code for tokens, creates or updates the user, and redirects back to the frontend.

URI

GET /api/v1/auth/google/callback

Query Parameters

Field Required Rules
code โœ“ OAuth authorization code, at least 1 character
state - OAuth state parameter

Response (302 Found)

Case Redirect target
Success {FRONTEND_URL}/login?token={accessToken}
Failure {FRONTEND_URL}/login?error=auth_failed

Failures also return 302, and internal error details never appear in the URL (they are logged only).

Errors

Description Status code Status name
code missing or malformed 400 Bad Request

Flow

sequenceDiagram
    participant G as Google
    participant API as Backend API
    participant DB as PostgreSQL
    participant FE as Frontend

    G->>API: GET /google/callback?code=...
    API->>G: Exchange code for an access token
    G-->>API: Profile (google_id / email / name / picture)
    API->>DB: Look up the user by google_id
    alt Not registered
        API->>DB: Create users row (role = member)
    else Registered
        API->>DB: Update the profile
    end
    API->>API: Sign a JWT (HS256)
    API-->>FE: 302 {FRONTEND_URL}/login?token=...

Refresh Access Token

URI

POST /api/v1/auth/refresh

Request Body

The request body is JSON.

{
  "refreshToken": "refresh_token_here"
}

Validation Rules

Field Rules
refreshToken Required, at least 1 character

Response (200 OK)

{
  "success": true,
  "status": "success",
  "statusCode": 200,
  "timestamp": "2026-01-15T10:00:00.000Z",
  "data": {
    "accessToken": "eyJhbGciOiJIUzI1NiIs...",
    "refreshToken": "refresh_token_here",
    "expiresIn": 3600
  },
  "path": "/api/v1/auth/refresh",
  "method": "POST"
}

Errors

Description Status code Status name
Malformed body 400 Bad Request
Refresh token invalid or expired 401 Unauthorized

Log Out

URI

POST /api/v1/auth/logout

Authentication

Requires Authorization: Bearer <access_token>.

Response (200 OK)

{
  "success": true,
  "status": "success",
  "statusCode": 200,
  "data": { "message": "Logged out successfully" },
  "path": "/api/v1/auth/logout",
  "method": "POST"
}

Errors

Description Status code Status name
Token missing or invalid 401 Unauthorized

Tokens are not invalidated server-side

With stateless JWTs, logout means discarding the token on the client. This endpoint does not blacklist anything, so already-issued tokens remain valid until they expire.


Get Current User

URI

GET /api/v1/auth/me

Authentication

Requires Authorization: Bearer <access_token>.

Response (200 OK)

{
  "success": true,
  "status": "success",
  "statusCode": 200,
  "data": {
    "id": 1,
    "email": "user@example.com",
    "name": "John Doe",
    "picture": "https://example.com/avatar.jpg",
    "role": "member",
    "createdAt": "2026-01-15T09:00:00Z"
  },
  "path": "/api/v1/auth/me",
  "method": "GET"
}
Field Type Description
id integer User ID
email string Email address
name string Display name
picture string | null Profile image URL (avatar_url in the DB)
role enum admin / member / viewer
createdAt string(date-time) Registration timestamp

Errors

Description Status code Status name
Token missing or invalid 401 Unauthorized