Overview

The Authentication API provides secure user registration, login, and token management functionality. It supports both email and phone number-based authentication with JWT token issuance.

Registration

Standard Registration

Register a new user with email and phone number. Endpoint: POST /api/register Request:
{
  "username": "johndoe",
  "email": "john@example.com",
  "phone": "1234567890",
  "country_code": "1",
  "password": "SecurePass123!",
  "password_confirmation": "SecurePass123!",
  "meta_data": "{\"referral_code\": \"ABC123\"}"
}
Validation Rules:
  • username: Required, 3-50 characters, must be unique
  • email: Required, valid email format, must be unique
  • phone: Required, minimum 10 digits, numeric only
  • country_code: Required, 1-3 digits
  • password: Required, minimum 8 characters
  • password_confirmation: Required, must match password
  • meta_data: Optional, valid JSON string
Response: 200 OK
{
  "code": 200,
  "message": "Registration successful",
  "data": {
    "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
    "refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
    "access_token_expires_at": "2025-10-13T12:30:00Z",
    "refresh_token_expires_at": "2025-10-20T12:00:00Z",
    "user": {
      "id": 123,
      "username": "johndoe",
      "email": {
        "email": "john@example.com",
        "verified": false
      },
      "phone": {
        "phone": "1234567890",
        "country_code": "1",
        "verified": false
      },
      "kyc_status": "pending",
      "is_active": true,
      "is_frozen": false
    }
  }
}
Example:
async function register(userData) {
  const response = await fetch('https://api.rcoinx.com/api/register', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json'
    },
    body: JSON.stringify(userData)
  });
  
  const data = await response.json();
  
  if (response.ok) {
    // Store tokens
    localStorage.setItem('access_token', data.data.access_token);
    localStorage.setItem('refresh_token', data.data.refresh_token);
    
    // Redirect to dashboard
    window.location.href = '/dashboard';
  } else {
    throw new Error(data.message);
  }
  
  return data;
}

// Usage
try {
  await register({
    username: 'johndoe',
    email: 'john@example.com',
    phone: '1234567890',
    country_code: '1',
    password: 'SecurePass123!',
    password_confirmation: 'SecurePass123!'
  });
} catch (error) {
  console.error('Registration failed:', error);
}

Multi-Step Registration

For a more controlled registration flow with SMS OTP verification, see Session Management Registration.

Login

Login with Email

Endpoint: POST /api/login Request:
{
  "email": "john@example.com",
  "password": "SecurePass123!",
  "remember": false
}
Parameters:
  • email: Required, valid email address
  • password: Required, minimum 8 characters
  • remember: Optional boolean, extends refresh token lifetime
Response: 200 OK
{
  "code": 200,
  "message": "Login successful",
  "data": {
    "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
    "refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
    "access_token_expires_at": "2025-10-13T12:30:00Z",
    "refresh_token_expires_at": "2025-10-20T12:00:00Z",
    "user": {
      "id": 123,
      "username": "johndoe",
      "email": {
        "email": "john@example.com",
        "verified": true
      },
      "phone": {
        "phone": "1234567890",
        "country_code": "1",
        "verified": true
      },
      "kyc_status": "approved",
      "is_active": true,
      "is_frozen": false
    }
  }
}

Login with Phone

Endpoint: POST /api/login Request:
{
  "phone": "1234567890",
  "country_code": "1",
  "password": "SecurePass123!",
  "remember": true
}
Parameters:
  • phone: Required, minimum 10 digits
  • country_code: Required, 1-3 digits
  • password: Required, minimum 8 characters
  • remember: Optional boolean
Example:
async function login(email, password, remember = false) {
  const response = await fetch('https://api.rcoinx.com/api/login', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({ email, password, remember })
  });
  
  const data = await response.json();
  
  if (response.ok) {
    // Store tokens
    localStorage.setItem('access_token', data.data.access_token);
    localStorage.setItem('refresh_token', data.data.refresh_token);
    localStorage.setItem('user', JSON.stringify(data.data.user));
    
    return data;
  } else {
    throw new Error(data.message);
  }
}

// Usage
try {
  const result = await login('john@example.com', 'SecurePass123!', true);
  console.log('Login successful:', result);
} catch (error) {
  console.error('Login failed:', error);
}

Login Flow

Token Refresh

When the access token expires, use the refresh token to obtain a new access token without requiring the user to log in again. Endpoint: POST /api/refresh Headers:
Authorization: Bearer <refresh_token>
Response: 200 OK
{
  "code": 200,
  "message": "Token refreshed successfully",
  "data": {
    "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
    "access_token_expires_at": "2025-10-13T12:45:00Z"
  }
}
Example:
async function refreshAccessToken() {
  const refreshToken = localStorage.getItem('refresh_token');
  
  const response = await fetch('https://api.rcoinx.com/api/refresh', {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${refreshToken}`,
      'Content-Type': 'application/json'
    }
  });
  
  const data = await response.json();
  
  if (response.ok) {
    // Update access token
    localStorage.setItem('access_token', data.data.access_token);
    return data.data.access_token;
  } else {
    // Refresh token expired, redirect to login
    localStorage.clear();
    window.location.href = '/login';
    throw new Error('Session expired');
  }
}

// Automatic token refresh with axios interceptor
axios.interceptors.response.use(
  response => response,
  async error => {
    const originalRequest = error.config;
    
    if (error.response.status === 401 && !originalRequest._retry) {
      originalRequest._retry = true;
      
      try {
        const newToken = await refreshAccessToken();
        originalRequest.headers['Authorization'] = `Bearer ${newToken}`;
        return axios(originalRequest);
      } catch (refreshError) {
        return Promise.reject(refreshError);
      }
    }
    
    return Promise.reject(error);
  }
);

Logout

Endpoint: POST /api/logout Headers:
Authorization: Bearer <access_token>
Response: 200 OK
{
  "code": 200,
  "message": "Logout successful"
}
Example:
async function logout() {
  const accessToken = localStorage.getItem('access_token');
  
  try {
    await fetch('https://api.rcoinx.com/api/logout', {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${accessToken}`,
        'Content-Type': 'application/json'
      }
    });
  } catch (error) {
    console.error('Logout error:', error);
  } finally {
    // Clear local storage regardless of API response
    localStorage.clear();
    window.location.href = '/login';
  }
}
The current logout implementation is a client-side operation. The API returns success but doesn’t invalidate the token server-side. To fully logout and revoke sessions, use the Session Logout endpoints.

Password Change

Change the authenticated user’s password. Endpoint: PUT /api/password Headers:
Authorization: Bearer <access_token>
Request:
{
  "old_password": "OldPassword123!",
  "new_password": "NewPassword456!",
  "new_password_confirmation": "NewPassword456!"
}
Response: 200 OK
{
  "code": 200,
  "message": "Password changed successfully"
}
Example:
async function changePassword(oldPassword, newPassword) {
  const accessToken = localStorage.getItem('access_token');
  
  const response = await fetch('https://api.rcoinx.com/api/password', {
    method: 'PUT',
    headers: {
      'Authorization': `Bearer ${accessToken}`,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      old_password: oldPassword,
      new_password: newPassword,
      new_password_confirmation: newPassword
    })
  });
  
  const data = await response.json();
  
  if (!response.ok) {
    throw new Error(data.message);
  }
  
  return data;
}

Forget Password Flow

For password recovery, see Session Management - Forget Password.

Error Responses

Invalid Credentials

Status: 401 Unauthorized
{
  "code": 401,
  "message": "Invalid credentials",
  "error": "Email or password is incorrect"
}

User Already Exists

Status: 409 Conflict
{
  "code": 409,
  "message": "User already exists",
  "error": "Email address is already registered"
}

Validation Error

Status: 422 Unprocessable Entity
{
  "code": 422,
  "message": "Validation failed",
  "error": "password: must be at least 8 characters"
}

Account Frozen

Status: 403 Forbidden
{
  "code": 403,
  "message": "Account is frozen",
  "error": "Your account has been frozen. Please contact support."
}

Best Practices

1. Secure Token Storage

Never store tokens in localStorage if your application is vulnerable to XSS attacks. Consider using httpOnly cookies for sensitive applications.
// Good: Store in httpOnly cookie (backend sets)
// The backend should set tokens in httpOnly cookies

// Acceptable: Store in localStorage with proper XSS protection
if (typeof window !== 'undefined') {
  localStorage.setItem('access_token', token);
}

// Bad: Store in regular cookies or global variables
document.cookie = `token=${token}`; // Vulnerable to XSS
window.token = token; // Accessible to any script

2. Automatic Token Refresh

Implement automatic token refresh before expiration:
const TOKEN_REFRESH_BUFFER = 60000; // 1 minute before expiration

function scheduleTokenRefresh(expiresAt) {
  const expiryTime = new Date(expiresAt).getTime();
  const now = Date.now();
  const timeUntilRefresh = expiryTime - now - TOKEN_REFRESH_BUFFER;
  
  if (timeUntilRefresh > 0) {
    setTimeout(async () => {
      try {
        await refreshAccessToken();
      } catch (error) {
        console.error('Auto refresh failed:', error);
      }
    }, timeUntilRefresh);
  }
}

// After login
scheduleTokenRefresh(data.data.access_token_expires_at);

3. Handle Network Errors

async function makeAuthRequest(url, options) {
  try {
    const response = await fetch(url, options);
    return await response.json();
  } catch (error) {
    if (error.name === 'TypeError') {
      // Network error
      throw new Error('Network error. Please check your connection.');
    }
    throw error;
  }
}

4. Implement Loading States

function LoginForm() {
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState(null);
  
  const handleLogin = async (email, password) => {
    setLoading(true);
    setError(null);
    
    try {
      await login(email, password);
    } catch (err) {
      setError(err.message);
    } finally {
      setLoading(false);
    }
  };
  
  return (
    <form onSubmit={handleLogin}>
      {error && <div className="error">{error}</div>}
      <button disabled={loading}>
        {loading ? 'Logging in...' : 'Login'}
      </button>
    </form>
  );
}