name: oneauth-oauth2 description: Integrate client applications with OneAuth for OAuth2/OIDC authentication. Use when implementing user login/logout, token management, or identity integration via OneAuth (based on Ory Kratos + Ory Hydra). Covers Web OAuth2 Authorization Code + PKCE flow, token refresh/verify/revoke, UI customization, and multi-platform support (Web, mini-program, CLI). Trigger when user mentions OneAuth login, OAuth2 integration, SSO, or authentication with OneAuth.
OneAuth OAuth2 Integration
OneAuth is an identity authentication system based on Ory Kratos and Ory Hydra, providing OAuth2/OIDC standard authentication.
Architecture
Client App → OneAuth Proxy (Gateway) → Hydra (OAuth2) → OneAuth Login UI → Kratos (Identity)
↓
Kratos (Identity)
- OneAuth Proxy: Unified entry point for all authentication services (OAuth2, OIDC, Identity)
- Kratos: Identity management (internal, accessed via proxy)
- Hydra: OAuth2/OIDC server (internal, accessed via proxy)
- OneAuth UI: Frontend login/register pages
Service Endpoints
| Service | Port | Description |
|---|---|---|
| OneAuth Proxy | :8080 (Default) |
Unified Entry Point for API, OAuth2, and Identity |
| OneAuth Web | :8081 |
Frontend static resources (Login UI) |
| Hydra/Kratos | Internal Only | Not exposed directly |
OIDC Discovery
GET {ONEAUTH_PROXY_URL}/.well-known/openid-configuration
Standard OIDC libraries can auto-configure from this endpoint.
Web OAuth2 Login Flow (Authorization Code + PKCE)
Step 1: Generate PKCE parameters and redirect
const codeVerifier = generateRandomString(64);
const codeChallenge = await sha256Base64Url(codeVerifier);
const state = generateRandomString(32);
sessionStorage.setItem('oauth_code_verifier', codeVerifier);
sessionStorage.setItem('oauth_state', state);
const params = new URLSearchParams({
client_id: 'your-client-id',
redirect_uri: 'http://your-app.com/callback',
response_type: 'code',
scope: 'openid profile email offline',
state: state,
code_challenge: codeChallenge,
code_challenge_method: 'S256'
});
// Redirect to OneAuth Proxy instead of direct Hydra URL
window.location.href = `${ONEAUTH_PROXY_URL}/oauth2/auth?${params}`;
Step 2: Hydra redirects to OneAuth login page
{ONEAUTH_WEB_URL}/login?login_challenge=abc123...
User completes login (password/SMS/email/social) on OneAuth UI.
Step 3: OneAuth accepts login challenge and consent
OneAuth frontend automatically calls:
PUT /api/hydra/login/accept(with Kratos session cookie)- Hydra auto-skips consent (if
skip_consent: true)
Step 4: Handle callback with authorization code
// On your /callback page
const urlParams = new URLSearchParams(window.location.search);
const code = urlParams.get('code');
const state = urlParams.get('state');
// Verify state
if (state !== sessionStorage.getItem('oauth_state')) throw new Error('Invalid state');
const codeVerifier = sessionStorage.getItem('oauth_code_verifier');
Step 5: Exchange code for tokens
Option A: Via OneAuth proxy (recommended)
POST {ONEAUTH_API_URL}/api/oauth2/token
Content-Type: application/x-www-form-urlencoded
grant_type=authorization_code&
code={code}&
redirect_uri={redirect_uri}&
client_id={client_id}&
code_verifier={code_verifier}
Option B: Via backend API
Frontend sends code + code_verifier to your backend, backend exchanges with Hydra.
Token response:
{
"access_token": "ory_at_...",
"refresh_token": "ory_rt_...",
"id_token": "eyJ...",
"token_type": "bearer",
"expires_in": 3599,
"scope": "openid profile email offline"
}
Step 6: Store tokens and redirect
localStorage.setItem('access_token', data.access_token);
localStorage.setItem('refresh_token', data.refresh_token);
sessionStorage.removeItem('oauth_code_verifier');
sessionStorage.removeItem('oauth_state');
Silent Token Exchange (Alternative)
Skip the redirect-based callback with POST /api/oauth/web/exchange:
POST {ONEAUTH_API_URL}/api/oauth/web/exchange
Cookie: ory_kratos_session=...
{
"client_id": "your-client-id",
"redirect_uri": "http://your-app.com/callback",
"scope": "openid profile email offline",
"code_verifier": "your-pkce-verifier"
}
Returns tokens directly. Requires same-origin Kratos session cookie.
Token Management
Refresh Token
POST {ONEAUTH_API_URL}/api/oauth/refresh
{ "refresh_token": "ory_rt_..." }
Verify Token
POST {ONEAUTH_API_URL}/oauth/token/verify
{ "token": "ory_at_..." }
Returns { "active": true, "sub": "...", "exp": ..., ... }.
Logout (Two-Step, Recommended)
async function logout() {
// 1. Revoke OAuth2 tokens
await fetch(`${ONEAUTH_API_URL}/api/oauth/logout`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
access_token: localStorage.getItem('access_token'),
refresh_token: localStorage.getItem('refresh_token')
})
});
// 2. Clear local storage
localStorage.removeItem('access_token');
localStorage.removeItem('refresh_token');
// 3. Redirect to revoke Kratos session
window.location.href = `${ONEAUTH_API_URL}/api/auth/logout?post_logout_redirect_uri=${encodeURIComponent(window.location.origin)}`;
}
Using Standard OIDC Libraries
import { UserManager } from 'oidc-client-ts';
const settings = {
authority: ONEAUTH_API_URL, // e.g. 'https://auth.example.com'
client_id: 'your-client-id',
redirect_uri: 'http://your-app.com/callback',
response_type: 'code',
scope: 'openid profile email offline',
};
const userManager = new UserManager(settings);
Login/Register UI Customization
Login page parameters
{ONEAUTH_WEB_URL}/login?
login_challenge={challenge}& // OAuth2 flow (required for OAuth2)
return_to={url}& // Post-login redirect (non-OAuth2)
login_methods=password,phone,email& // Enabled methods
enable_social=true& // Show social login buttons
default_method=password& // Default selected tab
mode=normal // normal | popup
Register page parameters
{ONEAUTH_WEB_URL}/register?
return_to={url}&
verify_method=email& // email | phone | both
confirm_password=true&
require_username=false&
require_name=false&
email={prefill}&
phone={prefill}
Parameters auto-transfer between login and register pages.
return_to vs redirect_uri
redirect_uri: OAuth2 standard callback URL for receiving authorization codereturn_to: OneAuth custom param for post-login redirect (non-OAuth2 or after OAuth2 completes)
PKCE Helper
function generateCodeVerifier() {
const array = new Uint8Array(32);
crypto.getRandomValues(array);
return base64URLEncode(array);
}
async function generateCodeChallenge(verifier) {
const data = new TextEncoder().encode(verifier);
const hash = await crypto.subtle.digest('SHA-256', data);
return base64URLEncode(new Uint8Array(hash));
}
function base64URLEncode(buffer) {
return btoa(String.fromCharCode(...buffer))
.replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
}
Hydra Client Registration
Register a new OAuth2 client for your app:
hydra create client \
--id your-app-id \
--name "Your App" \
--grant-type authorization_code,refresh_token \
--response-type code \
--scope openid,profile,email,offline \
--redirect-uri http://your-app.com/callback \
--token-endpoint-auth-method none \
--skip-consent
CLI Login
For headless/CLI tools, use the native CLI endpoint:
POST {ONEAUTH_API_URL}/oauth/native/cli
{
"client_id": "cli-tool",
"username": "user@example.com",
"password": "password",
"code_verifier": "pkce-verifier",
"scope": "openid profile offline"
}
Error Handling
| HTTP Status | Error Code Range | Description |
|---|---|---|
| 400 | 4001-4999 | Client request error |
| 401 | 2001-2999 | Authentication failed |
| 403 | 3001-3999 | Permission denied |
| 429 | - | Rate limited |
| 500 | 5001-5999 | Server error |
Security Checklist
- Always use PKCE for authorization code flow
- Use HTTPS in production
- Store tokens securely (prefer httpOnly cookies over localStorage)
- Implement automatic token refresh (refresh 1 min before expiry)
- Revoke all tokens on logout
- Validate
stateparameter to prevent CSRF - Never expose tokens in URLs or logs