This is for self-hosted installations. On rxresu.me, use the built-in sign-in options.
Before you start
You need:- Admin access to your identity provider, so you can register an application (client).
- The public address of your installation, set as
APP_URL, for examplehttps://resume.example.com. Every callback URL is built from it. - A provider that returns an email address for each user. Reactive Resume identifies accounts by email and refuses sign-ins without one.
Register Reactive Resume with your provider
Create a confidential OAuth 2.0 / OIDC client in your provider with these settings:
Copy the client ID and client secret. The callback must match exactly, including
https, the port, and the absence of a trailing slash.
Configure the environment variables
Set the client credentials and one way of finding the provider’s endpoints, then restart Reactive Resume.- OIDC discovery (recommended)
- Manual endpoints
Most modern providers publish a discovery document. Point Reactive Resume at it and it reads the authorization, token, and user-info endpoints from there.
.env
All URLs must use
http or https. On Docker, restart the container after changing .env; on Vercel, add the variables to the project and redeploy.
The sign-in button appears only when
OAUTH_CLIENT_ID and OAUTH_CLIENT_SECRET are set together with
OAUTH_DISCOVERY_URL or all three manual endpoint URLs. If it is missing, check that configuration and restart the
server.Check that it works
- Open your installation’s sign-in page. Under or continue with, a button with a key icon shows your
OAUTH_PROVIDER_NAME. - Select it, sign in at your provider, and approve access. You land on Documents.
- In Settings → Account, the Sign-in & security section lists your provider as Connected.
How profiles are mapped
When someone signs in for the first time, Reactive Resume creates their account from the provider’s profile:
If the email already belongs to an account, the sign-in is linked to that account, which keeps its name and username. This works only when that account’s email address is verified in Reactive Resume, protecting an existing account from an unverified email match. If it isn’t (common on installations without SMTP, where verification emails never arrive), the sign-in stops with an “account not linked” error. Verify the existing account’s email before trying SSO again, or sign in to that account and connect the provider in Settings → Account.
Make SSO the only way in
Two feature flags turn Reactive Resume into an SSO-only installation:
Set only
FLAG_DISABLE_EMAIL_AUTH if new people should still get an account on their first SSO sign-in.
Provider examples
Replace the hostnames with your own. The redirect URI is always{APP_URL}/api/auth/callback/custom.
Authentik
Authentik
- In the admin interface, open Applications → Providers and create an OAuth2/OpenID Provider. Set Client type to Confidential and add the redirect URI.
- Open Applications → Applications, create an application with the slug
reactive-resume, and select the provider. - Copy the client ID and secret from the provider.
.env
Authelia
Authelia
Add a client to Authelia’s Give Reactive Resume the plain-text secret, not the hash:
configuration.yml. Store a hashed secret there, generated with authelia crypto hash generate pbkdf2 --variant sha512..env
Keycloak
Keycloak
- In your realm, open Clients → Create client and set Client ID to
reactive-resume. - Turn Client authentication on and keep Standard flow enabled.
- Add the redirect URI under Valid redirect URIs.
- Copy the secret from the Credentials tab.
.env
Built-in providers
Google, GitHub, and LinkedIn sign-in each turn on when both of their variables are set, and can run alongside your own provider:Upgrading an install that uses OIDC discovery
Since v5.2.8, linked accounts are identified by the issuer your provider advertises. The database migration that introduced this can’t know your issuer, so it filled existing SSO accounts with the placeholderlocal:oauth:custom. If you used OAUTH_DISCOVERY_URL before v5.2.8, run this once after upgrading, with the issuer value from your discovery document:
URLs and reverse proxies
- Set
APP_URLto the exact public address people use, withhttpsin production. Callback URLs, secure cookies, and trusted origins all come from it. - Only the
APP_URLorigin (pluslocalhostand127.0.0.1on port 3000) is trusted. Other domains pointing at the same installation aren’t. - Behind a reverse proxy, pass the
HostandX-Forwarded-Protoheaders through unchanged.
Troubleshooting
The provider did not return an email address
The provider did not return an email address
The sign-in fails, and the server logs “OAuth Provider provider did not return an email address”. Include the
email scope, make sure your provider releases the email claim, and check that the user has an email address in the
provider.Redirect URI mismatch
Redirect URI mismatch
The callback registered in your provider must equal
{APP_URL}/api/auth/callback/custom exactly. Look for a
trailing slash, http instead of https, a different port, or a different hostname.Sign-in succeeds at the provider but you land on an error page or aren't signed in
Sign-in succeeds at the provider but you land on an error page or aren't signed in
Failed callbacks open the app’s
/auth/error page. The most common cause is an APP_URL that doesn’t match the
real public address, for example http:// behind a TLS proxy. Set APP_URL to the canonical https address and
restart.Sign-in fails with "account not linked"
Sign-in fails with "account not linked"
An account with the same email already exists, and its email address isn’t verified in Reactive Resume. The person
can verify their email (this needs SMTP) and try again. Accounts created through SSO are always treated as verified.
Existing users get a new, empty account or can't sign in after an upgrade
Existing users get a new, empty account or can't sign in after an upgrade
If you use
OAUTH_DISCOVERY_URL and upgraded from before v5.2.8, run the issuer update in Upgrading an install
that uses OIDC discovery.An MCP or API client's redirect URI is rejected
An MCP or API client's redirect URI is rejected
This is a different feature: Reactive Resume is then the OAuth server, for tools that connect to it. Dynamic client
registration accepts your app’s own origin and local loopback callbacks. Trusted private installations that need
other redirect URIs can set
FLAG_ALLOW_UNSAFE_OAUTH_REDIRECT_URI=true, which accepts any parseable URI. Don’t
enable it on public or shared installations.Security checklist
- Use
httpsfor both Reactive Resume and your provider. - Keep
OAUTH_CLIENT_SECRET,AUTH_SECRET, andBETTER_AUTH_API_KEYout of version control. RotatingAUTH_SECRETsigns everyone out. - Register the exact redirect URI; avoid wildcards.
- Request only the default scopes; Reactive Resume needs nothing more.
Related pages
- Environment variables: every variable the server reads.
- Self-hosting with Docker: where to put these variables in a container setup.
- Self-hosting on Vercel: adding variables to a Vercel project.