- JavaScript 96.5%
- Dockerfile 3.5%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
| .env-example | ||
| .gitignore | ||
| docker-compose.yml | ||
| Dockerfile | ||
| LICENSE | ||
| package-lock.json | ||
| package.json | ||
| README.md | ||
| server.js | ||
discourse-oauth2-bridge
A lightweight Node.js bridge that allows Discourse to act as an OpenID Connect (OIDC) identity provider, enabling single sign-on for apps like Nextcloud, Immich, and others.
Discourse natively supports DiscourseConnect as an SSO provider, but most modern apps expect OIDC. This bridge translates between the two.
How it works
User → App (e.g. Nextcloud) → auth-bridge (OIDC) → Discourse (DiscourseConnect) → back to App
The bridge exposes a standards-compliant OIDC provider interface (discovery, authorization, token, userinfo, and JWKS endpoints) and handles the DiscourseConnect handshake with Discourse behind the scenes.
Claims exposed
| Claim | Source |
|---|---|
sub |
Discourse external_id |
preferred_username |
Discourse username |
name |
Discourse full name |
email |
Discourse email |
groups |
Discourse group memberships |
picture |
Discourse avatar URL |
Prerequisites
- Discourse with DiscourseConnect provider enabled (Admin → Settings →
enable discourse connect provider) - Docker and Docker Compose
- A subdomain for the bridge (e.g.
auth-bridge.yourdomain.org) with SSL
Deployment
1. Clone the repo
git clone https://git.digitallysovereign.org/tobias/discourse-oauth2-bridge /opt/discourse-auth-bridge
cd /opt/discourse-auth-bridge
2. Configure environment
cp .env.example .env
nano .env
DISCOURSE_URL=https://discourse.yourdomain.org
DISCOURSE_SECRET=your_shared_secret
BRIDGE_URL=https://auth-bridge.yourdomain.org
OAUTH2_CLIENT_ID=your_client_id
OAUTH2_CLIENT_SECRET=your_client_secret
PORT=3000
| Variable | Description |
|---|---|
DISCOURSE_URL |
Your Discourse instance URL |
DISCOURSE_SECRET |
Shared secret — must match discourse connect provider secrets in Discourse admin |
BRIDGE_URL |
Public URL of this bridge |
OAUTH2_CLIENT_ID |
Client ID your downstream app uses to connect to this bridge |
OAUTH2_CLIENT_SECRET |
Client secret your downstream app uses to connect to this bridge |
3. Configure Discourse
In Discourse Admin → Settings:
- Enable
enable discourse connect provider - Under
discourse connect provider secrets, add a row:- Secret: (your
DISCOURSE_SECRET) - Allowed return URLs:
https://auth-bridge.yourdomain.org/callback
- Secret: (your
4. Configure nginx
server {
listen 80;
server_name auth-bridge.yourdomain.org;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl http2;
server_name auth-bridge.yourdomain.org;
ssl_certificate /etc/letsencrypt/live/auth-bridge.yourdomain.org/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/auth-bridge.yourdomain.org/privkey.pem;
location / {
proxy_pass http://127.0.0.1:3000;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Real-IP $remote_addr;
}
}
5. Start the bridge
docker compose up -d
Verify:
curl https://auth-bridge.yourdomain.org/.well-known/openid-configuration
curl https://auth-bridge.yourdomain.org/jwks
6. Create a systemd service
cat > /etc/systemd/system/discourse-bridge.service << 'EOF'
[Unit]
Description=Discourse OAuth2 Bridge
After=docker.service
Requires=docker.service
[Service]
Type=oneshot
RemainAfterExit=yes
WorkingDirectory=/opt/discourse-auth-bridge
ExecStart=/usr/bin/docker compose up -d
ExecStop=/usr/bin/docker compose down
[Install]
WantedBy=multi-user.target
EOF
systemctl daemon-reload
systemctl enable discourse-bridge
Configuring Nextcloud user_oidc
Install the user_oidc app from the Nextcloud app store, then go to Admin Settings → OpenID Connect and register a new provider:
| Field | Value |
|---|---|
| Identifier | Discourse |
| Client ID | (your OAUTH2_CLIENT_ID) |
| Client Secret | (your OAUTH2_CLIENT_SECRET) |
| Discovery endpoint | https://auth-bridge.yourdomain.org/.well-known/openid-configuration |
| Scope | openid profile email |
| Extra claims | groups |
Attribute mapping:
| Field | Value |
|---|---|
| User ID mapping | preferred_username |
| Display name mapping | name |
| Email mapping | email |
| Groups mapping | groups |
Access control settings:
- ☑️ Turn off hashed user ID (use plain text so Discourse username = Nextcloud username)
- ☑️ Enable groups syncing
- ☑️ Enable Allow API/WebDAV with OIDC token (required for desktop and mobile clients)
- ☑️ Enable Fetch from userinfo endpoint
To make Nextcloud redirect automatically to Discourse on login:
php occ config:app:set user_oidc allow_multiple_user_backends --value=0
⚠️ This disables local password login. Make sure you have an emergency admin access method before enabling.
Troubleshooting
| Symptom | Cause | Fix |
|---|---|---|
Missing fields: jwks_uri in Nextcloud |
Old bridge version without JWKS support | Update to latest version |
Authentication failed due to missing secret |
DISCOURSE_SECRET mismatch |
Check Discourse admin settings match .env exactly; restart bridge after .env changes with docker compose up -d --force-recreate |
| Nextcloud username is not Discourse username | User ID mapping wrong | Set User ID mapping to preferred_username in user_oidc provider settings |
| Admin group lost after login | No admin group in Discourse |
Create an admin group in Discourse and add yourself to it |
| Immich OAuth login fails after Nextcloud username rename | Stale oauthId in Immich database |
Update oauthId in Immich's user table to match new username |
Notes
- The RSA key pair used to sign ID tokens is generated fresh on each container restart. Since tokens are short-lived (1 hour) this is fine in practice.
- The bridge supports a single OIDC client. If you need multiple downstream apps, each should connect via Nextcloud's own OIDC Identity Provider app, using this bridge only for the Discourse → Nextcloud leg.
Architecture with Nextcloud as hub
Discourse (DiscourseConnect provider)
│
▼
auth-bridge (this repo — translates to OIDC)
│
▼
Nextcloud (user_oidc consumer + OIDC Identity Provider)
│
├──▶ Immich
├──▶ Forgejo
└──▶ (any other OIDC app)