# How to Set Up Single Sign-On (SSO) Authentication in AnythingLLM

> Learn how to set up Single Sign-On (SSO) authentication in AnythingLLM. Simplify user access with JWT session tokens and environment variable configuration. No passwords needed for seamless login.

- Repository: [Mintplex Labs/anything-llm](https://github.com/Mintplex-Labs/anything-llm)
- Tags: how-to-guide
- Published: 2026-03-07

---

**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`](https://github.com/Mintplex-Labs/anything-llm/blob/main/server/utils/middleware/simpleSSOEnabled.js).

## Step 1 – Enable SSO in Server Configuration

Configure three environment variables in your `.env` file or Docker environment:

```dotenv
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`](https://github.com/Mintplex-Labs/anything-llm/blob/main/server/models/systemSettings.js):

```javascript
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`](https://github.com/Mintplex-Labs/anything-llm/blob/main/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:

```javascript
// 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`](https://github.com/Mintplex-Labs/anything-llm/blob/main/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`](https://github.com/Mintplex-Labs/anything-llm/blob/main/frontend/src/hooks/useSimpleSSO.js), which polls `System.keys()` for the three SSO configuration flags.

Implement the redirect logic in your login page:

```jsx
// 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`](https://github.com/Mintplex-Labs/anything-llm/blob/main/frontend/src/pages/Login/SSO/simple.jsx) extracts the `token` query parameter and initiates authentication:

```jsx
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`](https://github.com/Mintplex-Labs/anything-llm/blob/main/server/endpoints/system.js) exchanges temporary tokens for JWT session tokens:

```javascript
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`](https://github.com/Mintplex-Labs/anything-llm/blob/main/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`](https://github.com/Mintplex-Labs/anything-llm/blob/main/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`.