Skip to main content

Overview

The login endpoint authenticates users using Supabase Auth. JCV Fitness supports two authentication methods:
  1. Password Authentication - Traditional email and password login
  2. Magic Link - Passwordless authentication via email link

Endpoint

POST /api/auth/login

Authentication

This endpoint does not require authentication.

Password Login

Request Body

email
string
required
User’s registered email address.
password
string
required
User’s password.
method
string
default:"password"
Authentication method. Use "password" for this flow.

Response

session
object
The authenticated session object.
user
object
The authenticated user object.

Request Example

cURL
curl -X POST https://jcv24fitness.com/api/auth/login \
  -H "Content-Type: application/json" \
  -d '{
    "email": "[email protected]",
    "password": "securePassword123",
    "method": "password"
  }'
JavaScript
const response = await fetch('https://jcv24fitness.com/api/auth/login', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    email: '[email protected]',
    password: 'securePassword123'
  })
});

const { session, user } = await response.json();

Response Example

{
  "session": {
    "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
    "refresh_token": "v1.MRjjBYLeWyDxNLsZzQwZGQ...",
    "expires_in": 3600,
    "expires_at": 1709305496,
    "token_type": "bearer"
  },
  "user": {
    "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "email": "[email protected]",
    "confirmed_at": "2026-03-01T12:34:56.789Z",
    "last_sign_in_at": "2026-03-01T14:20:15.123Z",
    "role": "authenticated"
  }
}

Request Body

email
string
required
User’s registered email address.
method
string
required
Authentication method. Use "magic_link" for this flow.

Response

success
boolean
Always true when magic link is sent successfully.
message
string
Confirmation message indicating that the magic link was sent.

Request Example

cURL
curl -X POST https://jcv24fitness.com/api/auth/login \
  -H "Content-Type: application/json" \
  -d '{
    "email": "[email protected]",
    "method": "magic_link"
  }'
JavaScript
const response = await fetch('https://jcv24fitness.com/api/auth/login', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    email: '[email protected]',
    method: 'magic_link'
  })
});

const data = await response.json();

Response Example

{
  "success": true,
  "message": "Magic link sent to [email protected]. Please check your email."
}

Error Codes

CodeDescription
INVALID_CREDENTIALSEmail or password is incorrect
EMAIL_NOT_CONFIRMEDUser has not confirmed their email address
USER_NOT_FOUNDNo account exists with this email
RATE_LIMIT_EXCEEDEDToo many login attempts or magic link requests
SUPABASE_ERRORInternal error from Supabase Auth service

Implementation Details

Password Authentication Flow

  1. Client submits email and password
  2. Supabase Auth verifies credentials against auth.users table
  3. If valid, returns JWT access token and refresh token
  4. Client stores tokens (typically in httpOnly cookies or secure storage)
  5. Client includes access token in Authorization header for protected API requests
  6. When access token expires, client uses refresh token to get a new one
  1. Client submits email address
  2. Supabase generates one-time use token
  3. Supabase sends email with magic link containing token
  4. User clicks link in email
  5. Link redirects to callback URL: https://jcv24fitness.com/auth/callback?token=...&type=magiclink
  6. Callback page exchanges token for session
  7. User is authenticated and redirected to dashboard

Session Management

Supabase automatically manages session state:
  • Access tokens expire after 1 hour (3600 seconds)
  • Refresh tokens are valid for 30 days
  • Sessions are automatically refreshed when accessing protected routes
  • Session state is synchronized across browser tabs

Frontend Integration

Password Login

import { useAuth } from '@/features/auth';

function LoginForm() {
  const { signIn } = useAuth();
  
  const handlePasswordLogin = async (e: React.FormEvent) => {
    e.preventDefault();
    
    const { error } = await signIn(email, password);
    
    if (error) {
      setError(error.message);
      return;
    }
    
    // User is now authenticated
    router.push('/dashboard');
  };
}
import { useAuth } from '@/features/auth';

function LoginForm() {
  const { signInWithMagicLink } = useAuth();
  
  const handleMagicLink = async (e: React.FormEvent) => {
    e.preventDefault();
    
    const { error } = await signInWithMagicLink(email);
    
    if (error) {
      setError(error.message);
      return;
    }
    
    // Show success message - check email
    setMagicLinkSent(true);
  };
}

Protected Routes

Use the ProtectedRoute component to guard routes that require authentication:
import { ProtectedRoute } from '@/features/auth';

export default function DashboardPage() {
  return (
    <ProtectedRoute>
      <Dashboard />
    </ProtectedRoute>
  );
}
The useAuth hook provides isAuthenticated and isLoading states to conditionally render UI based on auth status.

Security Considerations

Always use HTTPS in production to protect credentials in transit.
  • Passwords are hashed using bcrypt before storage (handled by Supabase)
  • Access tokens are short-lived (1 hour) to minimize risk if compromised
  • Rate limiting prevents brute force attacks
  • Magic links are single-use and expire after 1 hour
  • Row Level Security (RLS) ensures users can only access their own data

Build docs developers (and LLMs) love