Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.shipfastai.dev/llms.txt

Use this file to discover all available pages before exploring further.

Shipfastai includes a complete authentication system out of the box. You get JWT-based login with short-lived access tokens and long-lived refresh tokens, OAuth sign-in with Google and GitHub, email verification on registration, and a two-step password reset flow. All auth endpoints live under the /api/auth prefix.
Access tokens expire after 30 minutes by default. Your client must use the refresh token endpoint to obtain a new access token before the current one expires, or prompt the user to log in again.

Registering a user

Send a POST request to /api/auth/register with the user’s email, password, and full name. On success, the server creates the account, sends a verification email, and returns the new user object. The user is not yet verified at this point.
Request
POST /api/auth/register
Content-Type: application/json

{
  "email": "ada@example.com",
  "password": "hunter2secret",
  "full_name": "Ada Lovelace"
}
Response — 200 OK
{
  "id": "a1b2c3d4-0000-0000-0000-000000000001",
  "email": "ada@example.com",
  "full_name": "Ada Lovelace",
  "is_verified": false,
  "is_active": true,
  "created_at": "2026-04-09T10:00:00Z"
}

Logging in

Send a POST request to /api/auth/login with the user’s credentials. A successful response contains an access_token, a refresh_token, and the user object.
Request
POST /api/auth/login
Content-Type: application/json

{
  "email": "ada@example.com",
  "password": "hunter2secret"
}
Response — 200 OK
{
  "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "user": {
    "id": "a1b2c3d4-0000-0000-0000-000000000001",
    "email": "ada@example.com",
    "full_name": "Ada Lovelace",
    "is_verified": false,
    "is_active": true
  }
}
Pass the access_token in the Authorization: Bearer <token> header on every subsequent request that requires authentication.

Refreshing tokens

Access tokens are short-lived. When one expires, send the stored refresh_token to /api/auth/refresh to get a new pair of tokens without requiring the user to log in again.
Request
POST /api/auth/refresh
Content-Type: application/json

{
  "refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}
Response — 200 OK
{
  "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}
Refresh tokens are valid for 7 days. After expiry, the user must log in again.

OAuth login

To start an OAuth flow, direct the user’s browser to GET /api/auth/oauth/{provider} where {provider} is either google or github. The endpoint returns an authorization URL and a state token.
Response — 200 OK
{
  "authorization_url": "https://accounts.google.com/o/oauth2/auth?client_id=...",
  "state": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}
Redirect the user to authorization_url. After the user grants access, the provider sends them back to GET /api/auth/callback/{provider}?code=...&state=.... The backend exchanges the code for the user’s profile, creates or links the account, then issues tokens and redirects to:
{FRONTEND_URL}/auth/callback?access_token=<token>&refresh_token=<token>
OAuth users are automatically marked as verified. Accounts are matched by provider ID first, then by email address, so an existing email/password account is linked automatically if the OAuth email matches.

Email verification

After registering, the user receives an email containing a verification link. That link includes a short-lived token (valid for 24 hours). Submit that token to verify the account.
Request
POST /api/auth/verify-email
Content-Type: application/json

{
  "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}
Response — 200 OK
{
  "message": "Email verified successfully"
}
If the verification email expired or was lost, you can trigger a resend:
Request
POST /api/auth/resend-verification
Content-Type: application/json

{
  "email": "ada@example.com"
}
The endpoint always returns 200 regardless of whether the email is registered, to prevent user enumeration.

Password reset

Password reset is a two-step process. Step 1 — request a reset link
Request
POST /api/auth/forgot-password
Content-Type: application/json

{
  "email": "ada@example.com"
}
Response — 200 OK
{
  "message": "If your email is registered, a password reset link has been sent"
}
The user receives an email with a reset link containing a token that is valid for 1 hour. Step 2 — submit the new password
Request
POST /api/auth/reset-password
Content-Type: application/json

{
  "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "new_password": "newstrongerpassword"
}
Response — 200 OK
{
  "message": "Password reset successfully"
}
You can optionally validate the token before showing the form by calling POST /api/auth/verify-reset-token with { "token": "..." }. It returns { "valid": true } if the token is still usable.