Escore Auth Service

OAuth2 Authentication Microservice for the Escore Ecosystem

https://auth.escore.live

๐ŸŽฏ Comprehensive Integration Guide

This guide provides detailed instructions for integrating with the Escore Auth Service at https://auth.escore.live.

๐Ÿ‘ค User Profile Structure

All user objects returned by the API include the following profile fields:

{
  "id": 1,
  "name": "John",
  "surname": "Doe",
  "email": "john@example.com",
  "is_child_user": false,
  "can_login": true,
  "birth_year": 1990,          // nullable integer
  "birth_date": "1990-05-15",  // nullable date (YYYY-MM-DD)
  "gender": "male",            // nullable string
  "country_code": "US",        // nullable string (ISO 3166-1 alpha-2)
  "created_at": "2025-06-10T17:30:00.000000Z",
  "updated_at": "2025-06-10T17:30:00.000000Z"
}

๐Ÿ“‹ Profile Field Details

  • surname: String (max 255 chars, last name/family name)
  • birth_year: Integer (1900-current year)
  • birth_date: Date string (YYYY-MM-DD, before today)
  • gender: String (max 20 chars, e.g., "male", "female", "other")
  • country_code: 2-letter ISO country code (e.g., "US", "CA", "GB")

โœ… Important Notes

  • All profile fields are optional and can be null
  • Fields can be provided during registration
  • Child users can have profile fields
  • Profile data is included in all user responses
  • surname is separate from name (first name)

๐ŸŒ Frontend Integration

Complete JavaScript Authentication Class

class EscoreAuth {
    constructor(baseUrl = 'https://auth.escore.live') {
        this.baseUrl = baseUrl;
        this.token = localStorage.getItem('auth_token');
    }

    async register(userData) {
        const response = await fetch(`${this.baseUrl}/api/register`, {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify({
                name: userData.name,
                surname: userData.surname,
                email: userData.email,
                password: userData.password,
                password_confirmation: userData.password,
                birth_year: userData.birth_year,
                birth_date: userData.birth_date,
                gender: userData.gender,
                country_code: userData.country_code
            })
        });

        const result = await response.json();
        if (response.ok) {
            this.token = result.access_token;
            localStorage.setItem('auth_token', this.token);
            return { success: true, user: result.user };
        }
        return { success: false, message: result.message };
    }

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

        const result = await response.json();
        if (response.ok) {
            this.token = result.access_token;
            localStorage.setItem('auth_token', this.token);
            return { success: true, user: result.user };
        }
        return { success: false, message: result.message };
    }

    async logout() {
        if (!this.token) return;
        
        await fetch(`${this.baseUrl}/api/logout`, {
            method: 'POST',
            headers: { 'Authorization': `Bearer ${this.token}` }
        });
        
        localStorage.removeItem('auth_token');
        this.token = null;
    }

    async makeAuthenticatedRequest(endpoint, options = {}) {
        if (!this.token) throw new Error('No token available');

        const response = await fetch(`${this.baseUrl}${endpoint}`, {
            ...options,
            headers: {
                'Content-Type': 'application/json',
                'Authorization': `Bearer ${this.token}`,
                ...options.headers
            }
        });

        if (response.status === 401) {
            localStorage.removeItem('auth_token');
            this.token = null;
            window.location.href = '/login';
            return;
        }

        return { success: response.ok, data: await response.json() };
    }

    // Child User Methods
    async createChildUser(childData) {
        return this.makeAuthenticatedRequest('/api/children', {
            method: 'POST',
            body: JSON.stringify({
                name: childData.name,
                surname: childData.surname,
                birth_year: childData.birth_year,
                birth_date: childData.birth_date,
                gender: childData.gender,
                country_code: childData.country_code
            })
        });
    }

    async assignEmailToChild(childId, email, password) {
        return this.makeAuthenticatedRequest(`/api/children/${childId}/assign-email`, {
            method: 'PUT',
            body: JSON.stringify({
                email,
                password,
                password_confirmation: password
            })
        });
    }

    async getChildUsers() {
        return this.makeAuthenticatedRequest('/api/children');
    }
}

// Usage Example
const auth = new EscoreAuth();

// Register with profile fields
const result = await auth.register({
    name: 'John',
    surname: 'Doe',
    email: 'john@example.com',
    password: 'password123',
    birth_year: 1990,
    birth_date: '1990-05-15',
    gender: 'male',
    country_code: 'US'
});

if (result.success) {
    console.log('Registered:', result.user);
    // User object now includes: surname, birth_year, birth_date, gender, country_code
}

React Hook Integration

import { useState, useEffect, createContext, useContext } from 'react';

const AuthContext = createContext();

export const AuthProvider = ({ children }) => {
    const [user, setUser] = useState(null);
    const [loading, setLoading] = useState(true);
    const auth = new EscoreAuth();

    useEffect(() => {
        const token = localStorage.getItem('auth_token');
        if (token) {
            auth.token = token;
            auth.makeAuthenticatedRequest('/api/user').then(result => {
                if (result.success) setUser(result.data.user);
                setLoading(false);
            });
        } else {
            setLoading(false);
        }
    }, []);

    const login = async (email, password) => {
        const result = await auth.login(email, password);
        if (result.success) setUser(result.user);
        return result;
    };

    const logout = async () => {
        await auth.logout();
        setUser(null);
    };

    return (
        <AuthContext.Provider value=\{\{ user, login, logout, loading, auth \}\}>
            \{children\}
        </AuthContext.Provider>
    );
};

export const useAuth = () => useContext(AuthContext);

๐Ÿ–ฅ๏ธ Backend Integration

Node.js/Express Example

const express = require('express');
const axios = require('axios');
const app = express();

const AUTH_SERVICE = 'https://auth.escore.live';

const validateToken = async (req, res, next) => {
    const authHeader = req.headers.authorization;
    
    if (!authHeader?.startsWith('Bearer ')) {
        return res.status(401).json({ message: 'No token provided' });
    }
    
    const token = authHeader.substring(7);
    
    try {
        const response = await axios.get(`${AUTH_SERVICE}/api/verify`, {
            headers: { 'Authorization': `Bearer ${token}` }
        });
        
        req.user = response.data.user;
        next();
    } catch (error) {
        return res.status(401).json({ message: 'Invalid token' });
    }
};

// Protected route
app.get('/api/protected', validateToken, (req, res) => {
    res.json({ message: 'Access granted', user: req.user });
});

app.listen(3000);

PHP/Laravel Example

<?php

namespace App\Services;

use Illuminate\Support\Facades\Http;

class EscoreAuthService
{
    private $baseUrl = 'https://auth.escore.live/api';

    public function validateToken(string $token): ?array
    {
        try {
            $response = Http::withHeaders([
                'Authorization' => "Bearer $token"
            ])->get($this->baseUrl . '/verify');

            return $response->successful() ? $response->json('user') : null;
        } catch (\Exception $e) {
            return null;
        }
    }

    public function getUserById(int $userId): ?array
    {
        try {
            $response = Http::get($this->baseUrl . "/public/user/$userId");
            return $response->successful() ? $response->json('user') : null;
        } catch (\Exception $e) {
            return null;
        }
    }
}

// Middleware
namespace App\Http\Middleware;

class EscoreAuth
{
    public function handle($request, $next)
    {
        $token = $request->bearerToken();
        if (!$token) {
            return response()->json(['message' => 'No token provided'], 401);
        }

        $authService = new \App\Services\EscoreAuthService();
        $user = $authService->validateToken($token);

        if (!$user) {
            return response()->json(['message' => 'Invalid token'], 401);
        }

        $request->merge(['auth_user' => $user]);
        return $next($request);
    }
}

๐Ÿ‘ถ Child User Management

Complete Child User Workflow

class ChildUserManager {
    constructor(auth) {
        this.auth = auth;
    }

    async createChild(childData) {
        const result = await this.auth.createChildUser(childData);
        if (result.success) {
            console.log('Child created (no login yet):', result.data.child);
            return result.data.child;
        }
        throw new Error(result.data.message);
    }

    async assignEmailToChild(childId, email, password) {
        const result = await this.auth.assignEmailToChild(childId, email, password);
        if (result.success) {
            console.log('Email assigned, child can now login');
            return result.data.child;
        }
        throw new Error(result.data.message);
    }

    async getChildren() {
        const result = await this.auth.getChildUsers();
        return result.success ? result.data.children : [];
    }
}

// Usage Example
const manager = new ChildUserManager(auth);

// 1. Create child with profile (cannot login)
const child = await manager.createChild({
    name: 'Emma',
    surname: 'Doe',
    birth_year: 2010,
    birth_date: '2010-08-20',
    gender: 'female',
    country_code: 'US'
});

// 2. Later, assign email (can now login)
await manager.assignEmailToChild(child.id, 'emma@family.com', 'emma123');

// 3. Child can now login independently
const childAuth = new EscoreAuth();
const childLogin = await childAuth.login('emma@family.com', 'emma123');

// 4. All user objects include profile fields
console.log('Child profile:', childLogin.user);
// Output includes: surname, birth_year, birth_date, gender, country_code

๐Ÿงช Testing Examples

cURL Commands

# Health Check
curl https://auth.escore.live/api/health

# Register User with Profile
curl -X POST https://auth.escore.live/api/register \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Test",
    "surname": "User",
    "email": "test@example.com",
    "password": "password123",
    "password_confirmation": "password123",
    "birth_year": 1990,
    "birth_date": "1990-05-15",
    "gender": "male",
    "country_code": "US"
  }'

# Login
curl -X POST https://auth.escore.live/api/login \
  -H "Content-Type: application/json" \
  -d '{
    "email": "test@example.com",
    "password": "password123"
  }'

# Get User (replace TOKEN)
curl https://auth.escore.live/api/user \
  -H "Authorization: Bearer TOKEN"

# Create Child User with Profile
curl -X POST https://auth.escore.live/api/children \
  -H "Authorization: Bearer TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Emma",
    "surname": "Doe",
    "birth_year": 2010,
    "birth_date": "2010-08-20",
    "gender": "female",
    "country_code": "US"
  }'

JavaScript Testing

// Test authentication flow
async function testAuth() {
    const auth = new EscoreAuth();
    
    try {
        // Register with profile
        const register = await auth.register({
            name: 'Test',
            surname: 'User',
            email: `test${Date.now()}@example.com`,
            password: 'password123',
            birth_year: 1990,
            birth_date: '1990-05-15',
            gender: 'male',
            country_code: 'US'
        });
        console.log('Registration:', register);
        
        // Create child with profile
        const child = await auth.createChildUser({
            name: 'Test',
            surname: 'Child',
            birth_year: 2010,
            birth_date: '2010-08-20',
            gender: 'female',
            country_code: 'US'
        });
        console.log('Child created:', child);
        
        // Assign email to child
        const childEmail = await auth.assignEmailToChild(
            child.data.child.id,
            `child${Date.now()}@example.com`,
            'childpass123'
        );
        console.log('Child email assigned:', childEmail);
        
    } catch (error) {
        console.error('Test failed:', error);
    }
}

testAuth();

โญ Best Practices

๐Ÿ”’ Security Best Practices

  • Always use HTTPS in production
  • Store tokens securely (httpOnly cookies preferred)
  • Implement token refresh logic
  • Validate tokens server-side for sensitive operations
  • Handle authentication errors gracefully
  • Use CSRF protection for web applications

๐Ÿš€ Performance Tips

  • Cache user data when appropriate
  • Use connection pooling for backend services
  • Implement request retries with exponential backoff
  • Handle rate limiting gracefully
  • Monitor authentication metrics
  • Minimize token payload size

๐Ÿ› ๏ธ Error Handling

  • Always handle network failures
  • Provide meaningful error messages
  • Log authentication failures for monitoring
  • Implement graceful degradation
  • Use proper HTTP status codes
  • Handle token expiration automatically

๐Ÿš€ Production Deployment Checklist

  • โœ… Test all authentication flows
  • โœ… Verify CORS configuration for your domains
  • โœ… Set up monitoring and alerting
  • โœ… Configure rate limiting
  • โœ… Test child user workflows
  • โœ… Implement proper error handling
  • โœ… Set up backup authentication methods
  • โœ… Document your integration for your team

๐Ÿ“š Additional Resources

Check out the main API documentation for detailed endpoint specifications.