AcadeDoc/docs/security.md
Corentin 5b512e6324 feat(acadedoc): replace EE Settings with open source UI (audit log, API keys, OIDC status) — R4.5
Server (NestJS):
- AcadeniceAuditLogModule: GET /api/acadenice/audit-log (admin/owner, Kysely, paginated + filtered)
- AcadeniceApiKeysModule: GET/POST/DELETE /api/acadenice/api-keys (JWT, bcrypt hash, acdk_ prefix)
- AcadeniceSecurityModule: GET /api/acadenice/security/oidc-status (admin, no secrets exposed)
- Migration 20260510T100000: acadenice_api_key table with token_hash + bcrypt
- Permissions catalog: added audit_log:read

Client (React 18 + Mantine v7):
- Audit log page: paginated table with filters (event, userId, date range)
- API keys page: list/create/revoke personal tokens, one-time display modal
- Security/OIDC status page: read-only, env-var config reference
- Sidebar rewired: Security & SSO, API keys, Audit log -> acadenice/* routes (no EE feature gates)
- Prefetch functions for new routes

Tests: 36 server (Jest) + client typecheck clean

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-08 12:24:00 +02:00

4.7 KiB

Security — AcadeDoc

AcadeDoc security is configured server-side via environment variables. This document covers OIDC single sign-on, personal API keys, and the audit log.

OIDC Single Sign-On

OIDC lets users log in via an external identity provider (Authentik, Keycloak, Auth0, etc.) using the OpenID Connect protocol.

Environment variables

Variable Required Default Description
OIDC_ENABLED yes false Set to true to activate OIDC
OIDC_ISSUER yes Provider discovery URL (e.g. https://auth.example.com/realms/myrealm)
OIDC_CLIENT_ID yes OAuth2 client ID registered at the provider
OIDC_CLIENT_SECRET yes OAuth2 client secret (not returned by the status API — server-side only)
OIDC_REDIRECT_URI no auto Callback URL. Auto-detected if omitted: $APP_URL/api/auth/oidc/callback
OIDC_SCOPES no openid email profile Space-separated OAuth2 scopes
OIDC_PROVIDER_NAME no SSO Label shown on the login button
OIDC_AUTO_PROVISION no false Auto-create user account on first OIDC login
OIDC_DEFAULT_WORKSPACE_ID no Auto-join workspace UUID on provisioning

Authentik example

  1. Create an OAuth2/OIDC provider in Authentik with grant type Authorization Code.
  2. Set the redirect URI to https://yourdomain.com/api/auth/oidc/callback.
  3. Copy the client ID and secret from the provider page.
OIDC_ENABLED=true
OIDC_ISSUER=https://authentik.example.com/application/o/acadedoc/
OIDC_CLIENT_ID=acadedoc
OIDC_CLIENT_SECRET=<secret>
OIDC_PROVIDER_NAME=Authentik
OIDC_AUTO_PROVISION=true

Keycloak example

OIDC_ENABLED=true
OIDC_ISSUER=https://keycloak.example.com/realms/myrealm
OIDC_CLIENT_ID=acadedoc
OIDC_CLIENT_SECRET=<secret>
OIDC_PROVIDER_NAME=Keycloak
OIDC_SCOPES=openid email profile

Auth0 example

OIDC_ENABLED=true
OIDC_ISSUER=https://your-tenant.auth0.com/
OIDC_CLIENT_ID=<client_id>
OIDC_CLIENT_SECRET=<client_secret>
OIDC_PROVIDER_NAME=Auth0
OIDC_SCOPES=openid email profile

PKCE

AcadeDoc uses PKCE (Proof Key for Code Exchange, RFC 7636) by default. The code verifier is stored in a short-lived signed cookie and cleared immediately after the callback is consumed. No additional configuration is required.


Personal API Keys

Personal API keys allow programmatic access to the AcadeDoc API. Each key is scoped to the user account that created it.

Token format

Tokens are prefixed with acdk_ followed by 64 random hex characters. Example:

acdk_8f2a1b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a

Security

  • [CLAIM L1] Only a bcrypt hash is stored in the database (source: apps/server/src/core/acadenice/api-keys/services/api-key.service.tsbcrypt.hash(plain, BCRYPT_ROUNDS)). The plaintext is shown once at creation and not retained server-side.
  • Tokens are not included in server logs by design. Verify your log aggregator does not capture request bodies.
  • The acdk_ prefix allows ops to identify and redact tokens in log pipelines before forwarding.

Best practices

  • Rotate tokens every 90 days.
  • Use short-lived tokens (30 days) for CI/CD pipelines.
  • Do not commit tokens to source control. Use a secret manager (Vault, Doppler, GitHub Secrets).
  • Use separate tokens per application so you can revoke one without disrupting others.
  • Set expiresAt for all tokens except trusted long-term integrations.

RGPD / GDPR

Tokens grant full account access. When a user is deleted, all their tokens are cascade-deleted via the ON DELETE CASCADE foreign key on acadenice_api_key.user_id. No manual cleanup is required.


Audit Log

The audit log records workspace events for compliance and incident investigation.

What is logged

Event Trigger
page.created Page creation
page.updated Page edit
page.deleted Page deletion
space.created Space creation
space.deleted Space deletion
user.invited Invitation sent
user.deleted Member removed
workspace.updated Workspace settings changed

Each entry records: timestamp, actor (user email), event type, resource type and ID, IP address, and a JSONB diff of changes.

Retention

By default, audit entries are kept indefinitely. For GDPR compliance, configure a retention policy by purging rows older than your legal requirement:

-- Example: delete entries older than 365 days
DELETE FROM audit WHERE created_at < now() - interval '365 days';

A cron job or pg_cron task is recommended for automated purging.

Access control

Only workspace admins and owners can read the audit log (audit_log:read permission).