Skip to content

Authentication

The Coaching App uses Better-auth for all authentication. Sessions are cookie-based — a better-auth.session_token cookie is set on sign-in and must be included in subsequent requests.

All auth routes are handled by Better-auth under /api/auth/*.


Endpoints

Method Path Auth Description
POST /api/auth/sign-up/email Public Create a new user account
POST /api/auth/sign-in/email Public Sign in and start a session
POST /api/auth/sign-out Required End the current session
GET /api/auth/session Optional Get the current session
POST /api/auth/forget-password Public Send a password reset email
POST /api/auth/reset-password Public Reset password using a token
POST /api/auth/resend-activation Public Resend account activation link with cooldown

POST /api/auth/sign-up/email

Create a new user account with email and password. Newly created accounts have no organization role until they accept an invite or are manually assigned.

Request body:

{
  "email": "user@example.com",
  "password": "Secret1234!",
  "name": "Jane Smith",
  "preferredLanguage": "es"
}
Field Type Required Constraints
email string Yes Must be a valid email address
password string Yes Minimum 8 characters
name string Yes Full display name
preferredLanguage string No en or es. If omitted, backend uses Accept-Language header or defaults to en

Language persistence on signup

During signup, the backend persists the resolved language in profiles.preferred_language so future transactional emails (password reset, activation resend, invites) can be localized.

Response 200:

{
  "user": {
    "id": "abc123",
    "email": "user@example.com",
    "name": "Jane Smith",
    "createdAt": "2025-01-01T00:00:00.000Z"
  }
}

Error responses:

Code Condition
400 Missing fields or password too short
409 Email already registered

POST /api/auth/sign-in/email

Sign in with email and password. Sets a better-auth.session_token cookie on the response.

Request body:

{
  "email": "user@example.com",
  "password": "Secret1234!"
}
Field Type Required
email string Yes
password string Yes

Response 200:

{
  "token": "...",
  "user": {
    "id": "abc123",
    "email": "user@example.com",
    "name": "Jane Smith"
  }
}

Error responses:

Code Condition
401 Invalid email or password

POST /api/auth/sign-out

End the current session. Clears the session cookie.

Request: No body required. Session cookie must be present.

Response 200:

{}

GET /api/auth/session

Returns information about the currently active session. Returns null session if not authenticated.

Response 200 (authenticated):

{
  "session": {
    "id": "...",
    "userId": "abc123",
    "expiresAt": "2025-02-01T00:00:00.000Z"
  },
  "user": {
    "id": "abc123",
    "email": "user@example.com",
    "name": "Jane Smith"
  }
}

Response 200 (not authenticated):

{
  "session": null,
  "user": null
}

POST /api/auth/forget-password

Send a password reset email to the specified address. The email contains a time-limited reset link.

Request body:

{
  "email": "user@example.com",
  "redirectTo": "https://yourapp.com/reset-password"
}
Field Type Required Description
email string Yes Email address to send the reset link to
redirectTo string No URL the reset link will redirect to after clicking

Response 200:

{}

Always returns 200

This endpoint always returns 200 regardless of whether the email exists in the system, to prevent email enumeration.

Email localization

Password reset emails are localized using this fallback chain: callbackURL language (if present) -> profile preferred_language -> en.


POST /api/auth/reset-password

Reset a user's password using the token from a reset email.

Request body:

{
  "token": "<reset-token-from-email>",
  "newPassword": "NewSecret1234!"
}
Field Type Required Constraints
token string Yes Token from the password reset email
newPassword string Yes Minimum 8 characters

Response 200:

{}

Error responses:

Code Condition
400 Invalid or expired token, or password too short

POST /api/auth/resend-activation

Resend an account activation email for the provided address.

This endpoint is designed for the post-signup "I didn't receive the email" flow and includes a per-email cooldown.

Cooldown is configured with RESEND_ACTIVATION_COOLDOWN_SECONDS on the backend. If unset or invalid, it falls back to the recommended default of 60 seconds.

Request body:

{
  "email": "user@example.com",
  "callbackURL": "https://app.example.com/email-verification"
}
Field Type Required Description
email string Yes Account email address
callbackURL string No URL to redirect to after verification

Response 200:

{
  "success": true,
  "retryAfterSeconds": 60
}

Error responses:

Code Condition
400 Invalid email format
429 Cooldown active for this email (retryAfterSeconds included)

Enumeration-safe behavior

For validly formatted emails, the endpoint does not reveal whether the account exists. It attempts to dispatch a verification email through Better-auth and returns success metadata unless the cooldown is active.

Verification callback behavior

The verification link ultimately redirects to /email-verification. On mobile, if the app is installed and universal links are configured, this route opens in-app and shows a success/failure state (error query param) before the user continues to login.

Activation email localization

Activation emails are localized using callback URL language first (for example /email-verification?lang=es), then profile preferred_language, then en.


POST /api/users/me/password

Change the current user's own password. Requires knowing the current password.

Authentication: Required.

Request body:

{
  "currentPassword": "OldSecret1234!",
  "newPassword": "NewSecret5678!"
}
Field Type Required Constraints
currentPassword string Yes The user's existing password
newPassword string Yes Minimum 8 characters

Response 200:

{
  "success": true
}

Error responses:

Code Condition
400 Missing fields, new password too short, or current password incorrect