ScanopyScanopy

OIDC Setup

Configuring OpenID Connect authentication for self-hosted Scanopy.

Scanopy supports OpenID Connect (OIDC) for enterprise authentication with providers like Authentik, Keycloak, Auth0, Okta, PocketID, and others.

Quick Start

Option A: TOML File (recommended for complex setups)

  1. Copy oidc.toml.example to oidc.toml
  2. Configure your provider settings (see examples below)
  3. Mount the file in docker-compose
  4. Restart the server

Option B: Environment Variable

  1. Set SCANOPY_OIDC_PROVIDERS with your provider config (see format below)
  2. Restart the server

See Environment Variable Configuration for details.

Configuration File Format

[[oidc_providers]]
name = "Provider Name"           # Display name in UI
slug = "provider-slug"           # Used in callback URL (lowercase, no spaces)
logo = "https://..."             # Optional: logo URL for UI
issuer_url = "https://..."       # Provider's OIDC issuer URL
client_id = "your-client-id"
client_secret = "your-client-secret"

You can configure multiple providers by adding multiple [[oidc_providers]] sections.

Environment Variable Configuration

Instead of using a TOML file, you can configure OIDC providers using the SCANOPY_OIDC_PROVIDERS environment variable.

Important: This uses TOML-like syntax, NOT JSON. Use key="value" with equals signs, not "key":"value" with colons.

Shell / .env Files

For shell scripts, .env files, or tools like Kamal that use shell-style secrets:

SCANOPY_OIDC_PROVIDERS='[{name="Authentik",slug="authentik",issuer_url="https://auth.example.com/application/o/scanopy",client_id="your-client-id",client_secret="your-client-secret"}]'

Requirements:

  • Single quotes around the entire value
  • No spaces after commas
  • No newlines (must be a single line)

For multiple providers:

SCANOPY_OIDC_PROVIDERS='[{name="Authentik",slug="authentik",issuer_url="https://auth.example.com/application/o/scanopy",client_id="id1",client_secret="secret1"},{name="Keycloak",slug="keycloak",issuer_url="https://keycloak.example.com/realms/main",client_id="id2",client_secret="secret2"}]'

YAML Files (Docker Compose, Kubernetes)

For YAML-based configuration, use the >- block scalar to handle the format cleanly:

environment:
  SCANOPY_OIDC_PROVIDERS: >-
    [{name="Authentik",slug="authentik",issuer_url="https://auth.example.com/application/o/scanopy",client_id="your-client-id",client_secret="your-client-secret"}]

The >- folds the value into a single line and removes trailing newlines. You can also format it for readability:

environment:
  SCANOPY_OIDC_PROVIDERS: >-
    [{
      name="Authentik",
      slug="authentik",
      issuer_url="https://auth.example.com/application/o/scanopy",
      client_id="your-client-id",
      client_secret="your-client-secret"
    }]

Use >- (with the hyphen), not just >. The hyphen strips trailing newlines which prevents parsing errors.

Each provider object supports these fields:

FieldRequiredDescription
nameYesDisplay name shown in UI
slugYesURL-safe identifier (lowercase, no spaces)
issuer_urlYesOIDC provider's issuer URL
client_idYesOAuth2 client ID
client_secretYesOAuth2 client secret
logoNoLogo URL for UI display

Docker Compose Example

services:
  scanopy-server:
    image: ghcr.io/scanopy/scanopy/server:latest
    environment:
      SCANOPY_PUBLIC_URL: https://scanopy.example.com
      SCANOPY_OIDC_PROVIDERS: >-
        [{name="Authentik",slug="authentik",issuer_url="https://auth.example.com/application/o/scanopy",client_id="your-client-id",client_secret="your-client-secret"}]

This method is useful when:

  • You want to keep all configuration in environment variables
  • You're using container orchestration that injects secrets via env vars
  • You prefer not to mount additional config files

Note: Environment variables take precedence over the TOML file if both are configured.

Callback URL Format

Configure this URL in your OIDC provider's redirect/callback settings:

http://your-scanopy-domain:60072/api/auth/oidc/{slug}/callback

Replace {slug} with the slug value from your oidc.toml. For example, if slug = "authentik":

http://scanopy.local:60072/api/auth/oidc/authentik/callback

Required scopes: openid, email, profile (profile is optional but recommended)

Docker Compose Setup

Add the following volume mount to your scanopy-server service:

services:
  scanopy-server:
    image: ghcr.io/scanopy/scanopy/server:latest
    volumes:
      - ./oidc.toml:/oidc.toml:ro
    # ... rest of config

Provider Examples

Authentik

  1. Create Application in Authentik Admin → Applications → Create:

    • Name: Scanopy
    • Slug: scanopy
    • Provider: Create a new OAuth2/OpenID Provider
  2. Configure Provider:

    • Name: Scanopy OIDC
    • Authorization flow: default-provider-authorization-implicit-consent
    • Client type: Confidential
    • Redirect URIs: http://your-scanopy:60072/api/auth/oidc/authentik/callback
    • Copy the Client ID and Client Secret
  3. Find your Issuer URL:

    • Go to Providers → your provider → OpenID Configuration Issuer
    • Usually: https://auth.yourdomain.com/application/o/scanopy/
    • Important: Remove trailing slash if present (see Common Issues)
  4. Configure oidc.toml:

[[oidc_providers]]
name = "Authentik"
slug = "authentik"
logo = "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/authentik.svg"
issuer_url = "https://auth.yourdomain.com/application/o/scanopy"
client_id = "your-client-id"
client_secret = "your-client-secret"

Keycloak

  1. Create Client in Keycloak Admin → Clients → Create:

    • Client ID: scanopy
    • Client type: OpenID Connect
    • Client authentication: On
  2. Configure Client Settings:

    • Valid redirect URIs: http://your-scanopy:60072/api/auth/oidc/keycloak/callback
    • Web origins: http://your-scanopy:60072
  3. Get Credentials:

    • Go to Credentials tab
    • Copy Client Secret
  4. Configure oidc.toml:

[[oidc_providers]]
name = "Keycloak"
slug = "keycloak"
logo = "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/keycloak.svg"
issuer_url = "https://keycloak.yourdomain.com/realms/your-realm"
client_id = "scanopy"
client_secret = "your-client-secret"

PocketID

  1. Create OIDC Client in PocketID:

    • Go to OIDC Clients → Add Client
    • Name: Scanopy
    • Callback URLs: http://your-scanopy:60072/api/auth/oidc/pocketid/callback
  2. Copy Credentials:

    • Client ID
    • Client Secret
  3. Configure oidc.toml:

[[oidc_providers]]
name = "PocketID"
slug = "pocketid"
logo = "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/pocketid.svg"
issuer_url = "https://pocketid.yourdomain.com"
client_id = "your-client-id"
client_secret = "your-client-secret"

Auth0

  1. Create Application in Auth0 Dashboard → Applications → Create:

    • Type: Regular Web Application
    • Name: Scanopy
  2. Configure Application Settings:

    • Allowed Callback URLs: http://your-scanopy:60072/api/auth/oidc/auth0/callback
    • Allowed Web Origins: http://your-scanopy:60072
  3. Configure oidc.toml:

[[oidc_providers]]
name = "Auth0"
slug = "auth0"
logo = "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/auth0.svg"
issuer_url = "https://your-tenant.auth0.com"
client_id = "your-client-id"
client_secret = "your-client-secret"

Okta

  1. Create App Integration in Okta Admin → Applications → Create:

    • Sign-in method: OIDC - OpenID Connect
    • Application type: Web Application
  2. Configure Settings:

    • Sign-in redirect URIs: http://your-scanopy:60072/api/auth/oidc/okta/callback
    • Sign-out redirect URIs: http://your-scanopy:60072
  3. Configure oidc.toml:

[[oidc_providers]]
name = "Okta"
slug = "okta"
logo = "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/okta.svg"
issuer_url = "https://your-org.okta.com"
client_id = "your-client-id"
client_secret = "your-client-secret"

Authelia

identity_providers:
  oidc:
    clients:
      - client_id: 'scanopy'
        client_name: 'Scanopy'
        client_secret: { { secret "/run/secrets/authelia_scanopy_oidc" | msquote } }
        public: false
        authorization_policy: 'two_factor'
        require_pkce: false
        pkce_challenge_method: ''
        redirect_uris:
          - 'https://scanopy.YOURDOMAIN/api/auth/oidc/authelia/callback'
        scopes:
          - 'openid'
          - 'email'
          - 'profile'
        response_types:
          - 'code'
        grant_types:
          - 'authorization_code'
        access_token_signed_response_alg: 'none'
        userinfo_signed_response_alg: 'none'
        token_endpoint_auth_method: 'client_secret_basic'
        consent_mode: 'auto'
        pre_configured_consent_duration: '1M'
[[oidc_providers]]
name = "Authelia"
slug = "authelia"
logo = "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/authelia.svg"
issuer_url = "https://auth.YOURDOMAIN"
client_id = "scanopy"
client_secret = "YOURSUPERSECRET"

Linking OIDC to Existing Accounts

If OIDC is enabled, users can link it to their existing email/password account:

  1. Log in with your existing email and password
  2. Go to Account Settings (click the user icon in top right)
  3. Click Link under your OIDC provider
  4. Complete the authentication flow with your provider
  5. Your account is now linked — you can log in with either method

Unlinking: You can unlink OIDC at any time from Account Settings, but you'll need to have a password set first.

Common Issues

"Unexpected issuer URI" error

Failed to generate auth URL: Validation error: unexpected issuer URI
`https://auth.example.com/app/` (expected `https://auth.example.com/app`)

Cause: Trailing slash mismatch between your config and what the provider returns.

Solution: Try both with and without trailing slash in issuer_url. The value must exactly match what your provider returns in its .well-known/openid-configuration.

To check what your provider expects:

curl https://your-provider/.well-known/openid-configuration | jq .issuer

"Invalid redirect URI" error

Cause: The callback URL in your provider doesn't match what Scanopy sends.

Solution: Ensure the redirect URI in your provider exactly matches:

http://your-scanopy:60072/api/auth/oidc/{slug}/callback

Common mistakes:

  • Wrong protocol (http vs https)
  • Wrong port
  • Wrong slug (must match oidc.toml)
  • Missing /callback at the end

OIDC button not appearing in UI

Causes:

  1. oidc.toml file not mounted in Docker (if using file-based config)
  2. oidc.toml has syntax errors
  3. SCANOPY_OIDC_PROVIDERS has invalid JSON (if using env var config)
  4. Server not restarted after adding config

Solution:

  1. If using TOML file: Verify the volume mount exists in docker-compose.yml and validate TOML syntax
  2. If using env var: Validate JSON syntax — use echo $SCANOPY_OIDC_PROVIDERS | jq . to check
  3. Restart with docker compose restart scanopy-server
  4. Check server logs: docker logs scanopy-server

"Connection refused" when authenticating

Cause: Scanopy server can't reach your OIDC provider.

Solutions:

  1. Ensure the provider URL is reachable from the server container
  2. If provider is internal, ensure Docker can resolve the hostname
  3. Add provider to Docker's extra_hosts if needed:
    extra_hosts:
      - 'auth.internal:192.168.1.100'

On this page