Skip to main content

Organization Auth

Introduction​

Organization-scoped authentication enables server-side requests authenticated at the organization level rather than the user level. This authentication method is essential for operations that require authentication but cannot use user-scoped auth (typically when users are unauthenticated or don't have accounts yet).

Unlike user-scoped authentication which uses individual user credentials, organization-scoped auth uses a shared organization access token to perform operations on behalf of the organization. This prevents sensitive data from being publicly accessible while still allowing necessary operations for unauthenticated users.

Key Characteristics
  • Server-side only: Strictly for SSR, API handlers, and Server Actions. Never exposed client-side
  • Token storage: Encrypted tokens stored through a shared server cache adapter
  • Scheduled refresh: A protected cron job refreshes cached tokens
  • Request-time recovery: Server requests recover when no usable cached token is available

Getting started​

Before you begin

This is an advanced authentication feature. Complete the Getting Started: Pages Router or Getting Started: App Router guide first.

Configuration​

Organization-scoped authentication requires a shared server cache and token encryption in addition to the basic auth setup. This guide uses the supplied Vercel Global Config adapter.

Global Config setup

See Vercel's Global Config documentation for setup instructions.

Environment variables​

Add these environment variables to your .env.local file and configure them in your Vercel project settings:

.env.local
# Token verification
KRAKEN_AUTH_ENDPOINT="https://auth.xxxx-kraken.systems/"
KRAKEN_ACCESS_TOKEN_ISSUERS="https://api.xxxx-kraken.systems/v1/graphql/,https://auth.xxxx-kraken.systems/token/,https://support.xxxx-kraken.systems"

# Organization authentication (Kraken > API Tools > API organisations)
KRAKEN_ORGANIZATION_KEY="your-organization-secret-key"

# Vercel Global Config (automatically configured by Vercel)
GLOBAL_CONFIG="https://global-config.vercel.com/..."
# Vercel API token with access to the owning team and store
# Create one at https://vercel.com/account/tokens
VERCEL_AUTH_TOKEN="your-vercel-api-token"
# Vercel team ID
VERCEL_TEAM_ID="your-vercel-team-id"

# Token encryption
AUTH_ENCRYPTION_KEY="your-encryption-key"

# Cron job authentication (Vercel recommends at least 16 characters)
CRON_SECRET="your-cron-secret"
Generating encryption keys

Generate strong random values for the encryption key and cron secret:

# Encryption key
openssl rand -base64 32

# Cron secret
openssl rand -base64 16

Blueprint Auth creates a new 96-bit initialization vector for each encrypted organization token. It stores this value with the ciphertext. The initialization vector is not a secret.

Treat the encryption key and cron secret as sensitive credentials. Do not commit them to version control.

Auth configuration​

Automatic environment variable detection

createAuthConfig reads the encryption and Kraken settings from environment variables. createGlobalConfigCacheAdapter reads the three Vercel settings when it creates the adapter.

Server cache adapter​

Create one adapter at module scope in a file that browser code cannot import. For App Router, add import "server-only"; as the first line. Pages Router does not support this marker, so import the module only from server code.

lib/auth/cache.ts
import { createGlobalConfigCacheAdapter } from "@krakentech/blueprint-auth/cache/global-config";

export const cacheAdapter = createGlobalConfigCacheAdapter();

Adapter creation checks that its required environment values are present. It also parses the store ID and creates the Global Config client. Remote credentials and access are verified during cache operations. See the custom adapter contract when using another shared store.

Server functions​

Resolve organization auth with getAuth.org. The access token must have a verified API-KEY grant. Then create a client with getGraphQLClient.org. Use one of the following setups.

lib/queries/get-products.ts
import type { ServerActionContext } from "@krakentech/blueprint-auth";
import { getAuth, getGraphQLClient } from "@krakentech/blueprint-auth/server";
import { cacheAdapter } from "@/lib/auth/cache";
import { authConfig } from "@/lib/auth/config";
import { ProductsQuery } from "./products-query";

export async function getProducts(context: ServerActionContext) {
const auth = await getAuth.org(authConfig, { cacheAdapter, context });
if (!auth) throw new Error("Organization authentication is unavailable.");

const graphQLClient = getGraphQLClient.org(authConfig, { auth });
return graphQLClient.request(ProductsQuery);
}

Token refresh cron job​

Organization tokens expire after 60 minutes. Schedule a token refresh every 30 minutes. This interval reduces the risk that a missed run leaves an expired token.

API handler​

pages/api/auth/update-org-token.ts
import { createUpdateOrgTokenHandler } from "@krakentech/blueprint-auth/server";
import { cacheAdapter } from "@/lib/auth/cache";
import { authConfig } from "@/lib/auth/config";

export default createUpdateOrgTokenHandler(authConfig, { cacheAdapter });

Vercel cron configuration​

Create or update vercel.json at the root of your project:

vercel.json
{
"$schema": "https://openapi.vercel.sh/vercel.json",
"crons": [
{
"path": "/api/auth/update-org-token",
"schedule": "*/30 * * * *"
}
]
}

This schedule runs at minute 0 and minute 30 of each hour. See Vercel's cron expression documentation for the supported syntax and current limitations.

Cron secret required

Add CRON_SECRET to the production environment. Vercel sends this value in the Authorization header when it invokes the cron job. Treat the value as a sensitive credential. Never put it in a URL or log. See Vercel's guidance for securing cron jobs.

Usage​

Server-side only

Organization-scoped authentication is for server-side use only.

Queries​

Use organization-scoped auth when fetching data that isn't publicly accessible for unauthenticated users.

pages/signup/products.tsx
import type {
GetServerSidePropsContext,
InferGetServerSidePropsType,
} from "next";
import { getAuth, getGraphQLClient } from "@/lib/auth/server";
import { graphql } from "@/lib/graphql";
import { ProductCard } from "@/components/ProductCard";

export default function ProductsPage({
products,
}: InferGetServerSidePropsType<typeof getServerSideProps>) {
return (
<div>
<h1>Products</h1>
{products.map((product) => (
<ProductCard key={product.code} product={product} />
))}
</div>
);
}

const ProductsQuery = graphql(`
query SignupProducts {
products(availability: AVAILABLE) {
code
fullName
description
tariffs {
id
standingCharge
unitRate
}
}
}
`);

export async function getServerSideProps(context: GetServerSidePropsContext) {
const auth = await getAuth.org({ context });
if (!auth) throw new Error("Organization authentication is unavailable.");

const graphQLClient = getGraphQLClient.org({ auth });
const { products } = await graphQLClient.request(ProductsQuery);

if (!products?.length) {
return { notFound: true };
}

return {
props: { products },
};
}

Mutations​

Use organization-scoped auth for mutations that create or modify data for users who aren't authenticated yet. A common example is account creation.

pages/api/signup/create-account.ts
import type { NextApiRequest, NextApiResponse } from "next";
import { getAuth, getGraphQLClient } from "@/lib/auth/server";
import { graphql } from "@/lib/graphql";
import { z } from "zod";

const CreateAccountMutation = graphql(`
mutation CreateAccount($input: CreateAccountInput!) {
createAccount(input: $input) {
account {
id
number
}
}
}
`);

const createAccountSchema = z.object({
email: z.string().email(),
// ... other fields
});

export default async function handler(
req: NextApiRequest,
res: NextApiResponse,
) {
if (req.method !== "POST") {
return res.status(405).json({ error: "Method not allowed" });
}

const result = createAccountSchema.safeParse(req.body);
if (!result.success) {
return res.status(400).json({ error: "Invalid request data" });
}

const {
email,
// ... other fields
} = result.data;

try {
// The user has no account yet, so use organization auth.
const auth = await getAuth.org({ context: { req, res } });
if (!auth) {
return res.status(503).json({ error: "Authentication unavailable" });
}

const graphQLClient = getGraphQLClient.org({ auth });
const { createAccount } = await graphQLClient.request(
CreateAccountMutation,
{
input: {
email,
// ... other fields
},
},
);

return res.redirect(
303,
`/dashboard/accounts/${createAccount.account.number}`,
);
} catch (error) {
console.error("Account creation failed:", error);
return res.status(500).json({ error: "Account creation failed" });
}
}

How it works​

Token storage and recovery

Blueprint Auth uses this process for organization tokens:

  1. It reads an encrypted token through the adapter's normal read path.
  2. It requires a versioned value that contains a unique initialization vector and the ciphertext.
  3. If the cached value is absent or invalid, it requests another adapter read with bypassCache: true. Adapters with another cache layer can use this hint to bypass it.
  4. If no valid value is available, it requests a replacement token from Kraken.
  5. It encrypts the replacement and tries to store it for later requests.

If Kraken cannot provide a replacement token, organization authentication fails.

Security considerations​

Critical Security Requirements
  1. Environment variables: Treat KRAKEN_ORGANIZATION_KEY, AUTH_ENCRYPTION_KEY, and CRON_SECRET as highly sensitive credentials
  2. Cron endpoint protection: The token refresh endpoint is protected by CRON_SECRET. Never expose this value
  3. Token encryption: All cached tokens are encrypted before storage in the shared cache
  4. Minimal scope: Organization tokens grant broader permissions than user tokens. Use user-scoped auth whenever possible

FAQ​

When should I use organization-scoped vs user-scoped auth?

Use organization-scoped auth when:

  • Users are unauthenticated but need to perform authenticated operations (e.g., account creation, signup flows)
  • Users don't have accounts yet but need access to organization data (e.g., product catalogs, pricing)
  • Operations require organization-level permissions rather than user-level permissions

Use user-scoped auth when:

  • User is authenticated and performing actions on their own account
  • Accessing user-specific data (account details, usage, billing, payment methods)
  • Standard logged-in user workflows (dashboard, settings, account management)
How does token caching work?

Blueprint Auth encrypts organization tokens with AES-256-GCM before it stores them through the configured adapter. Each stored value contains a format version, a new random initialization vector, and the ciphertext.

Version 44 uses a new cache key. It does not read, change, or delete the item that earlier versions use. This isolation prevents a preview deployment from writing a value that an earlier production deployment cannot read.

The cron job requests a new token, encrypts it, and writes it to the version 44 item.

Can I use organization-scoped auth in client components?

No. Organization-scoped auth is strictly server-side only. Exposing the organization token to client-side code would be a critical security vulnerability, granting malicious actors organization-level permissions.

Use it only in:

  • Server-Side Rendering (getServerSideProps, getStaticProps in Pages Router)
  • Server Components (App Router)
  • API Route Handlers (pages/api/... in Pages Router, app/.../route.ts in App Router)
  • Server Actions (App Router)

Next steps​

Now that you have organization-scoped authentication configured, explore these related guides:

API Reference​

Quick reference to relevant functions: