OAuth 2.0 / OpenID Connect Server
Foodsharing provides an OAuth 2.0 authorization server with OpenID Connect support for authenticating users and accessing their data.
Features
- Authorization Code Grant with PKCE support
- OpenID Connect userinfo endpoint
- User consent management with "remember this choice" option
- JWT access tokens (1 hour) and refresh tokens (1 month)
- Region-based client restrictions
Scopes
openid- Required. Providessub(user ID) andpreferred_usernameprofile- Name, picture, locale (alwaysde-DE)email- Email address (always verified in foodsharing)regions- Arrays of region IDs:region_ids(member) andregion_ambassador(coordinator)
Endpoints
Authorization
GET|POST /oauth/authorize
https://foodsharing.network/oauth/authorize?
response_type=code&
client_id=YOUR_CLIENT_ID&
redirect_uri=https://your-app.com/callback&
scope=openid%20profile%20email&
state=RANDOM_STATE&
code_challenge=CHALLENGE&
code_challenge_method=S256Token
POST /oauth/token
Authorization code exchange:
curl -X POST https://foodsharing.network/oauth/token \
-d "grant_type=authorization_code" \
-d "client_id=YOUR_CLIENT_ID" \
-d "client_secret=YOUR_SECRET" \
-d "code=AUTH_CODE" \
-d "redirect_uri=https://your-app.com/callback" \
-d "code_verifier=VERIFIER"Refresh token:
curl -X POST https://foodsharing.network/oauth/token \
-d "grant_type=refresh_token" \
-d "client_id=YOUR_CLIENT_ID" \
-d "client_secret=YOUR_SECRET" \
-d "refresh_token=REFRESH_TOKEN"UserInfo
GET|POST /oauth/userinfo
curl https://foodsharing.network/oauth/userinfo \
-H "Authorization: Bearer ACCESS_TOKEN"Discovery
GET /.well-known/openid-configuration- Provider metadataGET /.well-known/jwks.json- JWT signing keys
Token Lifetimes
- Access tokens: 1 hour
- Refresh tokens: 1 month
- Authorization codes: 10 minutes
UserInfo Response Examples
Minimal (openid only):
{
"sub": "12345",
"preferred_username": "Max12345"
}With profile + email:
{
"sub": "12345",
"preferred_username": "Max12345",
"name": "Max Mustermann",
"given_name": "Max",
"family_name": "Mustermann",
"picture": "https://foodsharing.network/images/profile/12345.jpg",
"locale": "de-DE",
"email": "max@example.org",
"email_verified": true
}With regions:
{
"sub": "12345",
"preferred_username": "Max12345",
"name": "Max Mustermann",
"email": "max@example.org",
"email_verified": true,
"region_ids": ["741", "1567", "1566"],
"region_ambassador": ["1567"]
}Registering OAuth Clients
Go to /admin/oauthclients (Orga members only) and click "+ Neuen Client erstellen".
Required fields:
- Client-ID: Unique identifier (e.g.,
bluespice-wiki,mobile-app) - Name: Displayed to users during consent
- Confidential Client: ✅ for server apps (have secret), ❌ for browser/mobile apps (use PKCE)
- Redirect URIs: Exact callback URLs (one per line, must match exactly)
- Scopes: Select
openid+ any combination ofprofile,email,regions - Grant Types: Select
authorization_codeand optionallyrefresh_token
Optional:
- Required Regions: Restrict to users in specific regions (leave empty for global access)
After creating, save the client secret immediately - it won't be shown again.
Setup
Keys
OAuth keys are in keys/ directory (auto-generated on first run):
oauth-private.key- RSA private key for signing JWTsoauth-public.key- RSA public key for validationoauth-encryption.key- Encryption key (auto-generated)
To regenerate manually:
cd keys/
openssl genrsa -out oauth-private.key 4096
openssl rsa -in oauth-private.key -pubout -out oauth-public.key
chmod 600 oauth-private.key oauth-public.keyDatabase
./scripts/db-migrateBlueSpice Integration
BLUESPICE="true" ./scripts/startSecurity Notes
- Always HTTPS in production (required)
- PKCE required for public clients, strongly recommended for all
- Validate
stateparameter to prevent CSRF - Redirect URIs must match exactly (including trailing slash)
- Store client secrets securely (environment variables, never in code)
- Access tokens are JWTs - validate signature and expiration
- Refresh tokens are encrypted - store securely, revoke on logout
Troubleshooting
"Invalid redirect_uri" - URI must match exactly (including protocol and trailing slash)
"Code challenge required" - Public clients must use PKCE (code_challenge + code_verifier)
"User not in required regions" - User must be member of at least one region specified in client settings
"Invalid or expired code" - Authorization codes expire in 10 minutes and can only be used once
References
- OAuth 2.0 RFC 6749
- PKCE RFC 7636
- OpenID Connect Core
- OAuth 2.0 Playground (for testing)
- JWT.io (decode tokens)