Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/ihfaz297/MND/llms.txt

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

MND uses a passwordless magic link authentication system, providing a secure and user-friendly way to authenticate without requiring password management.

Overview

The authentication flow consists of two main steps:
  1. Send magic link: User provides their email, system generates a temporary token
  2. Verify token: User clicks the link, system validates the token and creates a session
Magic links expire after 15 minutes for security. Users must verify their email within this timeframe.

Authentication Flow

1

User requests magic link

The user submits their email address to receive a login link:
curl -X POST \
  -H "Content-Type: application/json" \
  -d '{"email": "student@sust.edu"}' \
  http://localhost:3000/api/auth/send-link
Response:
{
  "success": true,
  "message": "Magic link sent to your email",
  "_dev": {
    "note": "In production, this token would be sent via email only",
    "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
    "verifyUrl": "http://localhost:3000/api/auth/verify?token=eyJhbGciOiJI..."
  }
}
The _dev field containing the token is only included in development mode for testing. In production, the token is sent exclusively via email.
2

System generates token

The backend creates a JWT token containing:
  • Email address
  • Token type (“magic_link”)
  • Expiration timestamp (15 minutes from creation)
The token is logged to the console and would be sent via email in production:
📧 Magic Link Generated
   Email: student@sust.edu
   Link: http://localhost:3000/api/auth/verify?token=...
   Token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
   Expires in: 15 minutes
3

User verifies the token

When the user clicks the magic link (or makes a verification request):
curl "http://localhost:3000/api/auth/verify?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
Successful verification returns:
{
  "success": true,
  "message": "Login successful",
  "authToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiI3Y2QzOWE...",
  "user": {
    "id": "7cd39a42-9f8e-4c7d-a8f0-3e9b8c7d1234",
    "email": "student@sust.edu",
    "createdAt": "2026-03-05T08:00:00.000Z",
    "lastLogin": "2026-03-05T08:30:00.000Z"
  }
}
4

Store the auth token

Save the authToken from the response for use in subsequent API requests:
// Store in localStorage (web)
localStorage.setItem('authToken', response.authToken);

// Or secure storage (mobile)
await SecureStore.setItemAsync('authToken', response.authToken);

Making Authenticated Requests

Once authenticated, include the auth token in the Authorization header for protected endpoints:
curl -H "Authorization: Bearer YOUR_AUTH_TOKEN" \
  http://localhost:3000/api/favorites

JavaScript Example

const authToken = localStorage.getItem('authToken');

const response = await fetch('http://localhost:3000/api/favorites', {
  headers: {
    'Authorization': `Bearer ${authToken}`,
    'Content-Type': 'application/json'
  }
});

if (response.status === 401) {
  // Token expired or invalid - redirect to login
  window.location.href = '/login';
}

Flutter/Dart Example

import 'package:http/http.dart' as http;
import 'package:flutter_secure_storage/flutter_secure_storage.dart';

final storage = FlutterSecureStorage();
final authToken = await storage.read(key: 'authToken');

final response = await http.get(
  Uri.parse('http://localhost:3000/api/favorites'),
  headers: {
    'Authorization': 'Bearer $authToken',
    'Content-Type': 'application/json',
  },
);

if (response.statusCode == 401) {
  // Token expired - redirect to login
  Navigator.pushReplacementNamed(context, '/login');
}

Protected Endpoints

The following endpoints require authentication:
EndpointMethodDescription
/api/favoritesGETList user’s saved favorites
/api/favoritesPOSTCreate a new favorite
/api/favorites/:idPUTUpdate a favorite
/api/favorites/:idDELETEDelete a favorite
/api/profileGETGet user profile

Getting User Profile

Retrieve the authenticated user’s profile information:
curl -H "Authorization: Bearer YOUR_AUTH_TOKEN" \
  http://localhost:3000/api/profile
{
  "user": {
    "id": "7cd39a42-9f8e-4c7d-a8f0-3e9b8c7d1234",
    "email": "student@sust.edu",
    "createdAt": "2026-03-01T10:00:00.000Z",
    "lastLogin": "2026-03-05T08:30:00.000Z"
  }
}

Logging Out

Invalidate the current auth token:
curl -X POST \
  -H "Authorization: Bearer YOUR_AUTH_TOKEN" \
  http://localhost:3000/api/auth/logout
Response:
{
  "success": true,
  "message": "Logged out successfully"
}
After logging out, remove the token from local storage to prevent unauthorized access attempts.
// Clear token from storage
localStorage.removeItem('authToken');

// Redirect to login page
window.location.href = '/login';

Error Handling

Invalid Email Format

When sending a magic link with an invalid email:
{
  "error": "Invalid email",
  "message": "Please provide a valid email address"
}
HTTP Status: 400 Bad Request

Missing Email

If the email field is not provided:
{
  "error": "Email is required",
  "message": "Please provide a valid email address"
}
HTTP Status: 400 Bad Request

Expired or Invalid Token

When verifying an expired or invalid magic link token:
{
  "error": "Verification failed",
  "message": "Invalid or expired token"
}
HTTP Status: 401 Unauthorized

Missing Authorization Header

Attempting to access a protected endpoint without an auth token:
{
  "error": "Unauthorized",
  "message": "Please provide a valid auth token"
}
HTTP Status: 401 Unauthorized

Invalid Auth Token

Using an expired or malformed auth token:
{
  "error": "Unauthorized",
  "message": "Invalid or expired token"
}
HTTP Status: 401 Unauthorized

Authentication Middleware

The backend provides two middleware functions for route protection:

requireAuth (Mandatory Authentication)

Use this middleware for endpoints that require authentication:
// src/api/authController.ts:200
router.get('/api/favorites', requireAuth, getFavorites);
Rejects requests without valid auth tokens with a 401 response.

optionalAuth (Optional Authentication)

Use this for endpoints that work with or without authentication:
// src/api/authController.ts:182
router.get('/api/routes', optionalAuth, planRoute);
If a valid token is provided, the user information is attached to the request. Otherwise, the request continues without user context.

Security Considerations

Web Applications:
  • Store tokens in localStorage or sessionStorage
  • Never store tokens in cookies without HttpOnly flag
  • Clear tokens on logout
Mobile Applications:
  • Use platform-specific secure storage (Keychain on iOS, Keystore on Android)
  • Flutter: Use flutter_secure_storage package
  • React Native: Use react-native-keychain or expo-secure-store
  • Auth tokens remain valid until logout
  • Implement token refresh mechanism for long-running sessions
  • Monitor for 401 responses and redirect to login when detected
  • Store user ID alongside token for quick user identification

Email Integration (Production)

In production environments, integrate an email service to send magic links:

Using SendGrid

import sgMail from '@sendgrid/mail';

sgMail.setApiKey(process.env.SENDGRID_API_KEY);

const msg = {
  to: email,
  from: 'noreply@mnd.sust.edu',
  subject: 'Your MND Login Link',
  html: `
    <h1>Login to MND</h1>
    <p>Click the link below to log in to your account:</p>
    <a href="${magicLinkUrl}">Log In</a>
    <p>This link expires in 15 minutes.</p>
  `
};

await sgMail.send(msg);

Using Nodemailer

import nodemailer from 'nodemailer';

const transporter = nodemailer.createTransport({
  service: 'gmail',
  auth: {
    user: process.env.EMAIL_USER,
    pass: process.env.EMAIL_PASSWORD
  }
});

await transporter.sendMail({
  from: 'noreply@mnd.sust.edu',
  to: email,
  subject: 'Your MND Login Link',
  html: `
    <h1>Login to MND</h1>
    <p>Click the link below to log in:</p>
    <a href="${magicLinkUrl}">Log In</a>
    <p>Expires in 15 minutes.</p>
  `
});
Remove the _dev field from the response in production to avoid exposing tokens in API responses.

User Data Storage

User accounts are stored in users.json:
{
  "users": [
    {
      "id": "7cd39a42-9f8e-4c7d-a8f0-3e9b8c7d1234",
      "email": "student@sust.edu",
      "createdAt": "2026-03-01T10:00:00.000Z",
      "lastLogin": "2026-03-05T08:30:00.000Z"
    }
  ]
}
New users are automatically created on first login.

Implementation Reference

The authentication system is implemented in:
  • Core logic: src/core/auth.ts - Token generation, validation, user management
  • API endpoints: src/api/authController.ts - HTTP handlers and middleware
    • sendMagicLink() - Generate and send magic link (line 8)
    • verifyMagicLink() - Validate token and create session (line 65)
    • getProfile() - Retrieve user profile (line 113)
    • logout() - Invalidate auth token (line 157)
    • requireAuth() - Authentication middleware (line 200)
    • optionalAuth() - Optional authentication middleware (line 182)

Complete Authentication Example

import React, { useState, useEffect } from 'react';

function AuthExample() {
  const [email, setEmail] = useState('');
  const [user, setUser] = useState(null);
  const [token, setToken] = useState(localStorage.getItem('authToken'));

  // Send magic link
  async function sendMagicLink() {
    const response = await fetch('http://localhost:3000/api/auth/send-link', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ email })
    });
    
    const data = await response.json();
    
    if (data.success) {
      alert('Check your email for the login link!');
      
      // In dev mode, auto-verify for testing
      if (data._dev) {
        await verifyToken(data._dev.token);
      }
    }
  }

  // Verify magic link token
  async function verifyToken(token) {
    const response = await fetch(
      `http://localhost:3000/api/auth/verify?token=${token}`
    );
    
    const data = await response.json();
    
    if (data.success) {
      localStorage.setItem('authToken', data.authToken);
      setToken(data.authToken);
      setUser(data.user);
    }
  }

  // Fetch user profile
  async function fetchProfile() {
    const response = await fetch('http://localhost:3000/api/profile', {
      headers: { 'Authorization': `Bearer ${token}` }
    });
    
    if (response.ok) {
      const data = await response.json();
      setUser(data.user);
    } else {
      // Token invalid - clear and redirect to login
      localStorage.removeItem('authToken');
      setToken(null);
    }
  }

  // Logout
  async function logout() {
    await fetch('http://localhost:3000/api/auth/logout', {
      method: 'POST',
      headers: { 'Authorization': `Bearer ${token}` }
    });
    
    localStorage.removeItem('authToken');
    setToken(null);
    setUser(null);
  }

  // Load user on mount if token exists
  useEffect(() => {
    if (token) {
      fetchProfile();
    }
  }, [token]);

  // Render login form or user info
  if (!user) {
    return (
      <div>
        <h1>Login</h1>
        <input
          type="email"
          placeholder="Email"
          value={email}
          onChange={(e) => setEmail(e.target.value)}
        />
        <button onClick={sendMagicLink}>Send Magic Link</button>
      </div>
    );
  }

  return (
    <div>
      <h1>Welcome, {user.email}</h1>
      <p>User ID: {user.id}</p>
      <p>Last login: {new Date(user.lastLogin).toLocaleString()}</p>
      <button onClick={logout}>Logout</button>
    </div>
  );
}

Build docs developers (and LLMs) love