Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/astrxnomo/tourify/llms.txt

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

Overview

Tourify authenticates API requests using Laravel Sanctum bearer tokens. There are no session cookies — every protected request must include a token in the Authorization header. On the mobile client the token is persisted securely using Expo SecureStore (or localStorage on web), so it survives app restarts without requiring the user to log in again.
All protected endpoints require the Authorization: Bearer {your-token} header. Requests made without a valid token will receive a 401 Unauthenticated response.

Endpoints

POST /api/auth/register

Create a new user account. On success the API returns the new user object and a Sanctum plain-text token that can be used immediately. Required fields
name
string
required
The user’s display name. Maximum 255 characters.
email
string
required
A unique, valid e-mail address.
password
string
required
Minimum 8 characters.
password_confirmation
string
required
Must match password exactly.
curl -s -X POST http://localhost:8000/api/auth/register \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -d '{
    "name": "María García",
    "email": "maria@example.com",
    "password": "secret123",
    "password_confirmation": "secret123"
  }'

POST /api/auth/login

Authenticate an existing user and receive a new Sanctum token. Required fields
email
string
required
The user’s registered e-mail address.
password
string
required
The user’s password.
curl -s -X POST http://localhost:8000/api/auth/login \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -d '{
    "email": "maria@example.com",
    "password": "secret123"
  }'

GET /api/auth/me

Returns the currently authenticated user with their role loaded. Requires authentication.
curl -s http://localhost:8000/api/auth/me \
  -H "Accept: application/json" \
  -H "Authorization: Bearer 15|xYzAbCdEfGhIjKlMnOpQrStUvWx0987654321"

POST /api/auth/logout

Deletes the current access token, effectively invalidating it on the server. Requires authentication.
curl -s -X POST http://localhost:8000/api/auth/logout \
  -H "Accept: application/json" \
  -H "Authorization: Bearer 15|xYzAbCdEfGhIjKlMnOpQrStUvWx0987654321"

POST /api/auth/forgot-password

Initiates a password-reset flow for the given e-mail address.
email
string
required
The e-mail address associated with the account.
This endpoint always returns a success message regardless of whether the e-mail exists in the database. This is intentional — it prevents attackers from enumerating which e-mail addresses are registered with Tourify.
curl -s -X POST http://localhost:8000/api/auth/forgot-password \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -d '{"email": "maria@example.com"}'

Token storage on the client

After a successful login or registration the frontend persists the token using a thin wrapper around Expo SecureStore (native) and localStorage (web). The token is keyed as "auth_token".
storage.js
import * as SecureStore from "expo-secure-store";
import { Platform } from "react-native";

const isWeb = Platform.OS === "web";

export const storage = {
  getItem: (key) =>
    isWeb
      ? Promise.resolve(localStorage.getItem(key))
      : SecureStore.getItemAsync(key),

  setItem: (key, value) =>
    isWeb
      ? Promise.resolve(localStorage.setItem(key, value))
      : SecureStore.setItemAsync(key, value),

  deleteItem: (key) =>
    isWeb
      ? Promise.resolve(localStorage.removeItem(key))
      : SecureStore.deleteItemAsync(key),
};
Every API call reads the stored token and injects it into the Authorization header automatically:
services/post.js
import { storage } from "./storage";

const API_URL = "http://localhost:8000/api";

async function getAuthHeaders() {
  const token = await storage.getItem("auth_token");
  return {
    "Content-Type": "application/json",
    Accept: "application/json",
    ...(token ? { Authorization: `Bearer ${token}` } : {}),
  };
}

async function request(endpoint, method, data) {
  const headers = await getAuthHeaders();
  const options = { method, headers };
  if (data !== undefined) options.body = JSON.stringify(data);

  const response = await fetch(`${API_URL}${endpoint}`, options);
  const json = await response.json();

  if (!response.ok) {
    const message =
      json.message ||
      Object.values(json.errors || {})[0]?.[0] ||
      "Error en la solicitud";
    throw new Error(message);
  }

  return json;
}

export const post = (endpoint, data) => request(endpoint, "POST", data);
export const get  = (endpoint)       => request(endpoint, "GET");
export const patch = (endpoint, data = {}) => request(endpoint, "PATCH", data);
export const del  = (endpoint, data) => request(endpoint, "DELETE", data);
The useLogin and useRegister hooks call storage.setItem("auth_token", data.token) immediately after a successful API response:
hooks/useLogin.js
import { useCallback, useState } from "react";
import { Alert } from "react-native";
import { post } from "../services/post";
import { storage } from "../services/storage";

export function useLogin() {
  const [email, setEmail] = useState("");
  const [password, setPassword] = useState("");
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState(null);

  const handleLogin = useCallback(
    async (onSuccess) => {
      if (!email.trim() || !password.trim()) {
        Alert.alert("Error", "Por favor completa todos los campos.");
        return;
      }
      setLoading(true);
      setError(null);
      try {
        const data = await post("/auth/login", { email, password });
        await storage.setItem("auth_token", data.token); // ← persisted here
        onSuccess?.(data);
      } catch (err) {
        setError(err.message);
        Alert.alert("Error", err.message || "No se pudo iniciar sesión.");
      } finally {
        setLoading(false);
      }
    },
    [email, password],
  );

  return { email, setEmail, password, setPassword, loading, error, handleLogin };
}

Authentication flow summary

1

Register or log in

Call POST /api/auth/register or POST /api/auth/login. Both return { user, token }.
2

Persist the token

Store the token with storage.setItem("auth_token", token). On native devices this writes to Expo SecureStore; on web it writes to localStorage.
3

Attach the token to every request

getAuthHeaders() reads the token from storage and adds Authorization: Bearer {token} to each fetch call automatically.
4

Access protected endpoints

Any route inside the auth:sanctum middleware group — favorites, reviews, notifications, event registrations — will accept the request.
5

Log out

Call POST /api/auth/logout. The server deletes the token; remove it from local storage as well.

Build docs developers (and LLMs) love