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=1 in your environment variables. The simpleSSOEnabled middleware explicitly checks SystemSettings.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 .env configuration and execute token issuance scripts.

How Simple SSO Works

The Simple SSO architecture in AnythingLLM consists of four sequential stages:

  1. Server Configuration – Environment variables activate the SSO middleware and disable native login
  2. Token Issuance – Administrators generate single-use temporary tokens via TemporaryAuthToken.issue()
  3. Frontend Hand-off – The React frontend detects SSO mode and redirects to the SSO handler
  4. 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_ENABLED activates the SSO middleware at startup
  • SIMPLE_SSO_NO_LOGIN removes the username/password form from the UI
  • SIMPLE_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_tokens table
  • 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=1 and MULTI_USER_MODE=1 in your server environment
  • Generate tokens using TemporaryAuthToken.issue(userId) which creates 6-minute, single-use tokens prefixed with allm-tat-
  • Automate login by setting SIMPLE_SSO_NO_LOGIN=1 to disable the native login form and optionally specify SIMPLE_SSO_NO_LOGIN_REDIRECT for external identity providers
  • Exchange tokens via the /request-token/sso/simple endpoint which validates the temporary token and returns a JWT stored in localStorage under the key session-token
  • Protect endpoints using the simpleSSOEnabled middleware 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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →