518 lines
11 KiB
Markdown
518 lines
11 KiB
Markdown
# API Reference
|
|
|
|
## Base URL
|
|
|
|
Versioned identity API:
|
|
```
|
|
https://id.example.com/v1/
|
|
```
|
|
|
|
OAuth 2.0 / OIDC protocol endpoints live at the API root (not under `/v1/`):
|
|
```
|
|
https://id.example.com/oauth/authorize
|
|
https://id.example.com/oauth/token
|
|
https://id.example.com/oauth/jwks
|
|
https://id.example.com/.well-known/openid-configuration
|
|
```
|
|
|
|
## Authentication
|
|
|
|
All endpoints (except auth, oauth, discovery) require:
|
|
```
|
|
Authorization: Bearer <access_token>
|
|
```
|
|
|
|
Access tokens are JWTs signed with **HS256** (HMAC) using the platform
|
|
`JWT_SIGNING_KEY`. They are verified by a shared secret, not via JWKS — the
|
|
JWKS endpoint (`/oauth/jwks`) currently returns an empty key set.
|
|
|
|
## Pagination
|
|
|
|
List endpoints return:
|
|
```json
|
|
{
|
|
"count": 123,
|
|
"next": "https://id.example.com/api/v1/endpoint?page=2",
|
|
"previous": null,
|
|
"results": [...]
|
|
}
|
|
```
|
|
|
|
Default page size: 20. Override with `?page_size=50` (max 100).
|
|
|
|
## Filtering & Search
|
|
|
|
| Param | Example |
|
|
|-------|---------|
|
|
| `search` | `?search=john` (icontains on name/email) |
|
|
| `ordering` | `?ordering=-created_at` (prefix `-` for desc) |
|
|
| `field=value` | `?status=active` (exact match) |
|
|
|
|
## Error Format
|
|
|
|
```json
|
|
{
|
|
"detail": "Human-readable message",
|
|
"code": "ERROR_CODE",
|
|
"fields": {
|
|
"email": ["Enter a valid email."]
|
|
}
|
|
}
|
|
```
|
|
|
|
HTTP status codes: `200`, `201`, `400`, `401`, `403`, `404`, `429`, `500`.
|
|
|
|
---
|
|
|
|
## Authentication
|
|
|
|
### POST `/auth/login`
|
|
Obtain token pair.
|
|
|
|
**Request:**
|
|
```json
|
|
{
|
|
"email": "user@acme.com",
|
|
"password": "secret123"
|
|
}
|
|
```
|
|
|
|
**Response (200):**
|
|
```json
|
|
{
|
|
"access": "eyJ...",
|
|
"refresh": "eyJ...",
|
|
"token_type": "bearer",
|
|
"expires_in": 900
|
|
}
|
|
```
|
|
|
|
### POST `/auth/refresh`
|
|
Rotate access token.
|
|
|
|
**Request:**
|
|
```json
|
|
{
|
|
"refresh": "eyJ..."
|
|
}
|
|
```
|
|
|
|
**Response (200):**
|
|
```json
|
|
{
|
|
"access": "eyJ...",
|
|
"refresh": "eyJ...", // rotated
|
|
"token_type": "bearer",
|
|
"expires_in": 900
|
|
}
|
|
```
|
|
|
|
### POST `/auth/logout`
|
|
Revoke session + refresh token.
|
|
|
|
**Request:**
|
|
```json
|
|
{
|
|
"refresh": "eyJ..." // optional; if omitted, revokes current session only
|
|
}
|
|
```
|
|
|
|
**Response (204):** No content.
|
|
|
|
---
|
|
|
|
## Users
|
|
|
|
### GET `/users/me`
|
|
Current authenticated user.
|
|
|
|
**Response (200):**
|
|
```json
|
|
{
|
|
"id": "uuid",
|
|
"email": "user@acme.com",
|
|
"phone": "+989123456789",
|
|
"first_name": "John",
|
|
"last_name": "Doe",
|
|
"is_active": true,
|
|
"is_staff": false,
|
|
"date_joined": "2024-01-15T10:30:00Z",
|
|
"last_login": "2024-01-20T08:15:00Z",
|
|
"organizations": [
|
|
{ "id": "uuid", "name": "Acme Corp", "role": "owner" }
|
|
]
|
|
}
|
|
```
|
|
|
|
### GET `/users/{id}`
|
|
Admin only. User detail by UUID.
|
|
|
|
---
|
|
|
|
## Organizations
|
|
|
|
### GET `/organizations`
|
|
List organizations user belongs to.
|
|
|
|
**Response (200):**
|
|
```json
|
|
{
|
|
"count": 2,
|
|
"results": [
|
|
{
|
|
"id": "uuid",
|
|
"name": "Acme Corp",
|
|
"slug": "acme-corp",
|
|
"description": "Main organization",
|
|
"is_active": true,
|
|
"created_at": "2024-01-10T12:00:00Z",
|
|
"owner": { "id": "uuid", "email": "owner@acme.com" },
|
|
"member_count": 15,
|
|
"user_role": "owner"
|
|
}
|
|
]
|
|
}
|
|
```
|
|
|
|
### POST `/organizations`
|
|
Create organization (caller becomes owner).
|
|
|
|
**Request:**
|
|
```json
|
|
{
|
|
"name": "New Company",
|
|
"description": "Optional description"
|
|
}
|
|
```
|
|
|
|
**Response (201):** Organization object.
|
|
|
|
### GET `/organizations/{id}`
|
|
Organization detail.
|
|
|
|
### PATCH `/organizations/{id}`
|
|
Update (owner/admin only).
|
|
|
|
### DELETE `/organizations/{id}`
|
|
Delete (owner only).
|
|
|
|
---
|
|
|
|
## Memberships
|
|
|
|
### GET `/organizations/{org_id}/memberships`
|
|
List members of an organization.
|
|
|
|
**Response (200):**
|
|
```json
|
|
{
|
|
"count": 15,
|
|
"results": [
|
|
{
|
|
"id": "uuid",
|
|
"user": { "id": "uuid", "email": "user@acme.com", "name": "John Doe" },
|
|
"role": "member",
|
|
"status": "active",
|
|
"joined_at": "2024-01-15T10:30:00Z",
|
|
"invited_by": { "id": "uuid", "email": "owner@acme.com" }
|
|
}
|
|
]
|
|
}
|
|
```
|
|
|
|
### POST `/organizations/{org_id}/memberships`
|
|
Invite user (owner/admin).
|
|
|
|
**Request:**
|
|
```json
|
|
{
|
|
"email": "new@user.com",
|
|
"role": "member"
|
|
}
|
|
```
|
|
|
|
### PATCH `/organizations/{org_id}/memberships/{membership_id}`
|
|
Update role/status (owner/admin).
|
|
|
|
### DELETE `/organizations/{org_id}/memberships/{membership_id}`
|
|
Remove member (owner) or leave (self).
|
|
|
|
---
|
|
|
|
## Applications (OAuth Clients)
|
|
|
|
### GET `/applications`
|
|
List applications (admin).
|
|
|
|
**Response (200):**
|
|
```json
|
|
{
|
|
"count": 3,
|
|
"results": [
|
|
{
|
|
"id": "uuid",
|
|
"name": "Bermooda",
|
|
"client_id": "bermooda-client-id",
|
|
"client_type": "confidential",
|
|
"redirect_uris": ["https://bermooda.example.com/callback"],
|
|
"grant_types": ["authorization_code", "refresh_token"],
|
|
"scopes": ["openid", "profile", "email", "org:read"],
|
|
"is_active": true,
|
|
"created_at": "2024-01-10T12:00:00Z"
|
|
}
|
|
]
|
|
}
|
|
```
|
|
|
|
### POST `/applications`
|
|
Register new application.
|
|
|
|
**Request:**
|
|
```json
|
|
{
|
|
"name": "My App",
|
|
"client_type": "confidential",
|
|
"redirect_uris": ["https://app.example.com/callback"],
|
|
"grant_types": ["authorization_code", "refresh_token"],
|
|
"response_types": ["code"],
|
|
"scopes": ["openid", "profile", "email"]
|
|
}
|
|
```
|
|
|
|
**Response (201):** Application with `client_secret` (shown once).
|
|
|
|
### GET `/applications/{id}`
|
|
|
|
### PATCH `/applications/{id}`
|
|
|
|
### DELETE `/applications/{id}`
|
|
|
|
### POST `/applications/{id}/rotate-secret`
|
|
Rotate client secret.
|
|
|
|
---
|
|
|
|
## Sessions
|
|
|
|
### GET `/sessions`
|
|
List current user's active sessions.
|
|
|
|
**Response (200):**
|
|
```json
|
|
{
|
|
"count": 3,
|
|
"results": [
|
|
{
|
|
"id": "uuid",
|
|
"device_name": "Chrome on macOS",
|
|
"ip_address": "192.168.1.1",
|
|
"user_agent": "Mozilla/5.0...",
|
|
"type": "browser",
|
|
"status": "active",
|
|
"created_at": "2024-01-15T10:30:00Z",
|
|
"last_activity": "2024-01-20T08:15:00Z",
|
|
"current": true
|
|
}
|
|
]
|
|
}
|
|
```
|
|
|
|
### DELETE `/sessions/{id}`
|
|
Revoke session (also revokes its refresh token).
|
|
|
|
**Response (204):** No content.
|
|
|
|
---
|
|
|
|
## Security Events
|
|
|
|
### GET `/events`
|
|
List security events (admin, or user's own events).
|
|
|
|
**Query params:** `user_id`, `event_type`, `severity`, `date_from`, `date_to`
|
|
|
|
**Response (200):**
|
|
```json
|
|
{
|
|
"count": 50,
|
|
"results": [
|
|
{
|
|
"id": "uuid",
|
|
"user": { "id": "uuid", "email": "user@acme.com" },
|
|
"event_type": "login_success",
|
|
"severity": "low",
|
|
"ip_address": "192.168.1.1",
|
|
"user_agent": "Mozilla/5.0...",
|
|
"metadata": { "session_id": "uuid" },
|
|
"created_at": "2024-01-20T08:15:00Z"
|
|
}
|
|
]
|
|
}
|
|
```
|
|
|
|
**Event Types:**
|
|
- `login_success`, `login_failed`
|
|
- `logout`, `token_refreshed`
|
|
- `session_revoked`, `password_changed`
|
|
- `mfa_changed`, `mfa_enrolled`, `mfa_removed`
|
|
- `suspicious_activity`, `brute_force_detected`
|
|
|
|
**Severity:** `low`, `medium`, `high`, `critical`
|
|
|
|
---
|
|
|
|
## OAuth 2.0 / OIDC
|
|
|
|
Authorization Code + Refresh Token grants with PKCE support.
|
|
|
|
### GET `/oauth/authorize`
|
|
Start the authorization code flow.
|
|
|
|
**Query parameters:**
|
|
|
|
| Parameter | Required | Description |
|
|
|-----------|----------|-------------|
|
|
| `client_id` | yes | Application (OAuth client) identifier |
|
|
| `response_type` | yes | Must be `code` |
|
|
| `redirect_uri` | yes | Must match a registered redirect URI for the client |
|
|
| `scope` | yes | Space-delimited scopes (e.g. `openid profile`) |
|
|
| `state` | no | Opaque value echoed back to the client |
|
|
| `nonce` | no | Binds authorisation to the token |
|
|
| `code_challenge` | no | PKCE code challenge |
|
|
| `code_challenge_method` | no | `S256` (default `plain`) |
|
|
| `token` | no | Bearer JWT access token may be passed as a query parameter instead of a header |
|
|
|
|
If the user is not authenticated (or inactive), responds `401` with a `login_url`.
|
|
Otherwise redirects (`302`) to the registered `redirect_uri` with `code` + optional `state`:
|
|
```
|
|
https://bermooda.com/callback?code=<raw_code>&state=abc123
|
|
```
|
|
On validation failure (invalid `client_id`, `redirect_uri` mismatch, unsupported
|
|
`response_type`), responds `400` with `error` (`unauthorized_client`,
|
|
`invalid_request`, `unsupported_response_type`, `invalid_scope`).
|
|
|
|
### POST `/oauth/token`
|
|
Exchange an authorization code for tokens, or refresh an access token.
|
|
|
|
**Content-Type:** `application/x-www-form-urlencoded`
|
|
|
|
**Grant: `authorization_code`**
|
|
```
|
|
grant_type=authorization_code
|
|
code=<raw_code>
|
|
redirect_uri=https://bermooda.com/callback
|
|
client_id=<client_id>
|
|
client_secret=<client_secret>
|
|
code_verifier=<pkce_verifier> # required only if code_challenge was used
|
|
```
|
|
|
|
**Grant: `refresh_token`**
|
|
```
|
|
grant_type=refresh_token
|
|
refresh_token=<refresh_token>
|
|
client_secret=<client_secret>
|
|
```
|
|
|
|
**Response (200):**
|
|
```json
|
|
{
|
|
"access_token": "eyJ...",
|
|
"token_type": "Bearer",
|
|
"expires_in": 900,
|
|
"refresh_token": "<raw_refresh_token>", // rotated on refresh
|
|
"scope": "openid profile"
|
|
}
|
|
```
|
|
|
|
Authorization codes are single-use. Refresh tokens are rotated on each use and
|
|
the previous refresh token is revoked.
|
|
|
|
Errors: `400 invalid_client`, `400 invalid_grant`, `400 invalid_request`,
|
|
`401 invalid_client`, `400 unsupported_grant_type`.
|
|
|
|
### GET `/oauth/jwks`
|
|
Returns the JSON Web Key Set. Currently returns an empty key set `{"keys": []}`
|
|
because tokens are signed with HMAC (HS256), not an asymmetric key pair.
|
|
|
|
### GET `/oauth/scopes/`
|
|
Lists the non-system OAuth scopes available to applications.
|
|
|
|
---
|
|
|
|
## OIDC Discovery
|
|
|
|
### GET `/.well-known/openid-configuration`
|
|
Standard OpenID Provider configuration.
|
|
|
|
```json
|
|
{
|
|
"issuer": "https://id.example.com",
|
|
"authorization_endpoint": "https://id.example.com/oauth/authorize",
|
|
"token_endpoint": "https://id.example.com/oauth/token",
|
|
"jwks_uri": "https://id.example.com/oauth/jwks",
|
|
"scopes_supported": ["openid", "profile", "email"],
|
|
"response_types_supported": ["code"],
|
|
"response_modes_supported": ["query"],
|
|
"grant_types_supported": ["authorization_code", "refresh_token"],
|
|
"subject_types_supported": ["public"],
|
|
"token_endpoint_auth_methods_supported": ["client_secret_post"],
|
|
"id_token_signing_alg_values_supported": ["HS256"],
|
|
"claims_supported": ["sub", "name", "email", "email_verified", "preferred_username", "locale", "picture"],
|
|
"code_challenge_methods_supported": ["S256", "plain"]
|
|
}
|
|
```
|
|
|
|
## Admin Endpoints
|
|
|
|
### GET `/admin/users/` — List all users (staff)
|
|
### GET `/admin/organizations/` — All organizations
|
|
### GET `/admin/applications/` — All OAuth clients
|
|
### GET `/admin/security/events/` — Full audit log
|
|
|
|
Access: Django admin or staff API token.
|
|
|
|
---
|
|
|
|
## Rate Limits
|
|
|
|
| Scope | Limit |
|
|
|-------|-------|
|
|
| Per-user (authenticated) | 100 req/min |
|
|
| Per-IP (unauthenticated) | 20 req/min |
|
|
| `/auth/login` | 5/min/IP, 10/min/user |
|
|
| `/auth/refresh` | 20/min/user |
|
|
|
|
Headers on 429:
|
|
```
|
|
Retry-After: 60
|
|
X-RateLimit-Limit: 100
|
|
X-RateLimit-Remaining: 0
|
|
```
|
|
|
|
---
|
|
|
|
## SDKs / Clients
|
|
|
|
| Language | Package |
|
|
|----------|---------|
|
|
| Python | `pip install identity-platform-sdk` (planned) |
|
|
| JavaScript | `npm install @identity-platform/client` (planned) |
|
|
| Go | `go get github.com/identity-platform/go-sdk` (planned) |
|
|
|
|
Until SDKs exist, use standard HTTP clients with the endpoints above.
|
|
|
|
---
|
|
|
|
## Changelog
|
|
|
|
| Version | Date | Changes |
|
|
|---------|------|---------|
|
|
| v1.0.0 | 2024-01 | Initial release: auth, users, orgs, memberships, apps, sessions, events, OIDC discovery |
|
|
|
|
---
|
|
|
|
## Support
|
|
|
|
- API Issues: Internal ticket system
|
|
- Security: `security@identity-platform.internal`
|
|
- Docs: `/api/docs/` (Swagger UI) |