Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/4rt21/backend-proyecto/llms.txt

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

Authentication

FalconAlert uses JWT (JSON Web Tokens) for stateless authentication. This guide covers the complete authentication flow, token management, and best practices.

Authentication Flow

The API implements a dual-token authentication system:

Token Types

FalconAlert uses two types of JWT tokens:

Access Token

  • Purpose: Authenticate API requests
  • Lifetime: 1 hour
  • Payload: Contains user profile information
  • Usage: Include in Authorization header for protected endpoints
Payload structure:
{
  sub: "1",              // User ID
  type: "access",
  profile: {
    id: "1",
    email: "user@example.com",
    name: "John Doe",
    role_id: 1           // 1=mobile, 2=web/admin
  },
  iat: 1709550000,       // Issued at
  exp: 1709553600        // Expires at
}

Refresh Token

  • Purpose: Obtain new access tokens
  • Lifetime: 7 days
  • Payload: Minimal user identifier
  • Usage: Send to /auth/refresh when access token expires
Payload structure:
{
  sub: "1",              // User ID
  type: "refresh",
  iat: 1709550000,       // Issued at
  exp: 1710154800        // Expires at (7 days)
}
Tokens are signed with the secret "supersecret". In production, use a strong, environment-specific secret stored in environment variables.

Login Endpoint

Authenticate a user and receive tokens.

Request

POST /auth/login
Content-Type: application/json
{
  "email": "user@example.com",
  "password": "SecurePass123",
  "type": "mobile"  // or "web"
}

Parameters

ParameterTypeRequiredDescription
emailstringYesUser’s email address (must be valid email format)
passwordstringYesUser’s password
typeenumYesLogin type: "mobile" (role_id=1) or "web" (role_id=2)

Response

{
  "accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxIiwidHlwZSI6ImFjY2VzcyIsInByb2ZpbGUiOnsiaWQiOiIxIiwiZW1haWwiOiJhcnR1cm9AZ21haWwuY29tIiwibmFtZSI6IkFydHVybyBVdHJpbGxhYSDDiURJVCIsInJvbGVfaWQiOjF9LCJpYXQiOjE3NjEyOTI0MTEsImV4cCI6MTc2MTI5NjAxMX0.ECBWVGu-tXr7Shs9qvg9LMISOXtFQmp3R8C5Uk8cUuw",
  "refreshToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxIiwidHlwZSI6InJlZnJlc2giLCJpYXQiOjE3NjEyOTI0MTEsImV4cCI6MTc2MTg5NzIxMX0.tA-fCXWmhTzE1YlOXTpftjn_qFzZJoZaCJEPBEYrYZI"
}

Role-Based Access Control

The type parameter enforces role-based access:
  • type: "mobile" → Only allows users with role_id: 1
  • type: "web" → Only allows users with role_id: 2
If a user attempts to login with the wrong type:
{
  "message": "aqui no puedes entrar tontito",
  "error": "Unauthorized",
  "statusCode": 401
}

Using Access Tokens

Include the access token in the Authorization header for all protected endpoints.

Header Format

Authorization: Bearer <access_token>

Example Request

curl http://localhost:3000/auth/profile \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

Protected Endpoints

Endpoints that require authentication are marked with the @UseGuards(JwtAuthGuard) decorator. These include:
  • /auth/profile - Get current user profile
  • /auth/verify - Verify token validity
  • /users - Get/update user information
  • /users/report - Create fraud reports
  • /users/reports - Get user’s reports
  • /users/password - Change password
  • And many more…

JWT Authentication Guard

The API validates tokens using a custom guard (src/common/guards/jwt.auth.guard.ts:1):
@Injectable()
export class JwtAuthGuard implements CanActivate {
  async canActivate(ctx: ExecutionContext): Promise<boolean> {
    const request = ctx.switchToHttp().getRequest<Request>();
    const auth = request.headers.authorization ?? '';

    const [schema, token] = auth.split(' ');

    if (schema !== 'Bearer' || !token)
      throw new UnauthorizedException('Invalid token');

    try {
      const payload = await this.tokenService.verifyAccess(token);
      (request as AuthenticatedRequest).user = {
        userId: payload.sub,
        profile: payload.profile,
        raw: payload,
      };
      return true;
    } catch (error) {
      throw new UnauthorizedException('Invalid token', error);
    }
  }
}

Refreshing Tokens

When your access token expires, use the refresh token to obtain a new pair.

Request

POST /auth/refresh
Content-Type: application/json
{
  "refreshToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}

Response

{
  "accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.NEW_TOKEN...",
  "refreshToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.NEW_REFRESH..."
}
Both tokens are rotated on refresh for enhanced security. Always store the new refresh token.

Verifying Tokens

Check if an access token is still valid without making a data request.

Request

POST /auth/verify
Authorization: Bearer <access_token>

Response

{
  "message": "Token is valid"
}

Getting User Profile

Retrieve the authenticated user’s profile information.

Request

GET /auth/profile
Authorization: Bearer <access_token>

Response

{
  "profile": {
    "profile": {
      "id": "1",
      "email": "arturo@gmail.com",
      "name": "Arturo Utrillaa",
      "role_id": 1
    }
  }
}

Token Service Implementation

The token generation and verification logic is handled by TokensService (src/auth/tokens.service.ts:1):

Generate Access Token

async generateAccessToken(profile: UserProfile): Promise<string> {
  return this.jwtService.signAsync(
    {
      sub: profile.id,
      type: 'access',
      profile: profile,
    },
    { expiresIn: '1h', secret: 'supersecret' },
  );
}

Generate Refresh Token

async generateRefreshToken(userId: string): Promise<string> {
  return this.jwtService.signAsync(
    {
      sub: userId,
      type: 'refresh',
    },
    { expiresIn: '7d', secret: 'supersecret' },
  );
}

Verify Access Token

async verifyAccess(token: string): Promise<AccessPayload> {
  const payload = await this.jwtService.verifyAsync<AccessPayload>(token, {
    secret: 'supersecret',
  });
  if (payload.type !== 'access') {
    throw new Error('Invalid token type');
  }
  return payload;
}

Password Hashing

Passwords are hashed using SHA-256 with a per-user salt (src/users/users.service.ts:40):
// Registration
const salt = Math.random().toString(36).substring(2, 15);
const hashedPassword = sha256(password + salt);

// Login verification
if (user.password !== sha256(password + user.salt)) {
  throw new UnauthorizedException('Invalid password');
}
While SHA-256 is used in this implementation, consider using bcrypt or argon2 for production applications as they’re specifically designed for password hashing.

Best Practices

  • Web apps: Use HttpOnly cookies or secure localStorage
  • Mobile apps: Use platform-specific secure storage (Keychain/KeyStore)
  • Never store tokens in plain text or in version control
async function makeAuthenticatedRequest(endpoint: string) {
  try {
    return await fetch(endpoint, {
      headers: { Authorization: `Bearer ${accessToken}` }
    });
  } catch (error) {
    if (error.status === 401) {
      // Access token expired, refresh it
      const newTokens = await refreshTokens(refreshToken);
      accessToken = newTokens.accessToken;
      refreshToken = newTokens.refreshToken;
      
      // Retry the request
      return await fetch(endpoint, {
        headers: { Authorization: `Bearer ${accessToken}` }
      });
    }
    throw error;
  }
}
Refresh tokens proactively before they expire:
// Refresh 5 minutes before expiration
const tokenExpiresIn = 3600; // 1 hour
const refreshThreshold = 300; // 5 minutes

setTimeout(async () => {
  const newTokens = await refreshTokens(refreshToken);
  updateStoredTokens(newTokens);
}, (tokenExpiresIn - refreshThreshold) * 1000);
function logout() {
  localStorage.removeItem('accessToken');
  localStorage.removeItem('refreshToken');
  // Optionally call a logout endpoint to invalidate tokens server-side
}
Always use HTTPS to prevent token interception. Tokens transmitted over HTTP can be stolen by attackers.

Common Authentication Errors

ErrorCauseSolution
Invalid tokenMalformed or missing Bearer tokenEnsure header format: Authorization: Bearer <token>
Token is valid response on /verifyToken signature is invalidRe-authenticate and get a new token
User not foundEmail doesn’t exist in databaseVerify email or register a new account
Invalid passwordIncorrect passwordCheck password spelling and case sensitivity
Invalid token typeUsing refresh token as access token (or vice versa)Use correct token for the endpoint
Invalid refresh token: JsonWebTokenErrorExpired or tampered refresh tokenRe-authenticate with email/password

Example: Complete Authentication Flow

Here’s a complete example in JavaScript:
const API_BASE = 'http://localhost:3000';

class FalconAlertAuth {
  constructor() {
    this.accessToken = localStorage.getItem('accessToken');
    this.refreshToken = localStorage.getItem('refreshToken');
  }

  async login(email, password, type = 'mobile') {
    const response = await fetch(`${API_BASE}/auth/login`, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ email, password, type })
    });

    if (!response.ok) {
      throw new Error('Login failed');
    }

    const { accessToken, refreshToken } = await response.json();
    this.storeTokens(accessToken, refreshToken);
    return { accessToken, refreshToken };
  }

  async refresh() {
    const response = await fetch(`${API_BASE}/auth/refresh`, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ refreshToken: this.refreshToken })
    });

    if (!response.ok) {
      this.clearTokens();
      throw new Error('Session expired. Please login again.');
    }

    const { accessToken, refreshToken } = await response.json();
    this.storeTokens(accessToken, refreshToken);
    return { accessToken, refreshToken };
  }

  async authenticatedRequest(endpoint, options = {}) {
    const makeRequest = async (token) => {
      return fetch(`${API_BASE}${endpoint}`, {
        ...options,
        headers: {
          ...options.headers,
          'Authorization': `Bearer ${token}`
        }
      });
    };

    let response = await makeRequest(this.accessToken);

    // If unauthorized, try refreshing the token
    if (response.status === 401) {
      await this.refresh();
      response = await makeRequest(this.accessToken);
    }

    return response;
  }

  storeTokens(accessToken, refreshToken) {
    this.accessToken = accessToken;
    this.refreshToken = refreshToken;
    localStorage.setItem('accessToken', accessToken);
    localStorage.setItem('refreshToken', refreshToken);
  }

  clearTokens() {
    this.accessToken = null;
    this.refreshToken = null;
    localStorage.removeItem('accessToken');
    localStorage.removeItem('refreshToken');
  }
}

// Usage
const auth = new FalconAlertAuth();

// Login
await auth.login('user@example.com', 'SecurePass123', 'mobile');

// Make authenticated requests
const response = await auth.authenticatedRequest('/auth/profile');
const profile = await response.json();
console.log(profile);

Next Steps

API Reference

Explore all available endpoints

User Management

Learn about user registration and profiles

Reports

Create and manage fraud reports

WebSockets

Set up real-time notifications

Build docs developers (and LLMs) love