How to Set Up Single Sign-On (SSO) Authentication in AnythingLLM
AnythingLLM implements SSO through a Simple SSO mode that exchanges temporary auth tokens for JWT session tokens, eliminating the need for users to enter passwords while requiring only environment variable configuration and a token issuance mechanism.
Setting up Single Sign-On (SSO) authentication in AnythingLLM allows your organization to integrate external identity providers without modifying core authentication logic. The implementation relies on temporary tokens that bridge your identity provider with AnythingLLM's existing JWT-based session management. This guide covers the complete technical implementation based on the actual source code in the Mintplex-Labs/anything-llm repository.
Prerequisites for AnythingLLM SSO
Before configuring SSO, verify your deployment meets these baseline requirements:
- Multi-User mode must be enabled by setting
MULTI_USER_MODE=1in your environment variables. ThesimpleSSOEnabledmiddleware explicitly checksSystemSettings.isMultiUserMode()and returns 403 Forbidden if this is disabled. - Node.js version 18 or higher is required by the repository's runtime dependencies.
- Administrator access to the server filesystem to modify
.envconfiguration and execute token issuance scripts.
How Simple SSO Works
The Simple SSO architecture in AnythingLLM consists of four sequential stages:
- Server Configuration – Environment variables activate the SSO middleware and disable native login
- Token Issuance – Administrators generate single-use temporary tokens via
TemporaryAuthToken.issue() - Frontend Hand-off – The React frontend detects SSO mode and redirects to the SSO handler
- Session Creation – The backend validates the temporary token and returns a standard JWT stored in
localStorage
All SSO logic is gated by the simpleSSOEnabled middleware located at server/utils/middleware/simpleSSOEnabled.js.
Step 1 – Enable SSO in Server Configuration
Configure three environment variables in your .env file or Docker environment:
MULTI_USER_MODE=1
SIMPLE_SSO_ENABLED=1
SIMPLE_SSO_NO_LOGIN=1
SIMPLE_SSO_NO_LOGIN_REDIRECT=https://sso.mycompany.com/login
SIMPLE_SSO_ENABLEDactivates the SSO middleware at startupSIMPLE_SSO_NO_LOGINremoves the username/password form from the UISIMPLE_SSO_NO_LOGIN_REDIRECT(optional) redirects to an external identity provider instead of the built-in simple SSO page
These values are loaded into SystemSettings in server/models/systemSettings.js:
SimpleSSOEnabled: "SIMPLE_SSO_ENABLED" in process.env || false,
SimpleSSONoLogin: "SIMPLE_SSO_NO_LOGIN" in process.env || false,
SimpleSSONoLoginRedirect: this.simpleSSO.noLoginRedirect(),
The middleware defined in server/utils/middleware/simpleSSOEnabled.js validates both the environment variable and Multi-User mode before allowing access to SSO endpoints.
Step 2 – Generate Temporary Auth Tokens
SSO authentication requires a temporary auth token generated server-side. These tokens are UUID-based strings prefixed with allm-tat-, expire after 6 minutes, and are single-use.
Create a token issuance script using the TemporaryAuthToken model:
// scripts/issue-sso-token.js
require('dotenv').config();
const { TemporaryAuthToken } = require('../server/models/temporaryAuthToken');
const prisma = require('../server/utils/prisma');
(async () => {
const userId = parseInt(process.argv[2], 10);
if (!userId) {
console.error('Usage: node issue-sso-token.js <userId>');
process.exit(1);
}
await prisma.$connect();
const { token, error } = await TemporaryAuthToken.issue(userId);
if (error) {
console.error('Failed:', error);
process.exit(1);
}
console.log('Temporary SSO token:', token);
console.log(`URL: https://your-instance.com/login/sso/simple?token=${token}`);
await prisma.$disconnect();
})();
Execute with node scripts/issue-sso-token.js 42 where 42 is the target user's ID.
The TemporaryAuthToken.issue() method in server/models/temporaryAuthToken.js handles database insertion with a 6-minute expiry timestamp. The validate() method automatically deletes consumed tokens to prevent replay attacks.
Step 3 – Configure Frontend Detection and Redirection
The React frontend detects SSO mode via the useSimpleSSO hook at frontend/src/hooks/useSimpleSSO.js, which polls System.keys() for the three SSO configuration flags.
Implement the redirect logic in your login page:
// frontend/src/pages/Login/index.jsx
import useSimpleSSO from '@/hooks/useSimpleSSO';
import SimpleSSOPassthrough from '@/pages/Login/SSO/simple';
export default function LoginPage() {
const { loading, ssoConfig } = useSimpleSSO();
if (loading) return <Spinner />;
if (ssoConfig.enabled && ssoConfig.noLogin) {
return <SimpleSSOPassthrough />;
}
return <TraditionalLoginForm />;
}
The SimpleSSOPassthrough component at frontend/src/pages/Login/SSO/simple.jsx extracts the token query parameter and initiates authentication:
import { useSearchParams, useNavigate } from 'react-router-dom';
import System from '@/models/system';
export default function SimpleSSOPassthrough() {
const [search] = useSearchParams();
const navigate = useNavigate();
const token = search.get('token');
if (!token) {
navigate('/login');
return null;
}
System.simpleSSOLogin(token)
.then(({ valid, sessionToken }) => {
if (valid) {
localStorage.setItem('session-token', sessionToken);
navigate('/');
} else {
navigate('/login');
}
});
return <Spinner />;
}
Step 4 – Validate Tokens and Create JWT Sessions
The backend endpoint /request-token/sso/simple defined in server/endpoints/system.js exchanges temporary tokens for JWT session tokens:
app.get(
"/request-token/sso/simple",
[simpleSSOEnabled],
async (req, res) => {
const { token: tempAuthToken } = req.query;
const { sessionToken, token, error } = await TemporaryAuthToken.validate(tempAuthToken);
if (error) {
return res.status(401).json({
valid: false,
token: null,
message: error
});
}
return res.json({
valid: true,
sessionToken,
token: null,
message: null
});
}
);
The TemporaryAuthToken.validate() function in server/models/temporaryAuthToken.js performs three critical checks:
- Verifies the token exists in the
temporary_auth_tokenstable - Confirms the current time is before the expiry timestamp
- Ensures the associated user account is not suspended
Upon successful validation, the method calls makeJWT() to generate a session token with the standard JWT_EXPIRY duration, then deletes the temporary token row to prevent reuse.
Summary
- Enable SSO by setting
SIMPLE_SSO_ENABLED=1andMULTI_USER_MODE=1in your server environment - Generate tokens using
TemporaryAuthToken.issue(userId)which creates 6-minute, single-use tokens prefixed withallm-tat- - Automate login by setting
SIMPLE_SSO_NO_LOGIN=1to disable the native login form and optionally specifySIMPLE_SSO_NO_LOGIN_REDIRECTfor external identity providers - Exchange tokens via the
/request-token/sso/simpleendpoint which validates the temporary token and returns a JWT stored inlocalStorageunder the keysession-token - Protect endpoints using the
simpleSSOEnabledmiddleware which verifies Multi-User mode is active before allowing SSO requests
Frequently Asked Questions
Does AnythingLLM support SAML or OIDC protocols directly?
No, AnythingLLM implements a Simple SSO pattern rather than native SAML or OIDC support. You must handle SAML/OIDC authentication externally using your identity provider, then use the Simple SSO API to generate temporary tokens and redirect users to /login/sso/simple?token=TOKEN. This architecture keeps the codebase lightweight while allowing integration with any identity provider that can generate HTTP redirects.
How long do temporary SSO tokens remain valid?
Temporary tokens expire after 6 minutes from issuance. This value is hardcoded in server/models/temporaryAuthToken.js where the expiry date is set using new Date(new Date().getTime() + 6 * 60 * 1000). Tokens are also single-use; the validate() method deletes the database row immediately after successful authentication, preventing replay attacks even within the 6-minute window.
Can I use SSO without disabling the regular login form?
Yes, set SIMPLE_SSO_ENABLED=1 while omitting SIMPLE_SSO_NO_LOGIN. In this configuration, the SSO endpoint remains active but the frontend continues displaying the username/password form. Users can authenticate via either method. The useSimpleSSO hook will return noLogin: false, causing the LoginPage component to render the traditional form alongside any SSO options.
What happens if a user tries to reuse an expired or invalid token?
The backend returns 401 Unauthorized with an error message. Specifically, TemporaryAuthToken.validate() checks the expiry column against the current timestamp and verifies the token exists in the database. If either check fails, it returns an object with sessionToken: null and error: "Token expired or invalid", which the /request-token/sso/simple endpoint forwards to the client as a JSON response with valid: false.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →