This guide uses
lageas the example client. Sage Server acts as an OAuth2 authorization server and exposes the standard authorization code flow withrefresh_tokensupport.
OAuth2 Lage Integration Guide
Scope
When lage needs to reuse Sage sign-in, Sage can act as an OAuth2 authorization server and expose:
- authorization endpoint
- token endpoint
- userinfo endpoint
- authorization server metadata
lage only needs to follow the standard authorization code flow.
Recommended Endpoints
Assuming Sage Server is exposed at:
https://sage.example.com
Use these paths:
- metadata:
GET https://sage.example.com/.well-known/oauth-authorization-server - authorization:
GET https://sage.example.com/oauth2/authorize - token:
POST https://sage.example.com/oauth2/token - userinfo:
GET https://sage.example.com/oauth2/userinfo
Notes:
- Sage still keeps legacy compatibility routes under
/api/oauth2/*, but new integrations should use/oauth2/* - Sage’s own login and upstream identity integration still use
/api/auth/* - If Sage also sits behind an upstream OIDC provider,
lagedoes not need to know; it still talks only to Sage’s OAuth2 endpoints
1. Server Setup
1.1 Register the lage client
Register the client through SAGE_OAUTH2_CLIENTS:
[
{
"client_id": "lage",
"name": "Lage",
"description": "Lage Web OAuth2 Login",
"client_secret": "replace-with-a-strong-secret",
"redirect_uris": [
"https://lage.example.com/oauth/callback"
],
"grant_types": [
"authorization_code",
"refresh_token"
],
"response_types": [
"code"
],
"scope": "openid profile email",
"token_endpoint_auth_method": "client_secret_basic",
"skip_consent": true,
"enabled": true
}
]
Recommended defaults:
- keep
client_idaslage - use
client_secret_basicwhen the secret can be stored safely - make sure
redirect_urisexactly match the client callback URL - start with
openid profile email - if
lagecannot store a secret safely, use PKCE with a public client
1.2 Optional settings
export SAGE_OAUTH2_ISSUER="https://sage.example.com/oauth2"
export SAGE_OAUTH2_ACCESS_TOKEN_EXPIRES_IN=3600
1.3 Validate the metadata endpoint
curl https://sage.example.com/.well-known/oauth-authorization-server
Expected fields include:
- issuer
- authorization_endpoint
- token_endpoint
- userinfo_endpoint
- grant_types_supported
- response_types_supported
- scopes_supported
2. Client-Side Configuration
lage needs at least:
client_idclient_secretauthorization_urltoken_urluserinfo_urlredirect_uriscope
If metadata discovery is supported, the metadata URL can be used directly.
3. Authorization Code Flow
Step 1: Redirect to authorization
GET https://sage.example.com/oauth2/authorize
?response_type=code
&client_id=lage
&redirect_uri=https%3A%2F%2Flage.example.com%2Foauth%2Fcallback
&scope=openid%20profile%20email
&state=lage-random-state
If PKCE is enabled, add:
&code_challenge=YOUR_CODE_CHALLENGE
&code_challenge_method=S256
Step 2: Callback to Lage
After login, Sage redirects to the callback with code and state.
Step 3: Exchange code for token
curl -X POST https://sage.example.com/oauth2/token \
-u "lage:replace-with-a-strong-secret" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=authorization_code" \
-d "code=AUTH_CODE" \
-d "redirect_uri=https://lage.example.com/oauth/callback"
Add code_verifier when using PKCE.
Step 4: Read user info
curl https://sage.example.com/oauth2/userinfo \
-H "Authorization: Bearer ACCESS_TOKEN"
4. Security Notes
- Prefer HTTPS everywhere.
- Keep the client secret on the server side only.
- Use PKCE whenever the client cannot protect a secret.
- Keep redirect URIs exact and bounded.
5. Common Failure Points
- metadata endpoint unreachable
- redirect URI mismatch
- client secret mismatch
- authorization code expired
- token request missing PKCE verifier
- user has not logged in to Sage yet