npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

@voyantcloud/payload-auth

v0.0.6

Published

Voyant authentication plugin for Payload CMS

Downloads

250

Readme

@voyantcloud/payload-auth

Ticket-based authentication plugin that lets customers host Payload CMS on their own domain while trusting Voyant for identity. If a user is already logged in to the Voyant dashboard, they land in Payload Admin signed-in — no duplicated credentials, no cross-site cookies.

What this plugin does

  • Adds custom endpoints to your Payload instance:
    • GET /api/auth/voyant/login — completes the one-time ticket handoff and redirects to Admin.
    • GET /api/auth/voyant/start — initiates SSO flow for direct /admin access (redirects to Voyant Dashboard).
    • POST /api/auth/voyant/consume — JSON API used by the optional login component.
  • Auto-provisions Payload users keyed by the Voyant account ID and maps roles (default: Voyant super admins → super-admin, everyone else → user).
  • Issues the Payload auth cookie directly inside the Payload app using your existing auth.cookies configuration (no forced SameSite).
  • Optionally sets a payload-tenant cookie if you provide a resolver hook.
  • Injects a lightweight “Continue with Voyant” message on the login screen (disabled with installAdminLoginButton: false).
  • Exports helpers (getVoyantUserFields, defaultRoleMapper, defaultMapVoyantUser) for advanced customization.

Prerequisites

  • A Voyant API deployment exposing POST /auth/payload/validate-ticket (already provided by apps/api).
  • A Voyant API key scoped to the target workspace.
  • The workspace identifier the Voyant dashboard uses for the customer tenant.
  • A Payload collection with auth: true (usually users).

Install

pnpm add @voyantcloud/payload-auth

Quick start

// payload.config.ts
import { buildConfig } from 'payload'
import { voyantAuthPlugin } from '@voyantcloud/payload-auth'

export default buildConfig({
  collections: [Users],
  plugins: [
    voyantAuthPlugin({
      voyantWorkspaceId: process.env.VOYANT_WORKSPACE_ID!,
      voyantApiKey: process.env.VOYANT_API_KEY!,
      collectionSlug: 'users',
    }),
  ],
})

That’s it — the plugin registers the endpoints, injects the voyantId field, and adds the optional login helper.

Linking from Voyant Dash

When a user clicks "Admin" in Voyant Dash, send them to the customer-hosted Payload admin with a one-time ticket:

https://customersite.com/api/auth/voyant/login?ticket=<ticket>&return_to=/admin

Tickets are generated by POST /auth/payload/generate-ticket inside the Voyant API. They expire in ~2 minutes and are single-use.

Options

voyantAuthPlugin({
  voyantWorkspaceId: 'ws_xxx',        // Required workspace identifier
  voyantApiKey: 'voyant_api_abc',     // Required API key from Voyant dashboard

  collectionSlug: 'users',          // Auth-enabled collection (default "users")
  voyantIdFieldName: 'voyantId',     // Override the injected field name
  addVoyantIdField: true,            // Disable if you manage the field yourself
  installAdminLoginButton: true,     // Disable to skip the login helper component

  roleMapping: (profile) =>          // Optional custom role mapper
    profile.isSuperAdmin ? ['super-admin'] : ['user'],

  mapVoyantUser: ({ profile, roles }) => ({
    fullName: profile.fullName,
    avatarUrl: profile.pictureUrl,
    roles,
  }),

  cookieOverrides: { sameSite: 'none', secure: true },

  allowedReturnToHosts: ['admin.customer.com'],

  resolveTenantIdByHost: async ({ req }) => {
    const host = req.headers.get('host')
    if (!host) return
    const tenant = await req.payload.find({
      collection: 'tenants',
      where: { domain: { equals: host } },
      limit: 1,
    })
    return tenant.docs[0]?.id
  },
})

Added fields

By default the plugin appends the following field to your auth collection (unless addVoyantIdField: false):

import { getVoyantUserFields } from '@voyantcloud/payload-auth'

getVoyantUserFields() // → [{ name: 'voyantId', type: 'text', unique: true, saveToJWT: true }]

If you want to manage the field yourself, set addVoyantIdField: false and call getVoyantUserFields in your collection definition.

Endpoint reference

GET /api/auth/voyant/login

Query params:

| Param | Description | |-------------|----------------------------------| | ticket | Voyant one-time ticket (required) | | return_to | Relative or allowed absolute URL |

Behaviour:

  1. Validates the ticket with Voyant API.
  2. Creates/updates the Payload user.
  3. Signs the Payload auth cookie (respecting your configured cookie attributes).
  4. Optionally sets payload-tenant if resolveTenantIdByHost returns an ID.
  5. Redirects to return_to or /admin.

GET /api/auth/voyant/start

Initiates SSO flow for users who navigate directly to /admin without a ticket. Redirects to the Voyant Dashboard for authentication, which then redirects back with a ticket.

POST /api/auth/voyant/consume

Body:

{
  "ticket": "abc123",           // required
  "return_to": "/admin"         // optional
}

Returns { ok: true, redirectTo: "https://..." } and sets cookies. Used by the login helper to finish sign-in without a full page reload.

Front-end helper

When installAdminLoginButton is true, the plugin injects a client component on the Payload login screen. It:

  • Detects ticket/return_to query params and POSTs to /api/auth/voyant/consume automatically.
  • Shows guidance for users who arrive directly (without a ticket) to go back to the Voyant dashboard.

Disable the helper by setting installAdminLoginButton: false and render your own UI if preferred.

Helper exports

import {
  DEFAULT_VOYANT_ID_FIELD,
  getVoyantUserFields,
  defaultRoleMapper,
  defaultMapVoyantUser,
  resolveRoles,
} from '@voyantcloud/payload-auth'
  • defaultRoleMapper(profile)['super-admin'] if profile.isSuperAdmin, otherwise ['user'].
  • defaultMapVoyantUser({ profile, roles }){ email, name, roles, voyantId }.
  • resolveRoles(profile, roleMapping) → applies function or mapping config, falling back to defaultRoleMapper.

Voyant API contract

The plugin expects the Voyant API to expose:

POST https://api.voyant.cloud/auth/payload/validate-ticket
Headers: { Authorization: "Bearer <voyantApiKey>", X-Voyant-Workspace: "ws_xxx" }
Body: { ticket, workspaceId }
Response: { valid: boolean, user?: { id, email, firstName?, lastName?, fullName?, pictureUrl?, isSuperAdmin?, workspaceId } }

Only valid tickets return user. Invalid, expired, or already-consumed tickets must yield a non-200 or { valid: false }.

Error handling

  • Missing or invalid tickets redirect to /admin/login?voyantError=....
  • The JSON endpoint responds with { ok: false, error: '...' } and HTTP 400/401.
  • Enable debug logging with debug: true (logs prefixed with [VOYANT_AUTH]).

Security notes

  • All cookies are set by the Payload app, so browsers treat them as first-party.
  • return_to defaults to same-origin paths; absolute URLs must be allow-listed via allowedReturnToHosts.
  • Tickets are single-use and short-lived (enforced server-side by Voyant API).
  • Authentication uses a per-workspace API key (sent as Authorization: Bearer …).

Complete Integration Guide

Overview

The Voyant Payload Auth plugin enables seamless SSO between the Voyant platform and your Payload CMS admin panel. There are three ways users can access the admin:

  1. From Voyant Dashboard — Click "Open Admin" on a deployment
  2. Direct URL Access — Navigate directly to /admin on your site
  3. Local Development — Use voyant dev --payload for authenticated local development

Authentication Flows

Flow 1: Dashboard → Admin

User clicks "Open Admin" in Dashboard
        ↓
Dashboard generates ticket via POST /auth/payload/generate-ticket
        ↓
User redirected to: https://yoursite.com/api/auth/voyant/login?ticket=XXX&return_to=/admin
        ↓
Plugin validates ticket, creates/updates user, sets cookie
        ↓
User lands in authenticated Admin panel

Flow 2: Direct /admin Access

User navigates to https://yoursite.com/admin
        ↓
Plugin detects no auth → redirects to /api/auth/voyant/start
        ↓
Start endpoint redirects to Voyant Dashboard: /auth/redirect?workspace=X&return_to=...
        ↓
Dashboard authenticates user, generates ticket
        ↓
User redirected back with ticket → authenticated

Flow 3: Local Development

# Initialize your project
voyant init

# Start with Payload auth
voyant dev --payload

# Or just Payload (skip dev session)
voyant dev --payload-only

This generates a ticket and opens your browser to http://localhost:3000/api/auth/voyant/login?ticket=XXX&return_to=/admin

Environment Variables

The plugin requires two environment variables:

| Variable | Description | How to Set | |----------|-------------|------------| | VOYANT_WORKSPACE_ID | Your workspace identifier | Auto-injected during deployment | | VOYANT_API_KEY | API key for ticket validation | Create in Dashboard → Developers → API Keys |

For local development, create a .env file:

VOYANT_WORKSPACE_ID=wksp_xxxxxxxxxxxxx
VOYANT_API_KEY=vyk_xxxxxxxxxxxxx

Deployment Configuration

When deploying with Voyant:

  1. VOYANT_WORKSPACE_ID is automatically injected during deployment
  2. VOYANT_API_KEY must be added manually:
    • Go to Dashboard → Developers → API Keys
    • Create a new key with appropriate scopes
    • Add it as an environment variable in Dashboard → Projects → [Your Project] → Settings → Environment Variables

Multi-Tenant Support

For multi-tenant deployments, use the resolveTenantIdByHost hook:

voyantAuthPlugin({
  voyantWorkspaceId: process.env.VOYANT_WORKSPACE_ID!,
  voyantApiKey: process.env.VOYANT_API_KEY!,
  resolveTenantIdByHost: async ({ req, profile }) => {
    const host = req.headers.get('host')
    const tenant = await req.payload.find({
      collection: 'tenants',
      where: { domain: { equals: host } },
      limit: 1,
    })
    return tenant.docs[0]?.id
  },
})

This sets a payload-tenant cookie that your collections can use for tenant isolation.

Workspace & User Model

  • Workspaces — Each workspace has a unique ID and API key
  • Users — Workspace members can access deployed Payload admin panels
  • Roles — Voyant super admins map to super-admin, others to user (customizable)

Users are automatically provisioned in Payload on first login. Subsequent logins update their profile if changed.

Troubleshooting

"Invalid or expired ticket"

  • Tickets expire after 5 minutes — try again
  • Ensure VOYANT_API_KEY is valid and has correct scopes
  • Check that VOYANT_WORKSPACE_ID matches the ticket's workspace

"User not a member of this workspace"

  • The user must be added to the workspace in Dashboard → Settings → Team

Cookies not being set (local development)

// For local development, relax cookie settings:
voyantAuthPlugin({
  voyantWorkspaceId: process.env.VOYANT_WORKSPACE_ID!,
  voyantApiKey: process.env.VOYANT_API_KEY!,
  debug: true, // Enable verbose logging
  cookieOverrides: {
    secure: false,    // Allow HTTP for localhost
    sameSite: 'Lax',
  },
})

Debug logging

Enable debug mode to see detailed authentication logs:

voyantAuthPlugin({
  // ... other options
  debug: true, // or set VOYANT_AUTH_DEBUG=true
})

Logs are prefixed with [VOYANT_AUTH].

License

MIT