Kraken OAuth
Configure Kraken OAuth in your Next.js app with or without a login page. Your app starts sign-in on Kraken’s hosted login screen, and Kraken redirects visitors back to complete sign-in. Customers can use the same Kraken login across applications without entering their password on each site.
Getting started​
This guide extends the Blueprint Auth setup for the Pages Router or the App Router. Complete one setup guide before you continue. Choose With a login page to offer Kraken OAuth on a /login page, or Without a login page to send unauthenticated visitors directly to Kraken.
Register an OAuth client with Kraken and obtain its client ID. Register the full callback URL for each site origin, for example https://www.example.com/api/auth/oauth/callback. The path must match apiRoutes.krakenOAuth in the configuration below.
Both setups need the same Kraken environment variables. Define them in .env.local before choosing your flow:
KRAKEN_AUTH_ENDPOINT="https://auth.your_environment-kraken.systems/"
KRAKEN_OAUTH_CLIENT_ID="your_kraken_oauth_client_id"
Replace your_environment with your Kraken environment name and your_kraken_oauth_client_id with your registered client ID. Add the exact trusted access token issuers to validation in your existing createAuthConfig call:
validation: {
accessTokenIssuers: [
"https://api.your_environment-kraken.systems/v1/graphql/",
"https://auth.your_environment-kraken.systems/token/",
"https://support.your_environment-kraken.systems",
],
},
Keep this validation entry when applying either configuration below. Alternatively, set KRAKEN_ACCESS_TOKEN_ISSUERS to these exact issuers as one comma-separated value in .env.local; the config array takes precedence.
For the OAuth issuer, add token/ to the exact Kraken auth endpoint. Replace the example domains with your environment’s values. Issuer matching is case-sensitive, including paths and trailing slashes.
When deploying to Vercel, configure environment variables in your project dashboard rather than committing them to .env files. Follow the Vercel environment variable documentation for deployment settings.
Use environment variables for supported options to keep deployment-specific values outside your code.
With a login page​
Offer Kraken OAuth on your /login page alongside any other login methods. Keep appRoutes.login so middleware sends unauthenticated visitors to that page.
Configuration​
| Option | Description | Type | Environment variable | Required |
|---|---|---|---|---|
krakenConfig.authEndpoint | Kraken auth endpoint | string | KRAKEN_AUTH_ENDPOINT | ✅ |
krakenConfig.oauthClientId | Kraken OAuth client ID | string | KRAKEN_OAUTH_CLIENT_ID | ✅ |
validation.accessTokenIssuers | Exact trusted access token issuers | string[] | KRAKEN_ACCESS_TOKEN_ISSUERS | ✅ |
apiRoutes.krakenOAuth | OAuth callback route | string | ✅ | |
apiRoutes.startKrakenOAuth | OAuth start GET route | string | Pages Router link example | |
appRoutes.login.pathname | Login page | string | ✅ |
Add the OAuth callback route to the auth config from your getting-started guide. The Pages Router link example also configures startKrakenOAuth; the App Router Server Action does not need it.
The apiRoutes.login entry and its API handler are only needed if you also offer email-and-password login; keep them if you use them.
import { createAuthConfig } from "@krakentech/blueprint-auth";
export const authConfig = createAuthConfig({
apiRoutes: {
graphql: { kraken: "/api/graphql/kraken" },
krakenOAuth: "/api/auth/oauth/callback",
startKrakenOAuth: "/api/auth/oauth/start",
logout: "/api/auth/logout",
session: "/api/auth/session",
},
appRoutes: {
dashboard: { pathname: "/dashboard" },
home: { pathname: "/" },
login: { pathname: "/login" },
},
});
Middleware​
Use createAuthMiddleware(authConfig) as shown in your router’s getting-started guide. Middleware protects routes and refreshes auth tokens.
Middleware redirects unauthenticated visitors from protected pages to your configured login page. Add a link or action there to start Kraken OAuth.
OAuth callback​
The callback handler verifies Kraken’s tokens and stores them as cookies. On success, it redirects to the dashboard or a saved same-origin return path. Authentication failures redirect to the login page with an error query parameter by default. To use a separate error page, configure appRoutes.oauthError and follow the public error page example.
If token verification keys are unavailable, the callback returns HTTP 503 instead of redirecting.
Create the Kraken OAuth API handler using createKrakenOAuthHandler.
- Pages Router
- App Router
import { createKrakenOAuthHandler } from "@krakentech/blueprint-auth/server";
import { authConfig } from "@/lib/auth/config";
export default createKrakenOAuthHandler(authConfig);
import { createKrakenOAuthHandler } from "@krakentech/blueprint-auth/server";
import { authConfig } from "@/lib/auth/config";
export const GET = createKrakenOAuthHandler(authConfig);
Return path safety​
Pass nextPage when you want sign-in to return to a specific page. Blueprint Auth accepts only destinations on the request’s origin: the same scheme, host, and port. It rejects malformed or cross-origin destinations and strips fragments, storing only the path and query in oauthNextPage.
By default, the cookie expires after ten minutes; customization.getCookieOptions can override its lifetime. The callback reads, deletes, and revalidates it before processing the authorisation code. If no valid return path remains, successful sign-in redirects to the dashboard.
Initiate the OAuth flow​
When a visitor selects Kraken sign-in, the start handler or Server Action stores a one-time code verifier in a cookie. It then redirects the browser to Kraken using Proof Key for Code Exchange (PKCE).
- Pages Router
- App Router
Create a GET start handler. Route Handlers in app/ can coexist with Pages Router pages.
import {
createStartKrakenOAuthHandler,
} from "@krakentech/blueprint-auth/server";
import { authConfig } from "@/lib/auth/config";
export const GET = createStartKrakenOAuthHandler(authConfig);
Add a link to your login page alongside any other login methods. Pass through the login URL’s nextPage parameter to preserve the same-origin return path:
import type {
GetServerSidePropsContext,
InferGetServerSidePropsType,
} from "next";
import { authConfig } from "@/lib/auth/config";
export default function LoginPage({
errorCode,
startKrakenOAuthHref,
}: InferGetServerSidePropsType<typeof getServerSideProps>) {
return (
<>
{errorCode && (
<p role="alert">Sign-in failed ({errorCode}). Please try again.</p>
)}
<a href={startKrakenOAuthHref}>Log in with Kraken</a>
</>
);
}
In the same pages/login.tsx file, add getServerSideProps to build the start URL and read callback errors:
export function getServerSideProps({ query }: GetServerSidePropsContext) {
let startKrakenOAuthHref = authConfig.apiRoutes.startKrakenOAuth;
if (typeof query.nextPage === "string") {
const nextPage = new URLSearchParams({ nextPage: query.nextPage });
startKrakenOAuthHref += `?${nextPage}`;
}
return {
props: {
errorCode: typeof query.error === "string" ? query.error : null,
startKrakenOAuthHref,
},
};
}
Add startKrakenOAuth to the exports of the createAppRouterAuth call in lib/auth/server.ts from the getting-started guide. Keep your existing exports and factory options (including cacheAdapter if you use organization auth):
import { createAppRouterAuth } from "@krakentech/blueprint-auth/server";
import { cookies, headers } from "next/headers";
import { cache } from "react";
import { authConfig } from "./config";
export const {
getAuth,
getGraphQLClient,
getSession,
login,
logout,
redirectToLogin,
startKrakenOAuth,
} = createAppRouterAuth(authConfig, { cache, cookies, headers });
Call startKrakenOAuth from a Server Action to set the verifier cookie and redirect the browser when a visitor submits the form:
"use server";
import type {
AppRouterStartKrakenOAuthParams,
} from "@krakentech/blueprint-auth/server";
import { startKrakenOAuth } from "@/lib/auth/server";
export async function startKrakenOAuthAction(
searchParams: AppRouterStartKrakenOAuthParams["searchParams"],
) {
await startKrakenOAuth({ searchParams });
}
startKrakenOAuth redirects instead of returning form state, so a submit button with useFormStatus is enough to show progress and prevent duplicate submissions:
"use client";
import { useFormStatus } from "react-dom";
export function OAuthSubmitButton() {
const { pending } = useFormStatus();
return (
<button type="submit" disabled={pending}>
{pending ? "Redirecting…" : "Log in with Kraken"}
</button>
);
}
Add the form to your login page alongside any other login methods. Bind the page’s search parameters to preserve the same-origin nextPage return path:
import { startKrakenOAuthAction } from "./actions";
import { OAuthSubmitButton } from "./OAuthSubmitButton";
export default async function LoginPage({ searchParams }: PageProps<"/login">) {
const params = await searchParams;
const action = startKrakenOAuthAction.bind(null, params);
const errorCode = typeof params.error === "string" ? params.error : null;
return (
<>
{errorCode && (
<p role="alert">Sign-in failed ({errorCode}). Please try again.</p>
)}
<form action={action}>
<OAuthSubmitButton />
</form>
</>
);
}
Without a login page​
If Kraken provides your only login screen, omit appRoutes.login and configure a public error page. The same Kraken environment variables described above apply.
Configuration​
| Option | Description | Type | Environment variable | Required |
|---|---|---|---|---|
krakenConfig.authEndpoint | Kraken auth endpoint | string | KRAKEN_AUTH_ENDPOINT | ✅ |
krakenConfig.oauthClientId | Kraken OAuth client ID | string | KRAKEN_OAUTH_CLIENT_ID | ✅ |
validation.accessTokenIssuers | Exact trusted access token issuers | string[] | KRAKEN_ACCESS_TOKEN_ISSUERS | ✅ |
apiRoutes.krakenOAuth | OAuth callback route | string | ✅ | |
apiRoutes.startKrakenOAuth | OAuth start GET route | string | ✅ | |
appRoutes.oauthError.pathname | Public OAuth failure page | string | ✅ |
Without a login page, configure startKrakenOAuth so server and client code can redirect visitors to sign in. OAuth setups with a login page do not always need this route. Update your existing auth config:
- Remove
appRoutes.login. RemoveapiRoutes.loginif you no longer offer email-and-password login. - Add
apiRoutes.krakenOAuthandapiRoutes.startKrakenOAuthfor the callback and start routes. - Add
appRoutes.oauthError.pathnamefor a public failure page. - Keep your
validationentry and the other API routes from the getting-started guide.
The following example shows the route configuration:
import { createAuthConfig } from "@krakentech/blueprint-auth";
export const authConfig = createAuthConfig({
apiRoutes: {
graphql: { kraken: "/api/graphql/kraken" },
krakenOAuth: "/api/auth/oauth/callback",
startKrakenOAuth: "/api/auth/oauth/start",
logout: "/api/auth/logout",
session: "/api/auth/session",
},
appRoutes: {
dashboard: { pathname: "/dashboard" },
home: { pathname: "/" },
oauthError: { pathname: "/auth/error" },
},
});
Middleware​
Use createAuthMiddleware(authConfig) as in your router’s getting-started guide. Middleware starts OAuth directly for unauthenticated visitors to protected pages. Keep the public error page outside protected paths so callback failures do not restart the flow. Middleware requires the callback, start, and error routes above, even though it starts OAuth without requesting the start route.
OAuth callback​
Create the callback at apiRoutes.krakenOAuth using createKrakenOAuthHandler. It verifies and stores the tokens, then redirects to a saved same-origin return path or the dashboard. Authentication failures redirect to the public appRoutes.oauthError page with an error query parameter. If token verification keys are unavailable, the callback returns HTTP 503 instead of redirecting.
- Pages Router
- App Router
import { createKrakenOAuthHandler } from "@krakentech/blueprint-auth/server";
import { authConfig } from "@/lib/auth/config";
export default createKrakenOAuthHandler(authConfig);
import { createKrakenOAuthHandler } from "@krakentech/blueprint-auth/server";
import { authConfig } from "@/lib/auth/config";
export const GET = createKrakenOAuthHandler(authConfig);
OAuth start route​
Create a separate GET Route Handler at apiRoutes.startKrakenOAuth (in app/, even if your pages use the Pages Router). Keep API routes out of the middleware matcher.
import {
createStartKrakenOAuthHandler,
} from "@krakentech/blueprint-auth/server";
import { authConfig } from "@/lib/auth/config";
export const GET = createStartKrakenOAuthHandler(authConfig);
Public error page​
Create a public page at appRoutes.oauthError.pathname. Keep it outside protected paths such as /dashboard to avoid restarting a failed OAuth attempt. Read the error search parameter, show a sign-in failure message, and add a retry link to apiRoutes.startKrakenOAuth. These examples display the error code for troubleshooting and link to the configured start route:
- Pages Router
- App Router
import { useRouter } from "next/router";
export default function OAuthErrorPage() {
const { query } = useRouter();
const errorCode = typeof query.error === "string" ? query.error : null;
return (
<main>
<p role="alert">
Sign-in failed{errorCode ? ` (${errorCode})` : ""}. Please try again.
</p>
<a href="/api/auth/oauth/start">Try again</a>
</main>
);
}
export default async function OAuthErrorPage({
searchParams,
}: PageProps<"/auth/error">) {
const params = await searchParams;
const errorCode = typeof params.error === "string" ? params.error : null;
return (
<main>
<p role="alert">
Sign-in failed{errorCode ? ` (${errorCode})` : ""}. Please try again.
</p>
<a href="/api/auth/oauth/start">Try again</a>
</main>
);
}
Initiate the OAuth flow​
Add an explicit sign-in link in either router to request a same-origin return path:
<a href="/api/auth/oauth/start?nextPage=%2Faccount">Log in with Kraken</a>
Omit nextPage to return to the dashboard by default. Protected pages start OAuth automatically through middleware.
Explanation: OAuth flow​
Kraken OAuth uses a one-time code verifier to protect sign-in without a client secret. This section explains the values and cookies behind the setup above.
This integration uses Proof Key for Code Exchange (PKCE). Instead of a static client secret, Blueprint Auth generates a one-time code verifier and challenge for each login. It stores the verifier in a pkceVerifier cookie. You do not need to obtain, store, or configure a client secret.
OAuth values​
| Name | Description |
|---|---|
| Client ID | Kraken generates this public identifier when you register your application. Kraken uses it to identify your application’s requests. |
| Client secret | Some OAuth flows use this confidential value to authenticate an application. This integration uses PKCE instead. |
| Redirect URI | Kraken sends the browser to this URL after authorisation. It must match a redirect URI you registered with Kraken. |
| Authorize URI | Your app redirects the browser to this Kraken endpoint to start authorisation. Its query parameters include the client ID, redirect URI, and code challenge. |
A code challenge is a cryptographic value your app generates from the code verifier and sends to Kraken during authorisation. Kraken checks that the verifier matches the challenge during the token exchange. This check prevents someone who intercepts an authorisation code from exchanging it without the verifier.
Blueprint Auth sets pkceVerifier when the flow starts. If you provide a same-origin return path, startKrakenOAuth stores it in oauthNextPage, which expires after ten minutes by default. The callback reads and deletes that cookie before processing the authorisation code.
After a successful exchange and token verification, Blueprint Auth sets accessToken, refreshToken, and oAuthIdToken. It does not use sub or authProvider cookies.
The Kraken auth server can set other cookies, such as octosession.
Session state​
After Kraken OAuth sign-in, Blueprint Auth sets these session fields:
| Field | Value | Description |
|---|---|---|
authMethod | "oauth" | The verified grant is OPENID-CONNECT |
authSource | "web" | The token came from the web accessToken cookie |
isAuthenticated | true | The request has a verified user token |
See Session management for an overview of the session lifecycle.
Check the authentication method before you show method-specific user interface. Use the factory call for your router.
- Pages Router
- App Router
import type { GetServerSidePropsContext } from "next";
import { getSession } from "@/lib/auth/server";
type AccountPageProps = { usedOAuth: boolean };
export default function AccountPage({ usedOAuth }: AccountPageProps) {
return (
<p>
{usedOAuth
? "Authenticated with OAuth"
: "Authenticated without OAuth"}
</p>
);
}
export async function getServerSideProps(context: GetServerSidePropsContext) {
const session = await getSession({ context });
return {
props: {
usedOAuth: session.authMethod === "oauth",
},
};
}
import { getSession } from "@/lib/auth/server";
export default async function AccountPage() {
const session = await getSession();
return (
<p>
{session.authMethod === "oauth"
? "Authenticated with OAuth"
: "Authenticated without OAuth"}
</p>
);
}
FAQ​
How can I use OAuth during local development?
Kraken accepts .localhost subdomains as callback hosts, but not plain localhost. Choose one:
Run HTTPS with Portless​
Configure Portless for HTTPS, then run your app at myapp.localhost:
portless myapp next dev
Open https://myapp.localhost and register https://myapp.localhost/api/auth/oauth/callback with Kraken. Add the HTTPS origin to your existing trusted request origins in .env.local:
ALLOWED_REQUEST_ORIGINS="https://myapp.localhost"
Run HTTPS with a hosts file and Next.js​
Add this entry to /etc/hosts or your OS’s hosts file to resolve myapp.localhost:
127.0.0.1 myapp.localhost
Start Next.js with HTTPS on that hostname:
pnpm exec next dev --experimental-https -H myapp.localhost
Open https://myapp.localhost:3000 and register https://myapp.localhost:3000/api/auth/oauth/callback with Kraken. Add that origin, including port 3000, to your existing trusted request origins:
ALLOWED_REQUEST_ORIGINS="https://myapp.localhost:3000"
For either method, preserve other origins you need in the comma-separated list. If you set validation.allowedRequestOrigins in code, update that list instead: it takes precedence over ALLOWED_REQUEST_ORIGINS.
Match the HTTPS scheme, hostname, and port you actually use; omit the callback path. See trusted request origins for the cookie-authenticated POST handlers that require this setup. The OAuth GET routes do not require this allowlist.
Start OAuth from the registered origin: Blueprint Auth derives the callback’s scheme, host, and port from the request.
What happens when OAuth starts?
The start handler and startKrakenOAuth:
- Save the same-origin return path, if you provide one.
- Generate a one-time code verifier and challenge.
- Store the verifier in an HTTP-only cookie for the callback.
- Build the Kraken authorisation URL from the client ID and callback URL, then redirect the browser.
How is the OAuth flow initiated?
With a login page, visitors follow a link to the OAuth start handler or submit a Server Action that calls startKrakenOAuth. Without one, middleware starts OAuth for protected pages, or visitors follow a link to apiRoutes.startKrakenOAuth. In both cases, Kraken prompts them to authenticate and authorise the application.
How is the OAuth flow completed?
After a visitor authorises the application, Kraken redirects their browser to your OAuth callback with an authorisation code. The callback handler then:
- Reads the code verifier from the cookie.
- Exchanges the code and verifier for an identity token, access token, and refresh token.
- Removes
pkceVerifierafter the exchange succeeds. - Verifies the identity token issuer and OAuth client ID audience.
- Verifies that the access token is an OAuth access token.
- Requires both verified tokens to have the same
sub. - Stores
accessToken,refreshToken, andoAuthIdToken. - Redirects to the saved same-origin return path or the dashboard.
Blueprint Auth does not restore pkceVerifier if token verification fails after the exchange. It does not perform an OpenID Connect nonce check.
How are errors handled?
The callback redirects authentication failures to appRoutes.oauthError when configured, or to the login page by default. Read the error query parameter and show a sign-in failure message with a retry link, as in the public error page example. If token verification keys are unavailable, the callback returns HTTP 503 instead of redirecting.
How are the tokens used?
The access token authenticates subsequent Kraken API requests. Blueprint Auth uses the refresh token to replace the access token before it expires. The accessToken cookie expiry comes from the verified exp claim.
How does Blueprint Auth refresh an access token?
Blueprint Auth sends the refresh token to Kraken before the access token expires. It verifies the replacement access token before use. If Kraken rejects an expired, invalid, or unauthorized refresh token, Blueprint Auth ends the local session. See Session management for the complete refresh failure policy.
What happens when a user logs out?
Logout removes the managed session cookies and oAuthIdToken. It keeps pkceVerifier so an OAuth flow that is in progress can finish.