--- title: "Single Sign-On (SSO)" description: "A guide to setting up custom OAuth providers like Authentik, Authelia, Keycloak, or any OIDC-compliant identity provider for Single Sign-On (SSO) in your self-hosted instance." --- ## Overview Reactive Resume supports custom OAuth providers, allowing you to integrate with enterprise identity providers and self-hosted authentication solutions. This is particularly useful for organizations that want to: - Use a centralized identity provider (Authentik, Authelia, Keycloak, etc.) - Enforce Single Sign-On (SSO) across all internal applications - Integrate with existing LDAP/Active Directory infrastructure Custom OAuth is designed for **self-hosted instances**. If you're using the hosted version at [rxresu.me](https://rxresu.me), you can use the built-in Google and GitHub sign-in options. ## Environment Variables To enable a custom OAuth provider, you need to configure the following environment variables in your `.env` file: ### Required Variables | Variable | Description | | --------------------- | ------------------------------------------------- | | `OAUTH_CLIENT_ID` | The client ID provided by your OAuth provider | | `OAUTH_CLIENT_SECRET` | The client secret provided by your OAuth provider | ### Endpoint Configuration You must configure endpoints using **one** of these two methods: For OIDC-compliant providers (most modern identity providers), you only need to set the discovery URL: | Variable | Description | |----------|-------------| | `OAUTH_DISCOVERY_URL` | Your provider's `.well-known/openid-configuration` URL | The discovery URL automatically provides the authorization, token, and userinfo endpoints. **Examples:** - Authentik: `https://auth.example.com/application/o/reactive-resume/.well-known/openid-configuration` - Keycloak: `https://keycloak.example.com/realms/myrealm/.well-known/openid-configuration` - Authelia: `https://auth.example.com/.well-known/openid-configuration` For providers that don't support OIDC discovery, you must set all three URLs: | Variable | Description | |----------|-------------| | `OAUTH_AUTHORIZATION_URL` | The URL where users are redirected to authorize | | `OAUTH_TOKEN_URL` | The URL to exchange authorization codes for tokens | | `OAUTH_USER_INFO_URL` | The URL to fetch user profile information | ### Optional Variables | Variable | Description | Default | | --------------------- | ---------------------------------------- | ---------------------- | | `OAUTH_PROVIDER_NAME` | Display name shown on the sign-in button | `Custom OAuth` | | `OAUTH_SCOPES` | Space-separated list of OAuth scopes | `openid profile email` | ## Callback URL When configuring your OAuth provider, you'll need to set the **callback URL** (also called redirect URI). Use the following format: ``` {APP_URL}/api/auth/oauth2/callback/custom ``` For example, if your `APP_URL` is `https://resume.example.com`, the callback URL would be: ``` https://resume.example.com/api/auth/oauth2/callback/custom ``` Make sure the callback URL exactly matches what you configure in your OAuth provider. A mismatch will cause authentication to fail. ## Profile Mapping Reactive Resume automatically maps user profile data from the OAuth provider. The following fields are used: | Reactive Resume Field | OAuth Profile Fields (in order of preference) | | --------------------- | --------------------------------------------- | | **Email** (required) | `email` | | **Name** | `name` → `preferred_username` → email prefix | | **Username** | `preferred_username` → email prefix | | **Avatar** | `image` → `picture` → `avatar_url` | The OAuth provider **must** return an email address. If no email is provided, authentication will fail with an error. ## Provider-Specific Setup ### Authentik In the Authentik admin interface, navigate to **Applications → Providers** and create a new **OAuth2/OpenID Provider**. - **Name**: Reactive Resume - **Authorization flow**: Use your preferred authorization flow - **Client type**: Confidential - **Redirect URIs**: `https://resume.example.com/api/auth/oauth2/callback/custom` Navigate to **Applications → Applications** and create a new application: - **Name**: Reactive Resume - **Slug**: `reactive-resume` - **Provider**: Select the provider you just created From the provider settings, copy the **Client ID** and **Client Secret**. ```bash .env OAUTH_PROVIDER_NAME="Authentik" OAUTH_CLIENT_ID="your-client-id" OAUTH_CLIENT_SECRET="your-client-secret" OAUTH_DISCOVERY_URL="https://auth.example.com/application/o/reactive-resume/.well-known/openid-configuration" ``` ### Authelia Add a client configuration to your Authelia `configuration.yml`: ```yaml identity_providers: oidc: clients: - client_id: reactive-resume client_name: Reactive Resume client_secret: "your-hashed-secret" # Use authelia hash-password to generate public: false authorization_policy: two_factor # or one_factor redirect_uris: - https://resume.example.com/api/auth/oauth2/callback/custom scopes: - openid - profile - email token_endpoint_auth_method: client_secret_post ``` Generate the hashed secret using: `authelia crypto hash generate pbkdf2 --variant sha512` ```bash .env OAUTH_PROVIDER_NAME="Authelia" OAUTH_CLIENT_ID="reactive-resume" OAUTH_CLIENT_SECRET="your-plain-secret" OAUTH_DISCOVERY_URL="https://auth.example.com/.well-known/openid-configuration" ``` Use the **plain text** secret in Reactive Resume's environment, not the hashed version used in Authelia's configuration. ### Keycloak In the Keycloak admin console: 1. Select your realm 2. Navigate to **Clients → Create client** 3. Set **Client ID** (e.g., `reactive-resume`) 4. Set **Client authentication** to **On** 5. Enable **Standard flow** In the client settings, add the redirect URI: - **Valid redirect URIs**: `https://resume.example.com/api/auth/oauth2/callback/custom` Go to the **Credentials** tab and copy the **Client secret**. ```bash .env OAUTH_PROVIDER_NAME="Keycloak" OAUTH_CLIENT_ID="reactive-resume" OAUTH_CLIENT_SECRET="your-client-secret" OAUTH_DISCOVERY_URL="https://keycloak.example.com/realms/myrealm/.well-known/openid-configuration" ``` ### Generic OIDC Provider For any other OIDC-compliant provider: ```bash .env OAUTH_PROVIDER_NAME="My SSO" OAUTH_CLIENT_ID="your-client-id" OAUTH_CLIENT_SECRET="your-client-secret" OAUTH_DISCOVERY_URL="https://sso.example.com/.well-known/openid-configuration" ``` ### Non-OIDC Provider (Manual Configuration) For providers that don't support OIDC discovery: ```bash .env OAUTH_PROVIDER_NAME="Custom Provider" OAUTH_CLIENT_ID="your-client-id" OAUTH_CLIENT_SECRET="your-client-secret" OAUTH_AUTHORIZATION_URL="https://provider.example.com/oauth/authorize" OAUTH_TOKEN_URL="https://provider.example.com/oauth/token" OAUTH_USER_INFO_URL="https://provider.example.com/oauth/userinfo" OAUTH_SCOPES="openid profile email" ``` ## Complete Example Here's a complete `.env` snippet showing custom OAuth alongside other authentication options: ```bash .env # --- Authentication --- AUTH_SECRET="your-32-byte-hex-secret" # Built-in Social Auth (optional, can coexist with custom OAuth) # GOOGLE_CLIENT_ID="" # GOOGLE_CLIENT_SECRET="" # GITHUB_CLIENT_ID="" # GITHUB_CLIENT_SECRET="" # LINKEDIN_CLIENT_ID="" # LINKEDIN_CLIENT_SECRET="" # Custom OAuth Provider (e.g., Authentik) OAUTH_PROVIDER_NAME="Company SSO" OAUTH_CLIENT_ID="reactive-resume-client-id" OAUTH_CLIENT_SECRET="reactive-resume-client-secret" OAUTH_DISCOVERY_URL="https://auth.company.com/application/o/reactive-resume/.well-known/openid-configuration" # OAUTH_SCOPES="openid profile email" # Defaults to these scopes if not set ``` ## Troubleshooting Your OAuth provider must return an email address for user creation. Ensure: - The `email` scope is included in your scopes - Your provider is configured to release the email claim - The user has an email address set in the identity provider The callback URL configured in your OAuth provider must exactly match: ``` {APP_URL}/api/auth/oauth2/callback/custom ``` Common issues: - Trailing slash mismatch - HTTP vs HTTPS mismatch - Port number differences - Path case sensitivity The custom OAuth option only appears if both `OAUTH_CLIENT_ID` and `OAUTH_CLIENT_SECRET` are set, **and** either: - `OAUTH_DISCOVERY_URL` is set, **or** - All three manual URLs are set (`OAUTH_AUTHORIZATION_URL`, `OAUTH_TOKEN_URL`, `OAUTH_USER_INFO_URL`) Double-check your environment variables and restart the container. If running behind a reverse proxy: - Ensure `APP_URL` matches your public URL - Verify the proxy passes the correct headers (`X-Forwarded-Proto`, `X-Forwarded-Host`) - Check that your OAuth provider allows the redirect URI from your domain The profile mapping depends on your provider returning standard claims: - `email` (required) - `name` or `preferred_username` for display name - `picture`, `image`, or `avatar_url` for avatar Check your provider's documentation to ensure these claims are included in the ID token or userinfo response. ## Security Considerations Always use HTTPS for both your Reactive Resume instance and OAuth provider in production. OAuth tokens should never be transmitted over unencrypted connections. Never commit `OAUTH_CLIENT_SECRET` to version control. Use environment variables or a secrets manager. Configure your OAuth provider to only allow the exact redirect URI. Avoid wildcards in redirect URI configurations. Only request the scopes you need. The default (`openid profile email`) is sufficient for Reactive Resume.