Skip to main content
The Aura Farm API uses Supabase for identity management. Supabase issues standard JWT bearer tokens that you include on every protected request.

Sign in and obtain a token

Send a POST request to /api/auth/login with your email and password. The response includes an accessToken and a refreshToken.
email
string
required
Your @calpoly.edu email address.
password
string
required
Your account password.
curl -X POST http://localhost:3000/api/auth/login \
  -H "Content-Type: application/json" \
  -d '{"email": "[email protected]", "password": "SecurePass123"}'
Response (HTTP 200):
{
  "success": true,
  "data": {
    "accessToken": "<jwt>",
    "refreshToken": "<refresh-jwt>",
    "user": {
      "id": 42,
      "email": "[email protected]",
      "name": "musty_mustang",
      "auraPoints": 320,
      "streak": 5
    }
  }
}
Registration is restricted to @calpoly.edu email addresses. Attempts to sign up with any other domain are rejected with HTTP 400.

Use the token

Include the accessToken in the Authorization header as a Bearer token on every protected request.
curl http://localhost:3000/api/users/me \
  -H "Authorization: Bearer <accessToken>"
The server extracts the token, verifies it with Supabase, then looks up the matching user record. If the token is missing or invalid, the request is rejected with HTTP 401.

Token refresh

Access tokens expire. When a request returns HTTP 401, use the refreshToken to obtain a new session, then retry the original request. The frontend authedFetch helper in lib/api.ts does this automatically:
  1. Attach the current accessToken and make the request.
  2. If the response is 401, call refreshSession() (which calls Supabase with the stored refreshToken).
  3. If a new accessToken is returned, retry the original request once with the new token.
  4. If the refresh also fails, return the 401 response to the caller.
import { refreshSession } from "@/lib/auth";

async function authedRequest(url: string, options: RequestInit = {}) {
  let res = await fetch(url, {
    ...options,
    headers: {
      Authorization: `Bearer ${accessToken}`,
      ...(options.headers as Record<string, string> ?? {}),
    },
  });

  if (res.status === 401) {
    const refreshed = await refreshSession();
    if (refreshed?.accessToken) {
      res = await fetch(url, {
        ...options,
        headers: {
          Authorization: `Bearer ${refreshed.accessToken}`,
          ...(options.headers as Record<string, string> ?? {}),
        },
      });
    }
  }

  return res;
}
The frontend also checks token expiry proactively using the exp claim in the JWT payload (with a 30-second buffer) before making a request, so most refreshes happen silently before a 401 is ever received.

Protected endpoints

The following endpoints require a valid Authorization: Bearer <accessToken> header.
MethodEndpointNotes
POST/api/completionsSubmit a challenge completion
PATCH/api/completions/:idUpdate your own completion
POST/api/completions/:id/likeLike a completion
DELETE/api/completions/:id/likeUnlike a completion
GET/api/users/meGet your own profile
PATCH/api/users/meUpdate your profile
POST/api/challengesCreate a challenge (admin only)
POST/api/flagsFlag a completion
GET/api/flagsList all flags (admin only)
POST/api/uploadUpload an image
POST/api/auth/change-passwordChange your password
Admin-only endpoints additionally require the authenticated user to have role = "admin". Requests from non-admin users are rejected with HTTP 403.

Error responses

StatusErrorCause
401No token providedAuthorization header is missing or does not start with Bearer
401Invalid or expired tokenToken failed Supabase verification
401Authentication failedUnexpected error during token verification
403Admin access requiredEndpoint requires admin role
Example 401 response:
{
  "success": false,
  "error": "No token provided"
}

Registration flow

New accounts require email verification before the first login.
1

Sign up

POST /api/auth/signup with email, password, and username. A verification code is sent to the provided email.
2

Verify email

POST /api/auth/verify with email and the 6-digit token from the email. On success the response includes accessToken and refreshToken.
3

Sign in

Use POST /api/auth/login for all subsequent sessions.
If the verification email does not arrive, call POST /api/auth/resend with { "email": "..." } to send a new code.
The auth endpoints (/api/auth/*) are rate-limited to 5 requests per 15 minutes per IP to prevent brute-force attacks.

Build docs developers (and LLMs) love