Developer Documentation
Everything you need to integrate rIDP into your applications. rIDP is a self-hosted OAuth 2.0 authentication server designed for simplicity and privacy.
Getting Started
First, create a new application in the developer portal to get your OAuth credentials. Navigate to Applications and click "Create New".
After creating your application, you'll receive:
- Client ID — Public identifier for your application
- Client Secret — Keep this confidential! Never expose it in client-side code.
When creating your application, you must specify the redirect URI(s) where users will be sent after authentication. This must exactly match the URI you use in your OAuth requests.
Request the following scopes to access user information:
| Scope | Description |
|---|---|
openid | Returns the user's unique identifier (sub). |
profile | Access to user's name and profile information. |
email | Access to user's email address. |
Add a "Login with rIDP" button that redirects users to the authorization endpoint:
const authUrl = `https://your-idp.com/oauth/authorize?` +
`response_type=code&` +
`client_id=YOUR_CLIENT_ID&` +
`redirect_uri=${encodeURIComponent('https://yourapp.com/callback')}&` +
`scope=openid profile email&` +
`state=${generateRandomState()}`;
window.location.href = authUrl;OAuth 2.0 Authorization Flow
The most secure flow for web applications with server-side code.
- 1 Redirect to Authorization Endpoint
GET /oauth/authorize - 2 User Authenticates and Consents
User logs in and approves your application's access request.
- 3 Receive Authorization Code
User is redirected back to your callback URL with a temporary code.
- 4 Exchange Code for Tokens
POST /oauth/token - 5 Use Access Token
Make API calls (e.g., to UserInfo) with the access token.
Security Note
Never expose your Client Secret in client-side code. For Single Page Applications, use PKCE (Proof Key for Code Exchange).
API Reference
/oauth/authorizeInitiates the OAuth 2.0 authorization flow.
| Parameter | Required | Description |
|---|---|---|
| response_type | Yes | Must be code |
| client_id | Yes | Your application's Client ID |
| redirect_uri | Yes | Registered callback URL |
| scope | No | Space-separated list of scopes |
| state | Yes | CSRF protection token |
| resource | No | Resource Indicators per RFC 8707 |
/oauth/tokenExchange authorization code for access tokens, or refresh an existing token.
Content-Type: application/x-www-form-urlencoded
Supported Grant Types
authorization_code— Exchange authorization code for tokensrefresh_token— Get new access token using refresh token
| Parameter | Required | Description |
|---|---|---|
| grant_type | Yes | authorization_code or refresh_token |
| client_id | Yes | Your application's Client ID |
| client_secret | Conditional | Required for confidential clients |
| code | Conditional | Required for authorization_code grant |
| redirect_uri | Conditional | Must match initial request |
| refresh_token | Conditional | Required for refresh_token grant |
Response
{
"access_token": "eyJ...",
"token_type": "Bearer",
"expires_in": 3600,
"refresh_token": "...",
"scope": "openid profile email"
}/oauth/userinfoRetrieve information about the authenticated user.
Headers
| Header | Required | Description |
|---|---|---|
| Authorization | Yes | Bearer {access_token} |
Response (200 OK)
{
"sub": "6d9e1f2a-3e3b-4eae-bf8a-9f8b6de51577",
"email": "user@example.com"
}Error Responses
| Status | Error | Description |
|---|---|---|
| 401 | invalid_token | Access token is missing, invalid, or expired |
| 403 | insufficient_scope | Access token lacks required "openid" scope |
Self-Hosting
The easiest way to run rIDP is using Docker Compose. Add the
following service to your docker-compose.yml file:
services:
ridp:
image: ridp/server:latest
ports:
- "8000:8000"
environment:
- SECRET_KEY=change-this-to-a-secure-random-string
- ALLOWED_HOSTS=localhost,yourdomain.com
- DB_NAME=ridp
- DB_USER=ridp
- DB_PASSWORD=securepassword
- DB_HOST=db
depends_on:
- db
db:
image: postgres:17
environment:
- POSTGRES_DB=ridp
- POSTGRES_USER=ridp
- POSTGRES_PASSWORD=securepassword
volumes:
- ridp_data:/var/lib/postgresql/data
volumes:
ridp_data:Superuser Setup
After starting the container, you may need to create a superuser to access the admin panel. You can do this by executing commands directly in the running container.
List Users
docker-compose exec ridp python manage.py shell -c "from portal_auth.models import User; [print(f'{u.id} | {u.email}') for u in User.objects.all()]"Promote User to Superuser
Replace YOUR_EMAIL with the email of the user you want to promote.
docker-compose exec ridp python manage.py shell -c "from portal_auth.models import User; u = User.objects.get(email='YOUR_EMAIL'); u.is_staff = True; u.is_superuser = True; u.save(); print('Promoted!')"Code Examples
Express.js Complete Example
import express from 'express';
import session from 'express-session';
import crypto from 'crypto';
const app = express();
const CLIENT_ID = 'your-client-id';
const CLIENT_SECRET = 'your-client-secret';
const REDIRECT_URI = 'https://yourapp.com/callback';
const IDP_BASE_URL = 'https://your-idp.com';
app.use(session({
secret: 'your-session-secret',
resave: false,
saveUninitialized: false
}));
// Step 1: Redirect to rIDP login
app.get('/login', (req, res) => {
const state = crypto.randomUUID();
req.session.oauth_state = state;
const params = new URLSearchParams({
response_type: 'code',
client_id: CLIENT_ID,
redirect_uri: REDIRECT_URI,
scope: 'openid profile email',
state: state
});
res.redirect(`${IDP_BASE_URL}/oauth/authorize?${params}`);
});
// Step 2: Handle callback
app.get('/callback', async (req, res) => {
const { code, state } = req.query;
// Verify state matches
if (state !== req.session.oauth_state) {
return res.status(400).send('Invalid state');
}
// Exchange code for tokens
const tokenResponse = await fetch(`${IDP_BASE_URL}/oauth/token`, {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: new URLSearchParams({
grant_type: 'authorization_code',
code: code,
client_id: CLIENT_ID,
client_secret: CLIENT_SECRET,
redirect_uri: REDIRECT_URI
})
});
const tokens = await tokenResponse.json();
// Get user info
const userResponse = await fetch(`${IDP_BASE_URL}/oauth/userinfo`, {
headers: { 'Authorization': `Bearer ${tokens.access_token}` }
});
const user = await userResponse.json();
req.session.user = user;
res.redirect('/dashboard');
});