
Security News
Axios Supply Chain Attack Reaches OpenAI macOS Signing Pipeline, Forces Certificate Rotation
OpenAI rotated macOS signing certificates after a malicious Axios package reached its CI pipeline in a broader software supply chain attack.
payload-auth-cookie
Advanced tools
A Payload CMS authentication plugin for external SSO cookie-based authentication.
This plugin allows you to authenticate users based on cookies set by an external SSO system. It validates sessions via a configurable session endpoint and maps users to Payload CMS collections.
npm install payload-auth-cookie
# or
pnpm add payload-auth-cookie
# or
yarn add payload-auth-cookie
// payload.config.ts
import { buildConfig } from 'payload'
import { authPlugin } from 'payload-auth-cookie'
export default buildConfig({
plugins: [
authPlugin({
name: 'admin',
useAdmin: true,
usersCollectionSlug: 'adminUsers',
successRedirectPath: '/admin',
sso: {
cookieName: process.env.SSO_COOKIE_NAME!,
loginUrl: process.env.SSO_LOGIN_URL!,
logoutUrl: process.env.SSO_LOGOUT_URL!,
sessionUrl: process.env.SSO_SESSION_URL!,
},
}),
],
})
// collections/Users.ts
import { withUsersCollection } from 'payload-auth-cookie/collection'
export const AdminUsers = withUsersCollection({
slug: 'adminUsers',
auth: {
disableLocalStrategy: true,
},
fields: [
{
name: 'name',
type: 'text',
},
],
})
// components/SSOLoginButton.tsx
'use client'
import { LoginButton } from 'payload-auth-cookie/client'
export default function SSOLoginButton() {
return <LoginButton href="/api/admin/auth/login" label="Sign in with SSO" />
}
// payload.config.ts
export default buildConfig({
admin: {
components: {
afterLogin: ['@/components/SSOLoginButton'],
},
},
// ...
})
| Option | Type | Required | Default | Description |
|---|---|---|---|---|
name | string | ✅ | - | Unique name for this auth configuration (used in endpoint paths) |
usersCollectionSlug | string | ✅ | - | Slug of the users collection to authenticate against |
sso | SSOProviderConfig | ✅ | - | SSO provider configuration |
useAdmin | boolean | ❌ | false | Use this configuration for admin panel authentication |
allowSignUp | boolean | ❌ | false | Allow creating new users on first login |
successRedirectPath | string | ❌ | '/' | Path to redirect after successful authentication |
errorRedirectPath | string | ❌ | '/auth/error' | Path to redirect on authentication error |
onSuccess | function | ❌ | - | Callback after successful authentication |
onError | function | ❌ | - | Callback on authentication error |
| Option | Type | Required | Default | Description |
|---|---|---|---|---|
cookieName | string | ✅ | - | Name of the cookie set by your SSO system |
loginUrl | string | ✅ | - | URL to redirect for SSO login |
logoutUrl | string | ✅ | - | URL to redirect for SSO logout |
sessionUrl | string | ⚠️ | - | URL to validate session (required if jwt not provided) |
jwt | JWTVerificationConfig | ⚠️ | - | JWT verification config (required if sessionUrl not provided) |
timeoutMs | number | ❌ | 5000 | Timeout for session validation requests |
When your SSO cookie contains a JWT, you can verify it locally without calling an external session endpoint:
| Option | Type | Required | Default | Description |
|---|---|---|---|---|
secret | string | ✅ | - | Secret key for verifying JWT signatures |
algorithm | string | ❌ | 'HS256' | JWT algorithm (HS256, HS384, HS512) |
issuer | string | ❌ | - | Expected issuer (iss claim) |
audience | string | ❌ | - | Expected audience (aud claim) |
emailField | string | ❌ | 'email' | Field path to extract email (supports dot notation) |
firstNameField | string | ❌ | 'firstName' | Field path to extract first name |
lastNameField | string | ❌ | 'lastName' | Field path to extract last name |
profilePictureUrlField | string | ❌ | 'profilePictureUrl' | Field path to extract profile picture URL |
Map SSO response fields to your collection's field names:
| Option | Type | Required | Default | Description |
|---|---|---|---|---|
fieldMappings.nameField | string | ❌ | 'name' | SSO field containing the user's full name |
fieldMappings.firstNameField | string | ❌ | 'firstName' | SSO field containing the user's first name |
fieldMappings.lastNameField | string | ❌ | 'lastName' | SSO field containing the user's last name |
fieldMappings.profilePictureUrlField | string | ❌ | 'profilePictureUrl' | SSO field containing the profile picture URL |
fieldMappings.emailVerifiedField | string | ❌ | 'emailVerified' | SSO field indicating email verification status |
fieldMappings.lastLoginAtField | string | ❌ | 'lastLoginAt' | SSO field containing last login timestamp |
authPlugin({
name: 'app',
usersCollectionSlug: 'appUsers',
sso: {
cookieName: 'sso_token',
loginUrl: 'https://sso.example.com/login',
logoutUrl: 'https://sso.example.com/logout',
jwt: {
secret: process.env.SSO_JWT_SECRET!,
},
fieldMappings: {
nameField: 'full_name', // Maps SSO's full_name → user.name
profilePictureUrlField: 'avatar', // Maps SSO's avatar → user.profilePictureUrl
},
},
})
authPlugin({
name: 'admin',
useAdmin: true,
usersCollectionSlug: 'adminUsers',
sso: {
cookieName: 'sso_token',
loginUrl: 'https://sso.example.com/login',
logoutUrl: 'https://sso.example.com/logout',
jwt: {
secret: process.env.SSO_JWT_SECRET!,
algorithm: 'HS256',
issuer: 'https://sso.example.com',
emailField: 'user.email', // For nested JWT payloads
},
},
})
/adminloginUrl with returnUrl parametersupaku_session)/api/admin/auth/loginsessionUrl or JWT verification/admin/auth/login (or custom login page)/api/app/auth/loginloginUrlYour SSO session endpoint (sessionUrl) should:
email fieldThe plugin supports multiple response formats:
Direct user data:
{
"email": "user@example.com",
"firstName": "John",
"lastName": "Doe",
"profilePictureUrl": "https://example.com/avatar.jpg"
}
Nested user object (automatically extracted):
{
"authenticated": true,
"user": {
"email": "user@example.com",
"firstName": "John",
"lastName": "Doe",
"profilePictureUrl": "https://example.com/avatar.jpg"
}
}
If your SSO cookie is a JWT, configure jwt instead of sessionUrl:
sso: {
cookieName: 'sso_token',
loginUrl: 'https://sso.example.com/login',
logoutUrl: 'https://sso.example.com/logout',
jwt: {
secret: process.env.SSO_JWT_SECRET!,
},
}
The JWT payload should contain at least an email field (configurable via emailField).
You can configure multiple auth instances for different user types. Each instance creates its own set of endpoints and correctly returns users from its associated collection:
plugins: [
authPlugin({
name: 'admin',
useAdmin: true,
usersCollectionSlug: 'adminUsers',
successRedirectPath: '/admin',
sso: adminSSOConfig,
}),
authPlugin({
name: 'app',
allowSignUp: true,
usersCollectionSlug: 'appUsers',
successRedirectPath: '/',
sso: appSSOConfig,
}),
]
With this configuration:
/api/admin/auth/session returns users from adminUsers collection/api/app/auth/session returns users from appUsers collectionsso-cookie-adminUsers, sso-cookie-appUsers)The plugin creates the following endpoints (prefixed with your configured name):
| Endpoint | Method | Description |
|---|---|---|
/api/{name}/auth/login | GET | Initiates login or validates SSO cookie |
/api/{name}/auth/logout | GET | Clears session and redirects to SSO logout |
/api/{name}/auth/session | GET | Returns current session status |
/api/users/me | GET | Returns current user (for Payload admin bar compatibility) |
The plugin automatically adds a /api/users/me endpoint to support the Payload admin bar, which expects this endpoint to exist. This works around an issue where Payload calls /api/users/me on every page load even when disableLocalStrategy: true is set on the users collection.
import {
validateSSOSession,
verifyJWTSession,
fetchSSOSession,
getEmailFromSession,
parseCookies,
generateUserToken,
} from 'payload-auth-cookie'
import { LoginButton, AuthProvider, useAuth } from 'payload-auth-cookie/client'
# Common
SSO_COOKIE_NAME=supaku_session
SSO_LOGIN_URL=https://sso.example.com/login
SSO_LOGOUT_URL=https://sso.example.com/logout
# For Session URL validation
SSO_SESSION_URL=https://sso.example.com/api/session
# For JWT verification (alternative to SESSION_URL)
SSO_JWT_SECRET=your-jwt-signing-secret
MIT
FAQs
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
OpenAI rotated macOS signing certificates after a malicious Axios package reached its CI pipeline in a broader software supply chain attack.

Security News
Open source is under attack because of how much value it creates. It has been the foundation of every major software innovation for the last three decades. This is not the time to walk away from it.

Security News
Socket CEO Feross Aboukhadijeh breaks down how North Korea hijacked Axios and what it means for the future of software supply chain security.