API Documentation v2.0

Developer Documentation

Everything you need to integrate Onehux SSO into your web, mobile, or SPA application

Quick Start Guide

How Onehux SSO Works

Onehux SSO implements standard OAuth 2.0 and OpenID Connect (OIDC) protocols with two integration methods:

🌐 Web Flow

For server-rendered applications (Django, Laravel, Rails)

  • ✓ Browser redirects
  • ✓ Cookie-based sessions
  • ✓ Traditional web apps

📱 Mobile/SPA Flow

For mobile apps and SPAs (React, Vue, Flutter)

  • ✓ Pure API calls
  • ✓ Token-based auth
  • ✓ No redirects needed
📱

Mobile Apps Integration

Integrate Onehux SSO into your iOS, Android, or Flutter mobile applications using our dedicated mobile API endpoints.

Why Use Mobile Endpoints?

✅ Advantages

  • • Token-based authentication
  • • No webviews or redirects
  • • Native app experience
  • • Automatic token refresh
  • • Single Logout (SLO) support
  • • Role-based access control

🔐 Security Features

  • • Secure token storage
  • • Certificate pinning ready
  • • Rate limiting protection
  • • Device tracking
  • • OAuth 2.0 standard
  • • PKCE support

Mobile Authentication Flow

1

POST /api/mobile/login/

Send email + password → Get authorization_code

2

POST /api/mobile/token/

Exchange code → Get access_token + refresh_token

3

GET /api/mobile/userinfo/

Use access_token → Get user information

4

POST /api/mobile/logout/

Revoke all tokens + trigger SLO

Mobile API Endpoints

POST /api/mobile/login/

Login with email and password to get authorization code.

// Request
POST /api/mobile/login/
Content-Type: application/json

{
  "email": "[email protected]",
  "password": "password123",
  "client_id": "app_xxx",
  "scope": "openid profile email",
  "device_name": "iPhone 14 Pro",
  "device_id": "unique-device-id"
}

// Response (200 OK)
{
  "authorization_code": "xxx",
  "expires_in": 600,
  "user": {
    "id": "uuid",
    "email": "[email protected]",
    "name": "John Doe",
    "username": "johndoe",
    "picture": "https://...",
    "role": "admin"
  }
}
📱

Mobile Apps Integration

Integrate Onehux SSO into your iOS, Android, or Flutter mobile applications using our dedicated mobile API endpoints.

Mobile Authentication Flow

1

POST /api/mobile/login/

Send email + password → Get authorization_code

2

POST /api/mobile/token/

Exchange code → Get access_token + refresh_token

3

GET /api/mobile/userinfo/

Use access_token → Get user information

4

POST /api/mobile/logout/

Revoke all tokens + trigger SLO

Mobile API Endpoints

POST /api/mobile/login/
{
  "email": "[email protected]",
  "password": "password123",
  "client_id": "app_xxx",
  "scope": "openid profile email",
  "device_name": "iPhone 14",
  "device_id": "unique-id"
}
POST /api/mobile/token/
{
  "grant_type": "authorization_code",
  "code": "authorization_code_here",
  "client_id": "app_xxx",
  "client_secret": "secret_xxx"
}
GET /api/mobile/userinfo/

Header: Authorization: Bearer {access_token}

POST /api/mobile/logout/

Header: Authorization: Bearer {access_token}

🎯 Flutter Implementation

Complete Flutter integration with automatic token refresh and secure storage.

Dependencies

dependencies:
  dio: ^5.4.0
  flutter_secure_storage: ^9.0.0
  provider: ^6.1.1
  json_annotation: ^4.8.1
// AuthService example
class AuthService {
  final ApiClient _apiClient = ApiClient();
  
  Future login(String email, String password) async {
    // Step 1: Get authorization code
    final loginResponse = await _apiClient.dio.post(
      '/api/mobile/login/',
      data: {
        'email': email,
        'password': password,
        'client_id': 'app_xxx',
        'scope': 'openid profile email',
        'device_name': await _getDeviceName(),
      },
    );
    
    // Step 2: Exchange for tokens
    final tokenResponse = await _apiClient.dio.post(
      '/api/mobile/token/',
      data: {
        'grant_type': 'authorization_code',
        'code': loginResponse.data['authorization_code'],
        'client_id': 'app_xxx',
        'client_secret': 'secret_xxx',
      },
    );
    
    // Step 3: Save tokens securely
    await _storage.saveTokens(
      accessToken: tokenResponse.data['access_token'],
      refreshToken: tokenResponse.data['refresh_token'],
    );
  }
}

📚 Full Flutter Guide: See the complete production-ready Flutter implementation with protected routes, automatic token refresh, and SLO support in our GitHub repository.

🍎 Swift (iOS) Implementation

import Foundation

class OneHuxAuth {
    let baseURL = "https://accounts.onehux.com"
    let clientId = "app_xxx"
    let clientSecret = "secret_xxx"
    
    func login(email: String, password: String) async throws -> TokenResponse {
        // Step 1: Get authorization code
        let loginURL = URL(string: "\(baseURL)/api/mobile/login/")!
        var request = URLRequest(url: loginURL)
        request.httpMethod = "POST"
        request.setValue("application/json", forHTTPHeaderField: "Content-Type")
        
        let loginBody: [String: Any] = [
            "email": email,
            "password": password,
            "client_id": clientId,
            "scope": "openid profile email",
            "device_name": UIDevice.current.name
        ]
        
        request.httpBody = try JSONSerialization.data(withJSONObject: loginBody)
        let (loginData, _) = try await URLSession.shared.data(for: request)
        let authCode = try JSONDecoder().decode(AuthCodeResponse.self, from: loginData)
        
        // Step 2: Exchange for tokens
        let tokenURL = URL(string: "\(baseURL)/api/mobile/token/")!
        var tokenRequest = URLRequest(url: tokenURL)
        tokenRequest.httpMethod = "POST"
        tokenRequest.setValue("application/json", forHTTPHeaderField: "Content-Type")
        
        let tokenBody: [String: Any] = [
            "grant_type": "authorization_code",
            "code": authCode.authorizationCode,
            "client_id": clientId,
            "client_secret": clientSecret
        ]
        
        tokenRequest.httpBody = try JSONSerialization.data(withJSONObject: tokenBody)
        let (tokenData, _) = try await URLSession.shared.data(for: tokenRequest)
        
        return try JSONDecoder().decode(TokenResponse.self, from: tokenData)
    }
}

🤖 Kotlin (Android) Implementation

import retrofit2.http.*

interface OneHuxAuthAPI {
    @POST("/api/mobile/login/")
    suspend fun login(@Body request: LoginRequest): AuthCodeResponse
    
    @POST("/api/mobile/token/")
    suspend fun getToken(@Body request: TokenRequest): TokenResponse
    
    @GET("/api/mobile/userinfo/")
    suspend fun getUserInfo(@Header("Authorization") auth: String): UserInfo
}

// Usage
class AuthManager {
    suspend fun login(email: String, password: String): TokenResponse {
        // Step 1: Get authorization code
        val authCode = api.login(
            LoginRequest(
                email = email,
                password = password,
                clientId = "app_xxx",
                scope = "openid profile email",
                deviceName = Build.MODEL
            )
        )
        
        // Step 2: Exchange for tokens
        return api.getToken(
            TokenRequest(
                grantType = "authorization_code",
                code = authCode.authorizationCode,
                clientId = "app_xxx",
                clientSecret = "secret_xxx"
            )
        )
    }
}
⚛️

SPA Applications Integration

Integrate Onehux SSO into your Single Page Applications (React, Vue, SolidJS) using the same mobile API endpoints.

Why Mobile Endpoints for SPAs?

✅ Mobile Endpoints (Recommended)

  • • No redirects - pure API calls
  • • Token-based authentication
  • • Better UX - no page reloads
  • • CORS-enabled
  • • Same code for mobile & web
  • • Auto token refresh

⚠️ Traditional OAuth Flow

  • • Browser redirects required
  • • Cookie-based sessions
  • • Page reloads
  • • Complex CORS setup
  • • Better for server-rendered apps

⚛️ React + TypeScript

Complete React implementation with automatic token refresh and TypeScript support.

// src/services/auth.service.ts
import axios from 'axios';

const API_BASE_URL = 'https://accounts.onehux.com';
const CLIENT_ID = 'app_xxx';
const CLIENT_SECRET = 'secret_xxx';

class AuthService {
  async login(email: string, password: string): Promise {
    // Step 1: Get authorization code
    const loginResponse = await axios.post(
      `${API_BASE_URL}/api/mobile/login/`,
      {
        email,
        password,
        client_id: CLIENT_ID,
        scope: 'openid profile email',
        device_name: navigator.userAgent,
      }
    );

    // Step 2: Exchange code for tokens
    const tokenResponse = await axios.post(
      `${API_BASE_URL}/api/mobile/token/`,
      {
        grant_type: 'authorization_code',
        code: loginResponse.data.authorization_code,
        client_id: CLIENT_ID,
        client_secret: CLIENT_SECRET,
      }
    );

    // Save tokens
    localStorage.setItem('access_token', tokenResponse.data.access_token);
    localStorage.setItem('refresh_token', tokenResponse.data.refresh_token);
  }

  async getUserInfo() {
    const token = localStorage.getItem('access_token');
    const response = await axios.get(
      `${API_BASE_URL}/api/mobile/userinfo/`,
      { headers: { Authorization: `Bearer ${token}` } }
    );
    return response.data;
  }

  async logout() {
    const token = localStorage.getItem('access_token');
    await axios.post(
      `${API_BASE_URL}/api/mobile/logout/`,
      {},
      { headers: { Authorization: `Bearer ${token}` } }
    );
    localStorage.removeItem('access_token');
    localStorage.removeItem('refresh_token');
  }
}

export default new AuthService();
// src/App.tsx - Usage example
import { useState, useEffect } from 'react';
import authService from './services/auth.service';

function App() {
  const [user, setUser] = useState(null);

  const handleLogin = async (email: string, password: string) => {
    await authService.login(email, password);
    const userInfo = await authService.getUserInfo();
    setUser(userInfo);
  };

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

  return (
    
{user ? ( ) : ( )}
); }

💚 Vue 3 Composition API

// src/composables/useAuth.ts
import { ref } from 'vue';
import axios from 'axios';

const API_BASE_URL = 'https://accounts.onehux.com';
const user = ref(null);

export function useAuth() {
  const login = async (email: string, password: string) => {
    // Get authorization code
    const loginRes = await axios.post(`${API_BASE_URL}/api/mobile/login/`, {
      email,
      password,
      client_id: 'app_xxx',
      scope: 'openid profile email',
    });

    // Exchange for tokens
    const tokenRes = await axios.post(`${API_BASE_URL}/api/mobile/token/`, {
      grant_type: 'authorization_code',
      code: loginRes.data.authorization_code,
      client_id: 'app_xxx',
      client_secret: 'secret_xxx',
    });

    localStorage.setItem('access_token', tokenRes.data.access_token);
    await loadUserInfo();
  };

  const loadUserInfo = async () => {
    const token = localStorage.getItem('access_token');
    const res = await axios.get(`${API_BASE_URL}/api/mobile/userinfo/`, {
      headers: { Authorization: `Bearer ${token}` },
    });
    user.value = res.data;
  };

  const logout = async () => {
    const token = localStorage.getItem('access_token');
    await axios.post(`${API_BASE_URL}/api/mobile/logout/`, {}, {
      headers: { Authorization: `Bearer ${token}` },
    });
    user.value = null;
    localStorage.removeItem('access_token');
  };

  return { user, login, logout, loadUserInfo };
}

🔷 SolidJS

// src/auth/AuthProvider.tsx
import { createContext, useContext, createSignal } from 'solid-js';
import axios from 'axios';

const API_BASE_URL = 'https://accounts.onehux.com';

const AuthContext = createContext();

export function AuthProvider(props) {
  const [user, setUser] = createSignal(null);

  const login = async (email: string, password: string) => {
    const loginRes = await axios.post(`${API_BASE_URL}/api/mobile/login/`, {
      email, password, client_id: 'app_xxx', scope: 'openid profile email',
    });

    const tokenRes = await axios.post(`${API_BASE_URL}/api/mobile/token/`, {
      grant_type: 'authorization_code',
      code: loginRes.data.authorization_code,
      client_id: 'app_xxx',
      client_secret: 'secret_xxx',
    });

    localStorage.setItem('access_token', tokenRes.data.access_token);

    const userRes = await axios.get(`${API_BASE_URL}/api/mobile/userinfo/`, {
      headers: { Authorization: `Bearer ${tokenRes.data.access_token}` },
    });

    setUser(userRes.data);
  };

  const logout = async () => {
    await axios.post(`${API_BASE_URL}/api/mobile/logout/`, {}, {
      headers: { Authorization: `Bearer ${localStorage.getItem('access_token')}` },
    });
    localStorage.removeItem('access_token');
    setUser(null);
  };

  return (
    
    
      {props.children}
    
  );
}

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

🔒 Security Best Practices for SPAs

  • Use HTTPS only in production
  • Store tokens in memory or sessionStorage for sensitive apps
  • Implement auto token refresh using HTTP interceptors
  • Enable CORS properly on your API
  • Sanitize user inputs to prevent XSS
  • Never expose client_secret
    📱

    Mobile Apps Integration

    Integrate Onehux SSO into your iOS, Android, or Flutter mobile applications using our dedicated mobile API endpoints with full SSO/SLO support.

    Mobile Authentication Flow

    1

    POST /api/mobile/login/

    Send credentials → Get authorization_code + user info

    2

    POST /api/mobile/token/

    Exchange code → Get access_token + refresh_token + id_token

    3

    GET /api/mobile/userinfo/

    Use Bearer token → Get full user profile

    4

    POST /api/mobile/logout/

    Revoke all tokens + trigger Single Logout (SLO)

    ✨ Key Features

    • ✓ Token-based authentication (no webviews)
    • ✓ Automatic token refresh
    • ✓ Secure token storage (Keychain/KeyStore)
    • ✓ Single Logout (SLO) support
    • ✓ Role-based access control
    • ✓ Device tracking
    • ✓ Rate limiting protection

    Mobile API Endpoints

    POST
    /api/mobile/login/

    Login with credentials to get authorization code

    POST /api/mobile/login/
    Content-Type: application/json
    
    {
      "email": "[email protected]",
      "password": "password123",
      "client_id": "app_xxx",
      "scope": "openid profile email",
      "device_name": "iPhone 14 Pro",
      "device_id": "unique-device-identifier"
    }
    
    // Response (200 OK)
    {
      "authorization_code": "auth_code_xxx",
      "expires_in": 600,
      "user": {
        "id": "user-uuid",
        "email": "[email protected]",
        "name": "John Doe",
        "username": "johndoe",
        "picture": "https://...",
        "role": "admin"
      }
    }
    POST
    /api/mobile/token/

    Exchange authorization code for access tokens

    POST /api/mobile/token/
    Content-Type: application/json
    
    {
      "grant_type": "authorization_code",
      "code": "auth_code_xxx",
      "client_id": "app_xxx",
      "client_secret": "secret_xxx"
    }
    
    // Response (200 OK)
    {
      "access_token": "eyJhbGci...",
      "token_type": "Bearer",
      "expires_in": 3600,
      "refresh_token": "refresh_xxx",
      "id_token": "eyJhbGci...",
      "scope": "openid profile email"
    }
    POST
    /api/mobile/token/

    Refresh access token using refresh token

    POST /api/mobile/token/
    Content-Type: application/json
    
    {
      "grant_type": "refresh_token",
      "refresh_token": "refresh_xxx",
      "client_id": "app_xxx",
      "client_secret": "secret_xxx"
    }
    GET
    /api/mobile/userinfo/

    Get current user information

    GET /api/mobile/userinfo/
    Authorization: Bearer {access_token}
    
    // Response (200 OK)
    {
      "sub": "user-uuid",
      "email": "[email protected]",
      "email_verified": true,
      "name": "John Doe",
      "given_name": "John",
      "family_name": "Doe",
      "username": "johndoe",
      "picture": "https://...",
      "role": "admin",
      "organization_id": "org-uuid",
      "organization_name": "ACME Corp",
      "updated_at": 1234567890
    }
    POST
    /api/mobile/logout/

    Logout and revoke all tokens (triggers SLO)

    POST /api/mobile/logout/
    Authorization: Bearer {access_token}
    
    // Response (200 OK)
    {
      "success": true,
      "message": "Logged out successfully from all devices"
    }

    🎯 Flutter Implementation

    Complete production-ready Flutter integration with automatic token refresh, secure storage, and SLO support.

    Required Dependencies

    dependencies:
      dio: ^5.4.0                           # HTTP client
      flutter_secure_storage: ^9.0.0       # Secure token storage
      provider: ^6.1.1                      # State management
      json_annotation: ^4.8.1               # JSON serialization
      device_info_plus: ^9.1.1              # Device info
    
    dev_dependencies:
      build_runner: ^2.4.7
      json_serializable: ^6.7.1
    // lib/services/auth_service.dart
    class AuthService {
      final ApiClient _apiClient = ApiClient();
      final StorageService _storage = StorageService();
      
      Future login(String email, String password) async {
        // Step 1: Get authorization code
        final loginResponse = await _apiClient.dio.post(
          '/api/mobile/login/',
          data: {
            'email': email,
            'password': password,
            'client_id': 'app_xxx',
            'scope': 'openid profile email',
            'device_name': await _getDeviceName(),
            'device_id': await _getDeviceId(),
          },
        );
        
        // Step 2: Exchange for tokens
        final tokenResponse = await _apiClient.dio.post(
          '/api/mobile/token/',
          data: {
            'grant_type': 'authorization_code',
            'code': loginResponse.data['authorization_code'],
            'client_id': 'app_xxx',
            'client_secret': 'secret_xxx',
          },
        );
        
        // Step 3: Save tokens securely
        await _storage.saveTokens(
          accessToken: tokenResponse.data['access_token'],
          refreshToken: tokenResponse.data['refresh_token'],
          expiresIn: tokenResponse.data['expires_in'],
        );
      }
      
      Future getUserInfo() async {
        final response = await _apiClient.dio.get('/api/mobile/userinfo/');
        return UserInfo.fromJson(response.data);
      }
      
      Future logout() async {
        await _apiClient.dio.post('/api/mobile/logout/');
        await _storage.clearTokens();
      }
    }

    📚 Complete Flutter Guide: For the full production-ready implementation including protected routes, auto token refresh, HTTP interceptors, and complete code examples, see our detailed Flutter integration guide in the full documentation.

    🍎 Swift (iOS) Implementation

    import Foundation
    
    class OneHuxAuth {
        let baseURL = "https://accounts.onehux.com"
        let clientId = "app_xxx"
        let clientSecret = "secret_xxx"
        
        func login(email: String, password: String) async throws -> TokenResponse {
            // Step 1: Get authorization code
            let loginURL = URL(string: "\(baseURL)/api/mobile/login/")!
            var request = URLRequest(url: loginURL)
            request.httpMethod = "POST"
            request.setValue("application/json", forHTTPHeaderField: "Content-Type")
            
            let loginBody: [String: Any] = [
                "email": email,
                "password": password,
                "client_id": clientId,
                "scope": "openid profile email",
                "device_name": UIDevice.current.name,
                "device_id": UIDevice.current.identifierForVendor?.uuidString ?? ""
            ]
            
            request.httpBody = try JSONSerialization.data(withJSONObject: loginBody)
            let (loginData, _) = try await URLSession.shared.data(for: request)
            let authCode = try JSONDecoder().decode(AuthCodeResponse.self, from: loginData)
            
            // Step 2: Exchange for tokens
            let tokenURL = URL(string: "\(baseURL)/api/mobile/token/")!
            var tokenRequest = URLRequest(url: tokenURL)
            tokenRequest.httpMethod = "POST"
            tokenRequest.setValue("application/json", forHTTPHeaderField: "Content-Type")
            
            let tokenBody: [String: Any] = [
                "grant_type": "authorization_code",
                "code": authCode.authorizationCode,
                "client_id": clientId,
                "client_secret": clientSecret
            ]
            
            tokenRequest.httpBody = try JSONSerialization.data(withJSONObject: tokenBody)
            let (tokenData, _) = try await URLSession.shared.data(for: tokenRequest)
            
            let tokens = try JSONDecoder().decode(TokenResponse.self, from: tokenData)
            
            // Save to Keychain
            try saveToKeychain(accessToken: tokens.accessToken, refreshToken: tokens.refreshToken)
            
            return tokens
        }
        
        func getUserInfo(accessToken: String) async throws -> UserInfo {
            let url = URL(string: "\(baseURL)/api/mobile/userinfo/")!
            var request = URLRequest(url: url)
            request.setValue("Bearer \(accessToken)", forHTTPHeaderField: "Authorization")
            
            let (data, _) = try await URLSession.shared.data(for: request)
            return try JSONDecoder().decode(UserInfo.self, from: data)
        }
    }

    🤖 Kotlin (Android) Implementation

    import retrofit2.Retrofit
    import retrofit2.converter.gson.GsonConverterFactory
    import retrofit2.http.*
    
    interface OneHuxAuthAPI {
        @POST("/api/mobile/login/")
        suspend fun login(@Body request: LoginRequest): AuthCodeResponse
        
        @POST("/api/mobile/token/")
        suspend fun getToken(@Body request: TokenRequest): TokenResponse
        
        @GET("/api/mobile/userinfo/")
        suspend fun getUserInfo(@Header("Authorization") auth: String): UserInfo
        
        @POST("/api/mobile/logout/")
        suspend fun logout(@Header("Authorization") auth: String)
    }
    
    // Data classes
    data class LoginRequest(
        val email: String,
        val password: String,
        val client_id: String,
        val scope: String = "openid profile email",
        val device_name: String,
        val device_id: String
    )
    
    // Usage
    class AuthManager(private val context: Context) {
        private val api: OneHuxAuthAPI = Retrofit.Builder()
            .baseUrl("https://accounts.onehux.com")
            .addConverterFactory(GsonConverterFactory.create())
            .build()
            .create(OneHuxAuthAPI::class.java)
        
        suspend fun login(email: String, password: String): TokenResponse {
            // Step 1: Get authorization code
            val authCode = api.login(
                LoginRequest(
                    email = email,
                    password = password,
                    client_id = "app_xxx",
                    device_name = Build.MODEL,
                    device_id = Settings.Secure.getString(
                        context.contentResolver,
                        Settings.Secure.ANDROID_ID
                    )
                )
            )
            
            // Step 2: Exchange for tokens
            val tokens = api.getToken(
                TokenRequest(
                    grant_type = "authorization_code",
                    code = authCode.authorization_code,
                    client_id = "app_xxx",
                    client_secret = "secret_xxx"
                )
            )
            
            // Step 3: Save to EncryptedSharedPreferences
            saveTokensSecurely(tokens)
            
            return tokens
        }
    }
    ⚛️

    SPA Applications Integration