Developer Documentation
Everything you need to integrate Onehux SSO into your web, mobile, or SPA application
Contents
Quick Start Guide
Get your application integrated with Onehux SSO in under 10 minutes. Choose your platform below.
Choose Your Platform
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
POST /api/mobile/login/
Send email + password → Get authorization_code
POST /api/mobile/token/
Exchange code → Get access_token + refresh_token
GET /api/mobile/userinfo/
Use access_token → Get user information
POST /api/mobile/logout/
Revoke all tokens + trigger SLO
Mobile API Endpoints
/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
POST /api/mobile/login/
Send email + password → Get authorization_code
POST /api/mobile/token/
Exchange code → Get access_token + refresh_token
GET /api/mobile/userinfo/
Use access_token → Get user information
POST /api/mobile/logout/
Revoke all tokens + trigger SLO
Mobile API Endpoints
/api/mobile/login/
{
"email": "[email protected]",
"password": "password123",
"client_id": "app_xxx",
"scope": "openid profile email",
"device_name": "iPhone 14",
"device_id": "unique-id"
}
/api/mobile/token/
{
"grant_type": "authorization_code",
"code": "authorization_code_here",
"client_id": "app_xxx",
"client_secret": "secret_xxx"
}
/api/mobile/userinfo/
Header: Authorization: Bearer {access_token}
/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
1POST /api/mobile/login/
Send credentials → Get authorization_code + user info
2POST /api/mobile/token/
Exchange code → Get access_token + refresh_token + id_token
3GET /api/mobile/userinfo/
Use Bearer token → Get full user profile
4POST /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(); Futurelogin(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