Base URL: https://oauth.luna99.it
Authentication backend: LDAP (dc=luna,dc=local) — Login format: uid@domain
GET|POST /oauth/validate HTTP Basic Auth - validate credentials
GET /oauth/authorize OAuth2 Authorization Code - show login, return code
POST /oauth/token OAuth2 - exchange code or refresh token for tokens
GET /oauth/userinfo OAuth2 - get authenticated user profile
POST /oauth/revoke OAuth2 - revoke an access or refresh token
GET /health Health check (returns {"status":"ok"})
For services that need direct credential validation without the OAuth redirect flow.
GET /oauth/validate
Authorization: Basic base64(uid@domain:password)
Response 200 (valid):
{
"sub": "uid=john,ou=users,o=acme,ou=tenants,dc=luna,dc=local",
"username": "john",
"email": "john@acme.it",
"name": "John",
"surname": "Doe",
"organization": "acme",
"domain": "acme.it",
"services": ["mail","sync","file"],
"active": true
}
Response 401 (invalid):
{"error": "invalid_credentials"}
WWW-Authenticate: Basic realm="Luna OAuth"
# cURL
curl -u user@domain.it:password https://ooauth.luna99.it/oauth/validate
# Nginx auth_request
location /protected {
auth_request /auth;
}
location = /auth {
proxy_pass https://oauth.luna99.it/oauth/validate;
proxy_set_header Authorization $http_authorization;
proxy_pass_request_body off;
proxy_set_header Content-Length "";
}
# Apache (mod_proxy + Basic Auth forwarding)
<Location /protected>
AuthType Basic
AuthName "Luna OAuth"
AuthBasicProvider proxy
AuthProxy https://oauth.luna99.it/oauth/validate
Require valid-user
</Location>
# HAProxy
http-request set-header X-Auth-Header %[req.hdr(Authorization)]
http-request lua.auth_basic_check # forward to /oauth/validate
# Perl (Mojo::UserAgent)
use MIME::Base64;
my $tx = $ua->get("https://oauth.luna99.it/oauth/validate" =>
{ Authorization => "Basic " . encode_base64("user:pass", "") });
if ($tx->res->code == 200) {
my $user = $tx->res->json; # { username, email, role, ... }
}
# Python (requests)
import requests
r = requests.get("https://oauth.luna99.it/oauth/validate",
auth=("username", "password"))
if r.status_code == 200:
user = r.json() # {"username": ..., "email": ..., "role": ...}
# Shell (wget)
wget -qO- --user=username --password=password https://oauth.luna99.it/oauth/validate
For web applications that need browser-based login with redirect.
GET /oauth/authorize
?response_type=code
&client_id=YOUR_CLIENT_ID
&redirect_uri=https://yourapp.example.com/callback
&state=RANDOM_CSRF_STRING
&scope=profile email
Parameters:
response_type required Must be "code"
client_id required Your registered client ID
redirect_uri required Must match registered URI exactly
state required Random string for CSRF protection
scope optional Space-separated: profile, email
On success, user is redirected to:
YOUR_REDIRECT_URI?code=AUTH_CODE&state=RANDOM_CSRF_STRING
POST /oauth/token
Content-Type: application/x-www-form-urlencoded
grant_type=authorization_code
&code=AUTH_CODE
&redirect_uri=https://yourapp.example.com/callback
&client_id=YOUR_CLIENT_ID
&client_secret=YOUR_CLIENT_SECRET
Response 200:
{
"access_token": "at-uuid",
"refresh_token": "rt-uuid",
"token_type": "Bearer",
"expires_in": 3600
}
Error 400: {"error": "invalid_grant"}
Error 401: {"error": "invalid_client"}
GET /oauth/userinfo
Authorization: Bearer ACCESS_TOKEN
Response 200:
{
"sub": "uid=john,ou=users,o=acme,ou=tenants,dc=luna,dc=local",
"username": "john",
"email": "john@acme.it",
"name": "John",
"surname": "Doe",
"organization": "acme",
"domain": "acme.it",
"services": ["mail","sync","file"],
"active": true
}
Error 401: {"error": "invalid_token"}
POST /oauth/token
Content-Type: application/x-www-form-urlencoded
grant_type=refresh_token
&refresh_token=REFRESH_TOKEN
&client_id=YOUR_CLIENT_ID
&client_secret=YOUR_CLIENT_SECRET
Returns new access_token and refresh_token (same format as step 2).
POST /oauth/revoke
Content-Type: application/x-www-form-urlencoded
token=ACCESS_OR_REFRESH_TOKEN
&token_type_hint=access_token (or "refresh_token")
Always returns 200 {"status":"ok"}
Authorization code 10 minutes single use
Access token 1 hour
Refresh token 24 hours
invalid_credentials Username or password wrong (Basic Auth)
missing_credentials No Authorization header sent (Basic Auth)
invalid_client Client ID or secret is wrong (OAuth)
invalid_grant Auth code or refresh token invalid/expired/used (OAuth)
unsupported_grant_type Only authorization_code and refresh_token supported
invalid_token Access token is invalid, expired, or revoked
To register a new OAuth client, contact the administrator or use the admin panel.
HTTP Basic Auth requires no client registration — any active user can authenticate.
Plain-text API spec (for AI agents and scripts)