443 lines
14 KiB
Markdown
443 lines
14 KiB
Markdown
# NuQloud Identity Provider Guide
|
|
|
|
This guide explains how to operate NuQloud as an OpenID Connect identity
|
|
provider for private applications.
|
|
|
|
Use this guide when you want users to sign in to approved services such as
|
|
Gitea, Grafana, Wiki.js, OpenProject, Outline, or a custom internal app using
|
|
their NuQloud identity.
|
|
|
|
For the older Nextcloud-only `user_oidc` setup notes, see
|
|
`docs/nextcloud-oidc-setup.md`.
|
|
|
|
## What This Provides
|
|
|
|
NuQloud can act as the identity provider for other trusted applications.
|
|
|
|
The broker owns OIDC token issuance and client policy. Nextcloud remains the
|
|
admin surface where operators configure the provider, register clients, review
|
|
audit events, and control account access policy.
|
|
|
|
Supported provider endpoints:
|
|
|
|
- `/.well-known/openid-configuration`
|
|
- `/authorize`
|
|
- `/token`
|
|
- `/userinfo`
|
|
- `/jwks`
|
|
|
|
Supported client management:
|
|
|
|
- Create OIDC clients.
|
|
- Edit display name, redirect URIs, scopes, group rules, and PKCE policy.
|
|
- Disable or re-enable clients.
|
|
- Rotate client secrets.
|
|
- View recent identity-provider audit events.
|
|
- Use built-in setup templates for common apps.
|
|
|
|
Current limitations:
|
|
|
|
- Refresh tokens are not enabled.
|
|
- Token revocation is not implemented yet.
|
|
- Logout callbacks are not implemented yet.
|
|
- Redirect URI matching is exact.
|
|
- Client secrets are shown only when created or rotated.
|
|
|
|
## Architecture
|
|
|
|
NuQloud identity provider flow:
|
|
|
|
1. A private app redirects the user to the broker `/authorize` endpoint.
|
|
2. The broker validates the client, redirect URI, requested scopes, and PKCE
|
|
policy.
|
|
3. The user completes the NuQloud login flow.
|
|
4. The broker checks account policy and group policy server-side.
|
|
5. The broker issues an authorization code.
|
|
6. The app exchanges the code at `/token`.
|
|
7. The broker issues short-lived tokens and records audit events.
|
|
8. The app can call `/userinfo` for scoped profile claims.
|
|
|
|
Nextcloud app responsibilities:
|
|
|
|
- Admin UI for provider settings.
|
|
- Client registration UI.
|
|
- Client secret rotation.
|
|
- Audit log viewing.
|
|
- Invite and allowlist tools.
|
|
- Runtime config sync to the broker.
|
|
|
|
Broker responsibilities:
|
|
|
|
- Discovery metadata.
|
|
- OIDC authorization, token, userinfo, and JWKS endpoints.
|
|
- Client secret validation.
|
|
- Exact redirect URI enforcement.
|
|
- PKCE enforcement.
|
|
- Scope filtering.
|
|
- Group access enforcement.
|
|
- Token signing.
|
|
- Audit events.
|
|
|
|
## Built-In Setup Paths
|
|
|
|
The identity provider is wired into the standard setup paths.
|
|
|
|
For dev-prod Docker deployments:
|
|
|
|
- `start-devprod.sh` ensures `user_oidc` and `qortal_integration` are present,
|
|
then syncs identity-provider runtime config after the stack starts.
|
|
- `recreate-devprod.sh` enables the OIDC provider, configures Nextcloud app
|
|
runtime values, and verifies final sync.
|
|
- `scripts/finish-initial-setup.sh` enables the required apps, configures the
|
|
Nextcloud OIDC provider, syncs runtime settings, and records manual recovery
|
|
commands if something is not ready.
|
|
- `scripts/ensure-qortal-integration-runtime-config.sh` syncs env-backed values
|
|
into `qortal_integration` and the broker OIDC config.
|
|
|
|
For host-hybrid deployments:
|
|
|
|
- `scripts/install-host-nextcloud-hybrid.sh` generates sidecar broker env,
|
|
starts the broker, enables required host Nextcloud apps, configures
|
|
`user_oidc`, and syncs `qortal_integration` runtime config.
|
|
- `scripts/update-host-nextcloud-hybrid.sh` reapplies the host-hybrid installer.
|
|
- `scripts/bootstrap-host-nextcloud-hybrid.sh` writes the built-in provider env
|
|
defaults used by the sidecar broker.
|
|
|
|
Important env keys:
|
|
|
|
```bash
|
|
OIDC_ISSUER=https://qortalbroker.example.test
|
|
OIDC_CLIENT_ID=nextcloud-local
|
|
OIDC_CLIENT_SECRET=<secret>
|
|
OIDC_REDIRECT_URI_ALLOWLIST=https://cloud.example.test/apps/user_oidc/code
|
|
OIDC_POLICY_MODE=auto_provision
|
|
OIDC_AUTO_PROVISION_GUARD=invite_or_allowlist
|
|
OIDC_REQUIRE_EMAIL_FOR_NEW_ACCOUNT=true
|
|
OIDC_BRANDING_MODE=powered
|
|
OIDC_AUTH_REQUEST_TTL_SECONDS=600
|
|
OIDC_AUTH_CODE_TTL_SECONDS=120
|
|
OIDC_ACCESS_TOKEN_TTL_SECONDS=600
|
|
OIDC_ID_TOKEN_TTL_SECONDS=600
|
|
OIDC_PRIVATE_KEY_PEM=
|
|
OIDC_PRIVATE_KEY_PEM_B64=
|
|
OIDC_KEY_ID=
|
|
```
|
|
|
|
Use `scripts/ensure-oidc-signing-key.sh .env.devprod` to generate and validate
|
|
a stable signing key. The script stores the key as `OIDC_PRIVATE_KEY_PEM_B64`,
|
|
which is safer for single-line env files than raw PEM text. Raw
|
|
`OIDC_PRIVATE_KEY_PEM` remains supported, including escaped `\n` line breaks.
|
|
If the broker rotates signing keys unexpectedly, existing relying parties may
|
|
reject tokens until they refresh JWKS.
|
|
|
|
Existing installs without a configured key are stabilized the next time setup,
|
|
start, recreate, or host-hybrid install scripts run. If the broker was already
|
|
using an in-memory generated key, that private key cannot be recovered from env;
|
|
the generated env key becomes the stable key for future broker starts. Schedule
|
|
the first broker restart after stabilization during a low-impact window.
|
|
|
|
## First-Time Setup
|
|
|
|
Use the normal setup flow for the target deployment.
|
|
|
|
Dev-prod no-SSL:
|
|
|
|
```bash
|
|
./start-devprod.sh --mode nossl
|
|
./scripts/finish-initial-setup.sh --mode nossl --env-file .env.devprod
|
|
```
|
|
|
|
Dev-prod SSL:
|
|
|
|
```bash
|
|
./start-devprod.sh --mode ssl
|
|
./scripts/finish-initial-setup.sh --mode ssl --env-file .env.devprod
|
|
```
|
|
|
|
After setup, confirm the discovery document is reachable from the public broker
|
|
URL:
|
|
|
|
```bash
|
|
curl -fsS "$(grep -m1 '^OIDC_ISSUER=' .env.devprod | cut -d= -f2-)/.well-known/openid-configuration"
|
|
```
|
|
|
|
Confirm Nextcloud has the required apps enabled:
|
|
|
|
```bash
|
|
docker compose -f docker-compose.devprod.nossl.yml --env-file .env.devprod exec -T --user www-data app php occ app:list | rg 'qortal_integration|user_oidc'
|
|
```
|
|
|
|
For SSL mode, use `docker-compose.devprod.yml` with the same `.env.devprod`
|
|
file.
|
|
|
|
## Admin UI Workflow
|
|
|
|
Open Nextcloud Administration settings and select the Qortal Integration admin
|
|
section.
|
|
|
|
Before adding private apps:
|
|
|
|
1. Confirm broker connection succeeds.
|
|
2. Confirm OIDC issuer URL is public and stable.
|
|
3. Confirm the provider policy mode is correct.
|
|
4. Confirm the internal broker token matches between env and app config.
|
|
5. Confirm the Nextcloud `user_oidc` provider exists for the cloud login flow.
|
|
|
|
To register a private app:
|
|
|
|
1. Select a template.
|
|
2. Replace every example domain with the app's real public URL.
|
|
3. Confirm exact redirect URI values.
|
|
4. Choose allowed scopes.
|
|
5. Add allowed groups if access should be limited.
|
|
6. Enable PKCE for public/browser clients.
|
|
7. Create the client.
|
|
8. Copy the one-time secret immediately.
|
|
9. Add the issuer, client ID, and secret to the relying app.
|
|
10. Complete a sign-in and confirm an audit event appears.
|
|
|
|
The generated setup output in the admin UI shows the issuer, discovery URL,
|
|
client ID, scopes, auth method, and template-specific notes.
|
|
|
|
## Client Templates
|
|
|
|
Templates prefill common values only. Always replace placeholder domains and
|
|
callback paths with the real values from the relying app.
|
|
|
|
### Gitea
|
|
|
|
Template defaults:
|
|
|
|
```text
|
|
Client ID: gitea
|
|
Redirect URI: https://git.example.com/user/oauth2/NuQloud/callback
|
|
Scopes: openid profile email
|
|
```
|
|
|
|
Use a stable authentication source name in Gitea. That name becomes part of the
|
|
callback URL. Gitea can also be configured from the command line with
|
|
`gitea admin auth add-oauth`.
|
|
|
|
Official docs: https://docs.gitea.com/administration/command-line
|
|
|
|
### Grafana
|
|
|
|
Template defaults:
|
|
|
|
```text
|
|
Client ID: grafana
|
|
Redirect URI: https://grafana.example.com/login/generic_oauth
|
|
Scopes: openid profile email groups
|
|
```
|
|
|
|
Use Grafana generic OAuth settings. Configure auth URL, token URL, and API URL
|
|
from NuQloud discovery metadata.
|
|
|
|
Official docs:
|
|
https://grafana.com/docs/grafana/latest/setup-grafana/configure-access/configure-authentication/generic-oauth/
|
|
|
|
### Wiki.js
|
|
|
|
Template defaults:
|
|
|
|
```text
|
|
Client ID: wikijs
|
|
Redirect URI: https://wiki.example.com/login/<provider-callback>
|
|
Scopes: openid profile email groups
|
|
```
|
|
|
|
Copy the exact callback URL shown by Wiki.js into the NuQloud client.
|
|
|
|
Official docs: https://docs.requarks.io/auth
|
|
|
|
### OpenProject
|
|
|
|
Template defaults:
|
|
|
|
```text
|
|
Client ID: openproject
|
|
Redirect URI: https://projects.example.com/auth/<provider-slug>/callback
|
|
Scopes: openid profile email
|
|
```
|
|
|
|
Copy the exact redirect URI shown by OpenProject. Provider slugs can change the
|
|
path.
|
|
|
|
Official docs:
|
|
https://www.openproject.org/docs/system-admin-guide/authentication/openid-providers/
|
|
|
|
### Outline
|
|
|
|
Template defaults:
|
|
|
|
```text
|
|
Client ID: outline
|
|
Redirect URI: https://docs.example.com/auth/oidc.callback
|
|
Scopes: openid profile email
|
|
```
|
|
|
|
Configure Outline with the NuQloud issuer/discovery URL, client ID, and client
|
|
secret. Restart Outline after changing OIDC env values.
|
|
|
|
Compatibility note: NuQloud does not issue refresh tokens yet. If your Outline
|
|
version or deployment requires `offline_access` or refresh tokens, defer Outline
|
|
SSO until refresh-token support is added.
|
|
|
|
Official docs: https://docs.getoutline.com/s/hosting/doc/oidc-8CPBm6uC0I
|
|
|
|
### Generic OIDC App
|
|
|
|
Template defaults:
|
|
|
|
```text
|
|
Client ID: private-app
|
|
Redirect URI: https://app.example.com/oidc/callback
|
|
Scopes: openid profile email
|
|
PKCE: enabled
|
|
```
|
|
|
|
Use this for internal tools, desktop apps, and browser clients. Keep PKCE on for
|
|
public clients.
|
|
|
|
## Claims And Scopes
|
|
|
|
The broker releases only claims allowed by the registered client scopes.
|
|
|
|
Supported scopes:
|
|
|
|
- `openid`
|
|
- `profile`
|
|
- `email`
|
|
- `groups`
|
|
- `qortal_address`
|
|
|
|
Recommended defaults:
|
|
|
|
- Use `openid profile email` for most apps.
|
|
- Add `groups` only when the app needs group-based authorization.
|
|
- Add `qortal_address` only for apps that explicitly need Qortal identity data.
|
|
|
|
Powered Mode default:
|
|
|
|
- Use NuQloud-facing labels in the user experience.
|
|
- Do not expose Qortal-specific claims unless the client requires them.
|
|
|
|
Full Qortal Mode:
|
|
|
|
- Qortal-specific labels and claims are allowed when policy permits them.
|
|
|
|
Never rely on client-side filtering for access control. Enforce access using
|
|
the registered client's allowed groups and server-side policy.
|
|
|
|
## Security Checklist
|
|
|
|
Before calling an instance ready:
|
|
|
|
- Redirect URIs are exact HTTPS URLs for production apps.
|
|
- Client secrets are copied once and not stored in browser-visible notes.
|
|
- Disabled clients cannot authorize or exchange tokens.
|
|
- Secret rotation invalidates the old client secret.
|
|
- Public clients require PKCE.
|
|
- Allowed scopes are minimal.
|
|
- Allowed groups are set for restricted apps.
|
|
- `BROKER_INTERNAL_API_TOKEN` is present and matches app config.
|
|
- `OIDC_PRIVATE_KEY_PEM_B64` or `OIDC_PRIVATE_KEY_PEM` is set for production stability.
|
|
- Broker and Nextcloud public URLs match actual reverse-proxy routing.
|
|
- No secrets appear in client-side UI, public logs, or copied setup notes.
|
|
|
|
Admin endpoints are authenticated through the Nextcloud app and proxied to the
|
|
broker with the internal broker token. Do not expose broker admin APIs publicly
|
|
without the internal-token boundary.
|
|
|
|
## Recovery And Rollback
|
|
|
|
If a client is misconfigured:
|
|
|
|
1. Disable the client in the admin UI.
|
|
2. Fix redirect URIs, scopes, groups, or PKCE policy.
|
|
3. Re-enable the client.
|
|
4. Try a fresh sign-in.
|
|
|
|
If a client secret is lost:
|
|
|
|
1. Rotate the secret in the admin UI.
|
|
2. Copy the one-time secret.
|
|
3. Update the relying app.
|
|
4. Restart the relying app if it reads config only at boot.
|
|
|
|
If OIDC login to Nextcloud breaks:
|
|
|
|
1. Use an existing local Nextcloud admin session if available.
|
|
2. Verify `user_oidc` provider config.
|
|
3. Verify `OIDC_ISSUER`, `OIDC_CLIENT_ID`, and `OIDC_CLIENT_SECRET`.
|
|
4. Re-run setup sync:
|
|
|
|
```bash
|
|
./scripts/finish-initial-setup.sh --mode nossl --env-file .env.devprod
|
|
./scripts/ensure-qortal-integration-runtime-config.sh --compose-file docker-compose.devprod.nossl.yml --env-file .env.devprod
|
|
```
|
|
|
|
For SSL mode, use `--mode ssl` and `docker-compose.devprod.yml`.
|
|
|
|
If a relying app rejects tokens after broker restart:
|
|
|
|
1. Confirm the app can reach `/jwks`.
|
|
2. Confirm `OIDC_PRIVATE_KEY_PEM_B64` or `OIDC_PRIVATE_KEY_PEM` and `OIDC_KEY_ID` did not change.
|
|
3. Restart the relying app or clear its JWKS cache if needed.
|
|
|
|
## Devcloud Verification
|
|
|
|
Run these after code, env, or setup-script changes.
|
|
|
|
Static checks:
|
|
|
|
```bash
|
|
bash -n start-devprod.sh recreate-devprod.sh scripts/finish-initial-setup.sh scripts/bootstrap-host-nextcloud-hybrid.sh scripts/install-host-nextcloud-hybrid.sh scripts/ensure-qortal-integration-runtime-config.sh
|
|
./scripts/ensure-oidc-signing-key.sh .env.devprod
|
|
node --check nextcloud/custom_apps/qortal_integration/js/admin.js
|
|
npm --prefix services/qortal-oidc-broker run build
|
|
```
|
|
|
|
Runtime sync:
|
|
|
|
```bash
|
|
./scripts/finish-initial-setup.sh --mode nossl --env-file .env.devprod
|
|
./scripts/ensure-qortal-integration-runtime-config.sh --compose-file docker-compose.devprod.nossl.yml --env-file .env.devprod
|
|
```
|
|
|
|
Discovery:
|
|
|
|
```bash
|
|
curl -fsS "$(grep -m1 '^OIDC_ISSUER=' .env.devprod | cut -d= -f2-)/.well-known/openid-configuration"
|
|
```
|
|
|
|
App enablement:
|
|
|
|
```bash
|
|
docker compose -f docker-compose.devprod.nossl.yml --env-file .env.devprod exec -T --user www-data app php occ app:list | rg 'qortal_integration|user_oidc'
|
|
```
|
|
|
|
Admin UI checks:
|
|
|
|
- Create one client from a template.
|
|
- Copy the one-time secret.
|
|
- Edit redirect URIs and save.
|
|
- Rotate the secret.
|
|
- Disable and re-enable the client.
|
|
- Complete one app login.
|
|
- Confirm audit events show authorization and token activity.
|
|
|
|
## Completion Criteria
|
|
|
|
Call the identity-provider feature complete when:
|
|
|
|
- Built-in setup scripts configure the provider without manual file edits.
|
|
- The admin UI can create, edit, disable, enable, and rotate client secrets.
|
|
- Client secrets are not exposed after creation or rotation.
|
|
- Exact redirect validation is confirmed.
|
|
- Group-denied users cannot authorize restricted clients.
|
|
- Scope filtering is confirmed through `/userinfo`.
|
|
- Audit events are visible after auth and token actions.
|
|
- Devcloud runtime verification passes.
|
|
- This guide reflects the tested setup path.
|