LUNA OAUTH - API SPECIFICATION Base URL: https://oauth.luna99.it Authentication backend: LDAP (dc=luna,dc=local) Login format: uid@domain (e.g. gabrieleg@cloud88.it) Plain-text API reference for programmatic consumption. ======================================== ENDPOINTS ======================================== GET|POST /oauth/validate HTTP Basic Auth - validate user credentials via LDAP GET /oauth/authorize OAuth2 - redirect user to login form POST /oauth/token OAuth2 - exchange code or refresh for tokens GET /oauth/userinfo OAuth2 - get user profile with Bearer token POST /oauth/revoke OAuth2 - revoke a token GET /health Returns {"status":"ok"} ======================================== 1. HTTP BASIC AUTH VALIDATION ======================================== Validate user credentials directly via LDAP bind. No OAuth client registration needed. REQUEST: GET /oauth/validate Authorization: Basic base64(uid@domain:password) RESPONSE 200 (valid credentials): { "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"} Header: WWW-Authenticate: Basic realm="Luna OAuth" RESPONSE 401 (no auth header): {"error": "missing_credentials"} Header: WWW-Authenticate: Basic realm="Luna OAuth" CURL EXAMPLE: curl -u user@domain.it:password https://oauth.luna99.it/oauth/validate NGINX AUTH_REQUEST EXAMPLE: 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 ""; } ======================================== 2. OAUTH 2.0 AUTHORIZATION CODE FLOW ======================================== Requires a registered OAuth client (client_id + client_secret + redirect_uri). Contact admin or use admin panel at /admin/clients to register. --- STEP 1: REDIRECT USER TO LOGIN --- REQUEST: GET /oauth/authorize?response_type=code&client_id=CLIENT_ID&redirect_uri=REDIRECT_URI&state=RANDOM&scope=profile+email PARAMETERS: response_type required "code" client_id required registered client ID redirect_uri required must match registered URI exactly state required random CSRF string (verify on callback) scope optional space-separated: profile, email RESULT: User sees login form, authenticates with uid@domain, then redirected to: REDIRECT_URI?code=AUTH_CODE&state=RANDOM --- STEP 2: EXCHANGE CODE FOR TOKENS --- REQUEST: POST /oauth/token Content-Type: application/x-www-form-urlencoded grant_type=authorization_code&code=AUTH_CODE&redirect_uri=REDIRECT_URI&client_id=CLIENT_ID&client_secret=CLIENT_SECRET RESPONSE 200: { "access_token": "uuid-string", "refresh_token": "uuid-string", "token_type": "Bearer", "expires_in": 3600 } ERRORS: 400 {"error": "invalid_grant"} code invalid, expired, or already used 401 {"error": "invalid_client"} client_id or client_secret wrong --- STEP 3: GET USER INFO --- REQUEST: 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 } ERRORS: 401 {"error": "invalid_token"} token invalid, expired, or revoked ======================================== 3. REFRESH TOKEN ======================================== REQUEST: POST /oauth/token Content-Type: application/x-www-form-urlencoded grant_type=refresh_token&refresh_token=REFRESH_TOKEN&client_id=CLIENT_ID&client_secret=CLIENT_SECRET RESPONSE 200: { "access_token": "new-uuid", "refresh_token": "new-uuid", "token_type": "Bearer", "expires_in": 3600 } ======================================== 4. REVOKE TOKEN ======================================== REQUEST: POST /oauth/revoke Content-Type: application/x-www-form-urlencoded token=ACCESS_OR_REFRESH_TOKEN&token_type_hint=access_token RESPONSE: always 200 {"status":"ok"} ======================================== 5. TOKEN LIFETIMES ======================================== Authorization code: 10 minutes (single use) Access token: 1 hour Refresh token: 24 hours ======================================== 6. ERROR CODES ======================================== missing_credentials No Authorization header (Basic Auth) invalid_credentials Wrong username or password (Basic Auth) invalid_client Wrong client_id or client_secret (OAuth) invalid_grant Code or refresh token invalid/expired/used (OAuth) unsupported_grant_type Only authorization_code and refresh_token invalid_token Access token invalid/expired/revoked (userinfo) ======================================== 7. QUICK REFERENCE - CURL ======================================== # Basic Auth validation curl -u user@domain.it:pass https://oauth.luna99.it/oauth/validate # OAuth step 1 - open in browser open "https://oauth.luna99.it/oauth/authorize?response_type=code&client_id=ID&redirect_uri=URI&state=xyz&scope=profile+email" # OAuth step 2 - exchange code curl -X POST https://oauth.luna99.it/oauth/token -d "grant_type=authorization_code&code=CODE&redirect_uri=URI&client_id=ID&client_secret=SECRET" # OAuth step 3 - user info curl -H "Authorization: Bearer TOKEN" https://oauth.luna99.it/oauth/userinfo # Refresh curl -X POST https://oauth.luna99.it/oauth/token -d "grant_type=refresh_token&refresh_token=RT&client_id=ID&client_secret=SECRET" # Revoke curl -X POST https://oauth.luna99.it/oauth/revoke -d "token=TOKEN" # Health check curl https://oauth.luna99.it/health