Migrating to v40
This guide will help you migrate @krakentech/blueprint-auth to version 40,
which introduces App Router support and a unified configuration system.
Overview
Version 40 of @krakentech/blueprint-auth eliminates configuration duplication
by introducing a single source of truth for authentication. Instead of repeating
the same settings across middleware, API routes, and client code, you now define
your authentication configuration once in a central authConfig object and
import it wherever needed.
This becomes possible because all user-facing functions now expect a subset of a single unified interface. Previous versions came close to this design, but some functions had slightly different configuration requirements that prevented using a single shared config object. Now that these interfaces have been aligned, you can pass the same config everywhere.
This architectural shift means you maintain a single source of truth instead of duplicating configuration across multiple files. When you need to update authentication behavior, you make the change in one place rather than hunting through your codebase to ensure all instances stay in sync.
The new implementation brings full App Router support through automatic context detection, meaning the same utilities work seamlessly whether you're using the Pages Router or the App Router. All handlers have been converted to factory functions that accept your config as a parameter, creating a consistent API surface across the entire package. You'll also get improved error handling with structured error codes and messages that guide you toward solutions, as well as better TypeScript inference.
For complete details on all functions, hooks, and configuration options mentioned in this guide, see the API Reference.
If you identify a breaking change we've missed, please reach out so we can add it to this page. If you run into any issues during migration, don't hesitate to reach out to the team for support.
Migration Steps
We'll work from the inside out: first create the centralized config, then wire it up to your runtime components (middleware, API handlers, client), update any custom code that references old APIs, and finally verify everything works.
Step 1: Update packages
The @krakentech/blueprint-auth and @krakentech/blueprint-api packages must
be upgraded together.
- pnpm
- npm
- yarn
- bun
pnpm update @krakentech/blueprint-auth@^40 @krakentech/blueprint-api@^4
If your project relies on one of the other packages listing
@krakentech/blueprint-api as a peer-dependency, you must update them as well:
| Package | Command |
|---|---|
@krakentech/blueprint-utils | pnpm update @krakentech/blueprint-utils@^55 |
npm update @krakentech/blueprint-auth@^40 @krakentech/blueprint-api@^4
If your project relies on one of the other packages listing
@krakentech/blueprint-api as a peer-dependency, you must update them as well:
| Package | Command |
|---|---|
@krakentech/blueprint-utils | npm update @krakentech/blueprint-utils@^55 |
yarn upgrade @krakentech/blueprint-auth@^40 @krakentech/blueprint-api@^4
If your project relies on one of the other packages listing
@krakentech/blueprint-api as a peer-dependency, you must update them as well:
| Package | Command |
|---|---|
@krakentech/blueprint-utils | yarn upgrade @krakentech/blueprint-utils@^55 |
bun update @krakentech/blueprint-auth@^40 @krakentech/blueprint-api@^4
If your project relies on one of the other packages listing
@krakentech/blueprint-api as a peer-dependency, you must update them as well:
| Package | Command |
|---|---|
@krakentech/blueprint-utils | bun update @krakentech/blueprint-utils@^55 |
If you try to upgrade to @krakentech/blueprint-api@4.0.0 or @krakentech/blueprint-auth@40.0.0 or @krakentech/blueprint-utils@55.0.0 (without prefixing the version with ^ or ~) you will get a warning about a faulty release. Please skip these versions and upgrade directly to the latest version instead.
Step 2: Configuration
Create a centralized config that you'll reuse everywhere. See the
createAuthConfig API reference for all
available configuration options.
import { createAuthConfig } from "@krakentech/blueprint-auth";
export const authConfig = createAuthConfig({
apiRoutes: {
graphql: { kraken: "/api/graphql" },
login: "/api/auth/login",
logout: "/api/auth/logout",
session: "/api/auth/session",
},
appRoutes: {
dashboard: { pathname: "/dashboard" },
login: { pathname: "/login" },
},
krakenConfig: {
graphqlEndpoint: process.env.KRAKEN_GRAPHQL_ENDPOINT,
},
// Optional: customize behavior
customization: {
getCookieOptions: (cookieName) => {
if (cookieName === "accessToken") {
return { maxAge: 3600 };
}
return undefined; // use defaults
},
},
});
The router option is required if you're using client-side authentication.
However, it is not part of authConfig. Instead, it's passed as the second
argument to createClientSideAuth (see Step 5 below).
The router option must be set to either "pages-router" or "app-router"
depending on your Next.js setup. This tells the auth package which routing
strategy to use for client-side redirects.
If you're only using server-side functions (middleware, API handlers,
getServerSideProps, etc.), you don't need to specify a router.
Instead of repeating your authentication configuration across multiple files,
you now maintain everything in one place. When you need to update your auth
setup, you change it here and the update automatically applies everywhere you've
imported authConfig.
If you're migrating from a previous version, review the config-related breaking
changes below before creating your authConfig. This ensures you start with a
valid configuration.
| Change | What to do |
|---|---|
| Customization options |
|
| Validation options |
|
| Route extractors |
|
| Custom headers signature |
|
| Cookie options return |
|
See the Breaking Changes Reference section for detailed examples of each migration.
Now that you have your centralized config, the next step is to use it in your middleware where authentication checks happen first.
Step 3: Middleware
Replace the middleware with the new
createAuthMiddleware factory
function.
// Before
import { authMiddleware } from "@krakentech/blueprint-auth/middleware";
import type { NextRequest, NextResponse } from "next/server";
export async function middleware(req: NextRequest) {
const res = NextResponse.next();
return await authMiddleware({
appRoutes: {
dashboard: { pathname: "/dashboard" },
login: { pathname: "/login" },
},
krakenConfig: {
graphqlEndpoint: process.env.KRAKEN_GRAPHQL_ENDPOINT,
},
req,
res,
});
}
// After
import { createAuthMiddleware } from "@krakentech/blueprint-auth/middleware";
import { authConfig } from "@/lib/auth/config";
export const middleware = createAuthMiddleware(authConfig);
The matcher export defines which routes the middleware protects. If your
existing middleware already has a working matcher, you can keep it as-is. This
step only changes how you create the middleware function itself.
With middleware protecting your routes, let's update the API handlers that actually perform authentication operations.
Step 4: API handlers
Replace each handler with a one-liner factory call. See the API reference for
details: createLoginHandler,
createLogoutHandler,
createSessionHandler,
createGraphQLHandler,
createKrakenOAuthHandler,
createUpdateOrgTokenHandler.
- Login
- Logout
- Session
- GraphQL
- Kraken OAuth
- Update Org Token
// Before
import { loginHandler } from "@krakentech/blueprint-auth/server";
import type { NextApiRequest, NextApiResponse } from "next";
export default async function handler(
req: NextApiRequest,
res: NextApiResponse,
) {
await loginHandler({
krakenConfig: {
/* ... */
},
req,
res,
// ... more config
});
}
// After
import { createLoginHandler } from "@krakentech/blueprint-auth/server";
import { authConfig } from "@/lib/auth/config";
export default createLoginHandler(authConfig);
// Before
import { logoutHandler } from "@krakentech/blueprint-auth/server";
import type { NextApiRequest, NextApiResponse } from "next";
export default async function handler(
req: NextApiRequest,
res: NextApiResponse,
) {
await logoutHandler({ req, res });
}
// After
import { createLogoutHandler } from "@krakentech/blueprint-auth/server";
export default createLogoutHandler();
// Before
import { sessionHandler } from "@krakentech/blueprint-auth/server";
import type { NextApiRequest, NextApiResponse } from "next";
export default async function handler(
req: NextApiRequest,
res: NextApiResponse,
) {
await sessionHandler({ req, res });
}
// After
import { createSessionHandler } from "@krakentech/blueprint-auth/server";
export default createSessionHandler();
// Before
import { graphqlHandler } from "@krakentech/blueprint-auth/server";
import type { NextApiRequest, NextApiResponse } from "next";
export default async function handler(
req: NextApiRequest,
res: NextApiResponse,
) {
await graphqlHandler({
krakenConfig: {
/* ... */
},
req,
res,
// ... more config
});
}
// After
import { createGraphQLHandler } from "@krakentech/blueprint-auth/server";
import { authConfig } from "@/lib/auth/config";
export default createGraphQLHandler(authConfig);
// Before
import { krakenOAuthHandler } from "@krakentech/blueprint-auth/server";
import type { NextApiRequest, NextApiResponse } from "next";
export default async function handler(
req: NextApiRequest,
res: NextApiResponse,
) {
await krakenOAuthHandler({
appRoutes: {
/* ... */
},
apiRoutes: {
/* ... */
},
krakenConfig: {
/* ... */
},
req,
res,
// ... more config
});
}
// After
import { createKrakenOAuthHandler } from "@krakentech/blueprint-auth/server";
import { authConfig } from "@/lib/auth/config";
export default createKrakenOAuthHandler(authConfig);
// Before
import { updateOrgTokenHandler } from "@krakentech/blueprint-auth/server";
import type { NextApiRequest, NextApiResponse } from "next";
export default async function handler(
req: NextApiRequest,
res: NextApiResponse,
) {
await updateOrgTokenHandler({
krakenConfig: {
/* ... */
},
req,
res,
// ... more config
});
}
// After
import { createUpdateOrgTokenHandler } from "@krakentech/blueprint-auth/server";
import { authConfig } from "@/lib/auth/config";
export default createUpdateOrgTokenHandler(authConfig);
Your server-side auth is now set up. Time to wire up the client-side code that your components will use.
Step 5: Client-side auth
The client-side auth setup has several important changes. Your authConfig now
includes appRoutes field, the AuthProvider no longer takes appRoutes as a
prop, and the auth hooks have updated signatures.
createClientSideAuth
The function now takes your centralized authConfig as the first parameter, and
a client-only config object as the second parameter. The client-only config
includes defaultTarget and router. Your centralized authConfig must
include appRoutes (dashboard and login pathnames, previously passed to
AuthProvider). See the
createClientSideAuth API reference
for details.
// Before
import { createClientSideAuth } from "@krakentech/blueprint-auth/client";
export const {
AuthProvider,
useAuth,
useLogin,
useLogout,
useSession,
useGraphQLClient,
useKrakenAuthErrorHandler,
} = createClientSideAuth({
apiRoutes: {
graphql: { kraken: "/api/graphql" },
login: "/api/auth/login",
logout: "/api/auth/logout",
session: "/api/auth/session",
},
defaultTarget: "kraken",
});
// After
import { createClientSideAuth } from "@krakentech/blueprint-auth/client";
import { authConfig } from "@/lib/auth/config";
export const {
AuthProvider,
useAuth,
useLogin,
useLogout,
useSession,
useGraphQLClient,
useKrakenAuthErrorHandler,
} = createClientSideAuth(authConfig, {
defaultTarget: "kraken",
router: "pages-router",
});
AuthProvider
The appRoutes prop is removed since routes are now in your authConfig.
Handlers are now passed as individual props (onLoginSuccess, onLoginError,
onLogoutSuccess, onLogoutError) instead of a single handlers object.
Handler signatures have changed: all handlers now receive a defaultRedirect
callback function, onLoginError receives an ErrorCode string instead of a
KrakenError[] array, and onLogout splits into separate onLogoutSuccess and
onLogoutError handlers for better error handling. See the
AuthProvider API reference for details.
// Before
<AuthProvider
appRoutes={{
dashboard: { pathname: "/dashboard" },
login: { pathname: "/login" },
}}
handlers={{
onLoginSuccess: () => {
// custom success logic
},
onLoginError: (errors) => {
// custom error logic
},
onLogout: () => {
// custom logout logic
},
}}
>
{children}
</AuthProvider>
// After
<AuthProvider
onLoginSuccess={(defaultRedirect) => {
// custom success logic, optionally call defaultRedirect()
}}
onLoginError={(errorCode, defaultRedirect) => {
// custom error logic, optionally call defaultRedirect()
}}
onLogoutSuccess={(defaultRedirect) => {
// custom logout success logic
}}
onLogoutError={(errorCode, defaultRedirect) => {
// custom logout error logic
}}
>
{children}
</AuthProvider>
Hooks
If you're using auth hooks with options, their signatures have changed.
useLogin
The shouldRedirectToNextPage boolean flag is replaced by nextPage. Set it to
null to prevent redirect, or provide a path to redirect to a specific page.
See the useLogin API reference for details.
// Before
const login = useLogin({
shouldRedirectToNextPage: false,
});
// After
const login = useLogin({
nextPage: null,
});
useSession
The return type of the useSession hook has changed. The SessionState type
now includes an authMethod field and removes several boolean flags. See the
useSession API reference for details.
// Before
const { data: session } = useSession();
if (session.isMasquerading) {
return <MasqueradeBanner />;
}
if (session.isScopedSession) {
return <PaymentDetailsCard />;
}
if (session.isMobileWebView) {
return <UpdateAppLink />;
}
// After
const { data: session } = useSession();
if (session.authMethod === "masquerade") {
return <MasqueradeBanner />;
}
if (session.authMethod === "scoped") {
return <PaymentDetailsCard />;
}
if (session.authMethod === "mobile-web-view") {
return <UpdateAppLink />;
}
useLogout
The hook now accepts an options object. Use nextPage to control the redirect
destination, or set it to null to prevent redirect. See the
useLogout API reference for details.
// Before
const logout = useLogout();
// After
const logout = useLogout({
nextPage: "/goodbye",
});
useKrakenAuthErrorHandler
The hook now takes an options object instead of a single function. The onError
callback receives a defaultRedirect function as its second parameter, and you
can specify redirectType ("push" or "replace"). The getAuthErrorCode
function is no longer returned from the hook; import
getErrorCode directly from the main package
instead. See the
useKrakenAuthErrorHandler API reference
for details.
// Before
const { getAuthErrorCode, handleKrakenErrors } = useKrakenAuthErrorHandler(
(error) => {
// custom error handling
},
);
const errorCode = getAuthErrorCode(error);
// After
import { getErrorCode } from "@krakentech/blueprint-auth/utils";
const { handleKrakenErrors } = useKrakenAuthErrorHandler({
onError: (error, defaultRedirect) => {
// custom error handling
},
redirectType: "replace",
});
const errorCode = getErrorCode(error);
The core migration is complete. Now let's update any remaining server-side code.
Step 6: Server-side auth
Server-side authentication has several important changes. All relate to making the API more consistent and enabling App Router support.
Context parameter change
Server-side functions now accept a context parameter as part of their options
object instead of separate req and res parameters. This makes the API more
versatile by allowing server functions to adapt to different execution
environments, from traditional API routes to newer patterns like Server
Components and Server Actions.
Not all server functions work in every Next.js execution environment. For example, some utilities require request/response objects and won't work in Server Components. See the Execution Environment guide in the API reference for details on which functions work where.
- SSR
- API Routes
- Middleware
// Before
import { getUserScopedGraphQLClient } from "@krakentech/blueprint-auth/server";
import type { GetServerSidePropsContext } from "next";
export async function getServerSideProps({
req,
res,
}: GetServerSidePropsContext) {
const client = getUserScopedGraphQLClient({
krakenConfig: {
/* ... */
},
req,
res,
});
}
// After
import { getUserScopedGraphQLClient } from "@krakentech/blueprint-auth/server";
import { authConfig } from "@/lib/auth/config";
import type { GetServerSidePropsContext } from "next";
export async function getServerSideProps(context: GetServerSidePropsContext) {
const client = getUserScopedGraphQLClient(authConfig, { context });
}
// Before
import { getUserScopedGraphQLClient } from "@krakentech/blueprint-auth/server";
import type { NextApiRequest, NextApiResponse } from "next";
export default async function handler(
req: NextApiRequest,
res: NextApiResponse,
) {
const client = getUserScopedGraphQLClient({
krakenConfig: {
/* ... */
},
req,
res,
});
}
// After
import { getUserScopedGraphQLClient } from "@krakentech/blueprint-auth/server";
import { authConfig } from "@/lib/auth/config";
import type { NextApiRequest, NextApiResponse } from "next";
export default async function handler(
req: NextApiRequest,
res: NextApiResponse,
) {
const userClient = getUserScopedGraphQLClient(authConfig, {
context: { req, res },
});
}
// Before
import { getUserScopedGraphQLClient } from "@krakentech/blueprint-auth/server";
import type { NextRequest } from "next/server";
import { NextResponse } from "next/server";
export async function middleware(req: NextRequest) {
const res = NextResponse.next();
const client = getUserScopedGraphQLClient({
krakenConfig: {
/* ... */
},
req,
res,
});
}
// After
import { getUserScopedGraphQLClient } from "@krakentech/blueprint-auth/server";
import { authConfig } from "@/lib/auth/config";
import type { NextRequest } from "next/server";
import { NextResponse } from "next/server";
export async function middleware(req: NextRequest) {
const res = NextResponse.next();
const userClient = getUserScopedGraphQLClient(authConfig, {
context: { req, res },
});
}
This applies to all server-side utilities including
getUserScopedGraphQLClient, getOrganizationScopedGraphQLClient,
getSession, login, logout, and more.
Function renaming
Several server-side utilities have been renamed for clarity.
- getSessionData
- getServerSideGraphQLClients
- createKrakenOAuthURI
Renamed to getSession.
// Before
import { getSessionData } from "@krakentech/blueprint-auth/server";
import type { GetServerSidePropsContext } from "next";
export async function getServerSideProps({
req,
res,
}: GetServerSidePropsContext) {
const session = await getSessionData({
krakenConfig: {
/* ... */
},
req,
res,
});
}
// After
import { getSession } from "@krakentech/blueprint-auth/server";
import type { GetServerSidePropsContext } from "next";
export async function getServerSideProps(context: GetServerSidePropsContext) {
const session = await getSession({ context });
}
Split into two separate functions: getUserScopedGraphQLClient and
getOrganizationScopedGraphQLClient. Import only what you need.
// Before
import { getServerSideGraphQLClients } from "@krakentech/blueprint-auth/server";
import type { GetServerSidePropsContext } from "next";
export async function getServerSideProps({
req,
res,
}: GetServerSidePropsContext) {
const { userScopedClient, organizationScopedClient } =
await getServerSideGraphQLClients({
krakenConfig: {
/* ... */
},
req,
res,
});
}
// After
import {
getUserScopedGraphQLClient,
getOrganizationScopedGraphQLClient,
} from "@krakentech/blueprint-auth/server";
import { authConfig } from "@/lib/auth/config";
import type { GetServerSidePropsContext } from "next";
export async function getServerSideProps(context: GetServerSidePropsContext) {
const userClient = getUserScopedGraphQLClient(authConfig, { context });
const orgClient = getOrganizationScopedGraphQLClient(authConfig, { context });
}
Renamed to generateKrakenOAuthURI for consistency with similar utilities.
// Before
import { createKrakenOAuthURI } from "@krakentech/blueprint-auth/server";
import type { GetServerSidePropsContext } from "next";
export async function getServerSideProps({
req,
res,
}: GetServerSidePropsContext) {
const oauthUri = await createKrakenOAuthURI({
krakenConfig: {
/* ... */
},
apiRoutes: {
/* ... */
},
req,
res,
});
}
// After
import { generateKrakenOAuthURI } from "@krakentech/blueprint-auth/server";
import { authConfig } from "@/lib/auth/config";
import type { GetServerSidePropsContext } from "next";
export async function getServerSideProps(context: GetServerSidePropsContext) {
const oauthUri = await generateKrakenOAuthURI(authConfig, { context });
}
getSession
The return type of the getSession server function has changed. The
SessionState type now includes an authMethod field and removes several
boolean flags. See the getSession API reference
for details.
// Before
import { getSession } from "@krakentech/blueprint-auth/server";
import type { GetServerSidePropsContext } from "next";
export async function getServerSideProps(context: GetServerSidePropsContext) {
const session = await getSession({ context });
if (session.isMasquerading) {
// Handle masquerade session
}
if (session.isScopedSession) {
// Handle scoped session
}
if (session.isMobileWebView) {
// Handle mobile web view session
}
}
// After
import { getSession } from "@krakentech/blueprint-auth/server";
import type { GetServerSidePropsContext } from "next";
export async function getServerSideProps(context: GetServerSidePropsContext) {
const session = await getSession({ context });
if (session.authMethod === "masquerade") {
// Handle masquerade session
}
if (session.authMethod === "scoped") {
// Handle scoped session
}
if (session.authMethod === "mobile-web-view") {
// Handle mobile web view session
}
}
For a cleaner API with less boilerplate, consider using
createServerSideAuth. This
factory function returns pre-configured versions of all server utilities that
only require runtime parameters.
Show example
// Instead of repeating config across calls
import {
getSession,
getUserScopedGraphQLClient,
} from "@krakentech/blueprint-auth/server";
import type { GetServerSidePropsContext } from "next";
export async function getServerSideProps(context: GetServerSidePropsContext) {
const session = await getSession({ context });
const client = getUserScopedGraphQLClient({
context,
krakenConfig: {
/* ... */
},
});
}
// You can use the factory
import { createserversideauth } from "@krakentech/blueprint-auth/server";
import { authConfig } from "@/lib/auth/config";
import type { GetServerSidePropsContext } from "next";
const { getSession, getUserScopedGraphQLClient } =
createserversideauth(authConfig);
export async function getServerSideProps(context: GetServerSidePropsContext) {
const session = await getSession({ context });
const client = getUserScopedGraphQLClient({ context });
}
See the API reference for details.
Step 7: Cookies
The cookie constants have been renamed for clarity.
// Before
import { TOKEN_COOKIE_KEYS } from "@krakentech/blueprint-auth";
const token = cookies[TOKEN_COOKIE_KEYS.access];
// After
import { AUTH_COOKIE } from "@krakentech/blueprint-auth";
const token = cookies[AUTH_COOKIE.accessToken];
Also make sure to pdate cookie names if you're manipulating auth cookies manually or in tests.
// Before
await setCookies({
cookies: [
{ name: "access", value: accessToken },
{ name: "refresh", value: refreshToken },
],
});
// After
await setCookies({
cookies: [
{ name: "accessToken", value: accessToken },
{ name: "refreshToken", value: refreshToken },
],
});
Before testing, verify all required environment variables are configured.
If you're using server-side GraphQL clients in getServerSideProps, getStaticProps, API routes, or middleware, they also need updating.
Step 8: Internationalization (i18n)
Version 40 introduces a new i18n configuration to support localized routing and pathname translation. If your app uses Next.js built-in i18n features and supports multiple locales, you need to configure the auth package accordingly.
For comprehensive documentation, configuration examples, and integration patterns, see the i18n guide.
import { createAuthConfig } from "@krakentech/blueprint-auth";
import { getPathname } from "@/i18n/navigation";
import { hasLocale } from "next-intl";
import { routing } from "@/i18n/routing";
export const authConfig = createAuthConfig({
i18n: {
localeCookie: "NEXT_LOCALE",
getLocalizedPathname({ locale, pathname }) {
return getPathname({
href: { pathname },
locale: hasLocale(routing.locales, locale)
? locale
: routing.defaultLocale,
});
},
},
// ... other config
});
See the i18n guide for detailed setup instructions, common patterns, and FAQ.
Step 9: Environment variables
Your environment variables remain the same, with one addition:
krakenConfig.xClientIpSecretKey is now a required option. Following best
practices for handling secrets, we recommend setting the
KRAKEN_X_CLIENT_IP_SECRET_KEY environment variable to provide this value.
With everything in place, thoroughly test your authentication flows.
Step 10: Test everything
You know your application best. Run your type checker, build your project, and verify your authentication flows work as expected. Consider testing login, logout, token refresh, and protected routes. If you use OAuth, masquerade, organization-scoped auth, or anonymous auth features, give those a check too.
Breaking Changes Reference
Configuration Changes
Config gets organized
Options are now grouped logically into customization and validation objects.
See the createAuthConfig API reference
for all configuration options.
// Before
{
getCookieOptions: (cookieName) => ({ /* ... */ }),
setCustomHeaders: (headers) => { /* ... */ },
shouldValidateUrl: !process.env.IS_TEST,
}
// After
{
customization: {
getCookieOptions: (cookieName) => ({ /* ... */ }),
setCustomHeaders: (headers, authScope) => { /* ... */ },
},
validation: {
validateGraphQLEndpointUrl: !process.env.IS_TEST,
},
}
Custom headers get more context
The setCustomHeaders callback now receives authScope for user vs
organization requests. This unifies the API across the package; write your
header logic once and reuse it everywhere.
// Before
setCustomHeaders: (headers: Headers) => void
// After
customization: {
setCustomHeaders: (headers: Headers, authScope: "user" | "organization") => void | Promise<void>
}
Cookie options allow selective customization
// returned full context object
getCookieOptions: (cookieName: CookieName) => Omit<HttpContext, "req" | "res">;
// nested in customization, returns just cookie options
customization: {
getCookieOptions: (cookieName: CookieName) => SerializeOptions | undefined;
}
Route config gets consistent names
Route parameter extractors now use consistent naming and accept an options object for easier extensibility.
// Before
appRoutes: {
anon: {
getPreSignedKey: (url) => ({ preSignedKey }),
},
masquerade: {
getUserIdAndMasqueradeToken: (url) => ({ userId, masqueradeToken }),
},
}
// After
appRoutes: {
anon: {
getAnonParams({ url }) {
return { preSignedKey };
},
},
masquerade: {
getMasqueradeParams({ url }) {
return { userId, masqueradeToken };
},
},
}
Middleware
Middleware gets factory pattern
The middleware now uses the factory pattern with
createAuthMiddleware.
// Before
import { authMiddleware } from "@krakentech/blueprint-auth/middleware";
import type { NextRequest, NextResponse } from "next/server";
export async function middleware(req: NextRequest) {
const res = NextResponse.next();
return await authMiddleware({
appRoutes: {
/* ... */
},
krakenConfig: {
/* ... */
},
req,
res,
// ... more config
});
}
// After
import { createAuthMiddleware } from "@krakentech/blueprint-auth/middleware";
import { authConfig } from "@/lib/auth/config";
export const middleware = createAuthMiddleware(authConfig);
export const config = {
matcher: ["/dashboard/:path*", "/login"],
};
Redirect callbacks support fallback
Before:
customSuccessResponse: ({ url }) => NextResponse.redirect(url);
After:
customSuccessResponse({ url }, { redirect }) {
if (someCondition) {
return redirect(url);
}
// return undefined to use default
}
The customSuccessResponse and customErrorResponse callbacks now receive
helper functions (redirect, rewrite) as a second parameter. This dependency
injection pattern eliminates the need to import NextResponse in your config
file, making it framework-agnostic and browser-safe.
API Handlers
Handlers now use factories
Handlers are now factories that work with the unified config. You can export
them directly from your API routes, reducing boilerplate code significantly. See
the API reference for:
createLoginHandler,
createLogoutHandler,
createSessionHandler,
createGraphQLHandler.
// Before
import { loginHandler } from "@krakentech/blueprint-auth/server";
import type { NextApiRequest, NextApiResponse } from "next";
export default async function handler(
req: NextApiRequest,
res: NextApiResponse,
) {
await loginHandler({
krakenConfig: { graphqlEndpoint: process.env.KRAKEN_GRAPHQL_ENDPOINT },
req,
res,
edgeConfig: {
/* ... */
},
encryption: {
/* ... */
},
});
}
// After
import { createLoginHandler } from "@krakentech/blueprint-auth/server";
import { authConfig } from "@/lib/auth/config";
export default createLoginHandler(authConfig);
GraphQL client functions split for clarity
The single getServerSideGraphQLClients function is replaced by two separate
functions:
getUserScopedGraphQLClient and
getOrganizationScopedGraphQLClient.
Import only what you need.
// Before
import { getServerSideGraphQLClients } from "@krakentech/blueprint-auth/server";
const { userScopedClient, organizationScopedClient } =
await getServerSideGraphQLClients({
krakenConfig: {
/* ... */
},
req,
res,
});
// After
import {
getUserScopedGraphQLClient,
getOrganizationScopedGraphQLClient,
} from "@krakentech/blueprint-auth/server";
import { authConfig } from "@/lib/auth/config";
const userClient = getUserScopedGraphQLClient(authConfig, {
context: { req, res },
});
const orgClient = getOrganizationScopedGraphQLClient(authConfig, {
context: { req, res },
});
Server Functions
Server functions now use context parameter
Server-side functions now accept a context parameter as part of their options
object instead of separate req and res parameters.
// Before
import { getServerSideGraphQLClients } from "@krakentech/blueprint-auth/server";
const { userScopedClient, organizationScopedClient } =
await getServerSideGraphQLClients({
krakenConfig: {
/* ... */
},
req,
res,
});
// After
import {
getUserScopedGraphQLClient,
getOrganizationScopedGraphQLClient,
} from "@krakentech/blueprint-auth/server";
import { authConfig } from "@/lib/auth/config";
const userClient = getUserScopedGraphQLClient(authConfig, {
context: { req, res },
});
const orgClient = getOrganizationScopedGraphQLClient(authConfig, {
context: { req, res },
});
This pattern applies to all server-side utilities including getSession,
getUserScopedGraphQLClient, getOrganizationScopedGraphQLClient, and others.
Client-Side Auth
Client auth uses unified config
The createClientSideAuth function signature changed from one parameter to two.
The defaultTarget option moved to the second parameter along with the new
router option. See the
createClientSideAuth API reference
for details.
// Before
import { createClientSideAuth } from "@krakentech/blueprint-auth/client";
export const { AuthProvider, useLogin, useLogout, useSession } =
createClientSideAuth({
apiRoutes: {
graphql: { kraken: "/api/graphql" },
login: "/api/auth/login",
},
defaultTarget: "kraken",
});
// After
import { createClientSideAuth } from "@krakentech/blueprint-auth/client";
import { authConfig } from "@/lib/auth/config";
export const { AuthProvider, useLogin, useLogout, useSession } =
createClientSideAuth(authConfig, {
defaultTarget: "kraken",
router: "pages-router",
});
AuthProvider API changes
The AuthProvider component API has been simplified and improved:
- Removed
appRoutesprop - Route configuration now lives in your centralizedauthConfig - Individual handler props - Handler callbacks are now passed as individual
props directly to
AuthProviderinstead of being nested in ahandlersobject - Updated handler signatures - All handlers now receive a
defaultRedirectcallback function, giving you control over whether to use default redirect behavior - Improved error handling - The
onLoginErrorhandler receives a normalizedErrorCodestring instead of aKrakenError[]array - Split logout handlers - The
onLogouthandler splits into separateonLogoutSuccessandonLogoutErrorhandlers for granular error handling - Async support - All handlers can now be async
See the AuthProvider API reference for
details.
// Before
<AuthProvider
appRoutes={{
dashboard: "/dashboard",
login: "/login",
}}
onLoginSuccess={() => {
// Handle login success
}}
onLoginError={(errorCode) => {
// Handle login error
}}
onLogoutSuccess={() => {
// Handle logout success
}}
onLogoutError={(errorCode) => {
// Handle logout error
}}
>
// After
<AuthProvider
onLoginSuccess={(defaultRedirect) => {
// Handle login success
defaultRedirect(); // Optional: use default redirect behavior
}}
onLoginError={(errorCode, defaultRedirect) => {
// Handle login error with normalized error code
defaultRedirect(); // Optional: use default redirect behavior
}}
onLogoutSuccess={(defaultRedirect) => {
// Handle logout success
defaultRedirect(); // Optional: use default redirect behavior
}}
onLogoutError={(errorCode, defaultRedirect) => {
// Handle logout error with normalized error code
defaultRedirect(); // Optional: use default redirect behavior
}}
>
useLogin accepts nextPage option
The shouldRedirectToNextPage boolean flag is replaced by nextPage for more
granular redirect control. Set it to null to prevent redirect, or provide a
path to redirect to a specific page. The server now returns a redirectUrl in
the response. See the useLogin API reference for
details.
// Before
const login = useLogin({
shouldRedirectToNextPage: false,
});
// After
const login = useLogin({
nextPage: null,
});
useLogout accepts options
The hook now accepts an options object to control redirect behavior. Use
nextPage to specify the redirect destination or set it to null to prevent
redirect. See the useLogout API reference for
details.
// Before
const logout = useLogout();
// After
const logout = useLogout({
nextPage: "/goodbye",
});
useKrakenAuthErrorHandler uses options object
The hook now accepts an options object instead of a single callback function.
The onError callback receives a defaultRedirect function as its second
parameter, and you can specify redirectType ("push" or "replace"). The
getAuthErrorCode function is no longer returned from the hook; import
getErrorCode directly from the main package instead. See the
useKrakenAuthErrorHandler API reference
for details.
// Before
const { getAuthErrorCode, handleKrakenErrors } = useKrakenAuthErrorHandler(
(error) => {
// custom error handling
},
);
// After
import { getErrorCode } from "@krakentech/blueprint-auth/utils";
const { handleKrakenErrors } = useKrakenAuthErrorHandler({
onError: (error, defaultRedirect) => {
// custom error handling
},
redirectType: "replace",
});
const errorCode = getErrorCode(error);
useSession no longer refetches on window focus or reconnect
The useSession hook no longer automatically refetches session data when the
user refocuses the window or reconnects to the network. This prevents UI tearing
when sessions expire.
Previous behavior: The hook would refetch on refetchOnWindowFocus and
refetchOnReconnect events. If the session had expired during one of these
refetches, the hook would return an unauthenticated state while the user
remained on a protected page (e.g., dashboard). This caused UI tearing where
conditionally rendered authenticated UI elements (user menus, dashboard
navigation, etc.) would suddenly disappear while the user was still viewing the
page.
New behavior: The session state remains stable during the user's current
page view. Users are redirected to the login page during their next page
navigation or GraphQL request (when
useKrakenAuthErrorHandler is
used), ensuring a consistent UI experience without jarring visual changes.
See the useSession API reference for details.
Import & Export Changes
Cookie constants renamed
// TOKEN_COOKIE_KEYS with shortened property names
import { TOKEN_COOKIE_KEYS } from "@krakentech/blueprint-auth";
TOKEN_COOKIE_KEYS.access; // actually "accessToken"
TOKEN_COOKIE_KEYS.refresh; // actually "refreshToken"
// AUTH_COOKIE with full property names
import { AUTH_COOKIE } from "@krakentech/blueprint-auth";
AUTH_COOKIE.accessToken; // "accessToken"
AUTH_COOKIE.refreshToken; // "refreshToken"
Import and export names updated
The API has been redesigned for consistency. Handlers use the create* factory
pattern, and utilities have clearer names.
// Utility functions renamed:
createKrakenOAuthURI → generateKrakenOAuthURI
getSessionData → getSession
// Constants renamed:
TOKEN_COOKIE_KEYS → AUTH_COOKIE
// New error handling exports:
AuthError // structured error class with helpful messages
BlueprintAuthErrorCode // error code enum
getErrorCode // extract error codes from errors
isBlueprintAuthErrorCode // type guard for error codes
See the migration steps above for detailed handler and middleware changes.
getErrorCode now standalone export
The getErrorCode function is no longer returned from
useKrakenAuthErrorHandler. Import it directly from the main package. See the
getErrorCode API reference for details.
// Before
const { getAuthErrorCode, handleKrakenErrors } =
useKrakenAuthErrorHandler(/* ... */);
const errorCode = getAuthErrorCode(error);
// After
import { getErrorCode } from "@krakentech/blueprint-auth/utils";
const { handleKrakenErrors } = useKrakenAuthErrorHandler(/* ... */);
const errorCode = getErrorCode(error);
isBlueprintAuthErrorCode type guard
The isBlueprintAuthErrorCode function is a TypeScript type guard that checks
if an error code is a valid Blueprint auth error code.
Usage:
import { isBlueprintAuthErrorCode } from "@krakentech/blueprint-auth/utils";
import type { ErrorCode } from "@krakentech/blueprint-auth";
const errorCode: string | null = getErrorCode(error);
if (isBlueprintAuthErrorCode(errorCode)) {
// errorCode is now typed as ErrorCode (not string | null)
// You get autocomplete for all valid error codes
switch (errorCode) {
case "ACCOUNT_NOT_FOUND":
// handle specific error
break;
// ...
}
}
This is useful when working with error codes from external sources or when you need strict type checking.
New SessionState
New field:
authMethod: Identifies which authentication method is currently active ("email","oauth","scoped","masquerade","mobile-web-view", ornull)
Removed fields:
isMasqueradingisMobileWebViewisScopedSession
const { data: session } = useSession();
if (session.isAuthenticated) {
return <LogoutButton />
} else {
return <LoginButton />
}
if (session.authMethod === "masquerade") {
return <MasqueradeBanner />
}
if (session.authMethod === "scoped") {
return <PaymentDetailsCard />
} else {
return <PaymentDetailsForm />
}
if (session.authMethod === "mobile-web-view") {
return <UpdateAppLink />
} else {
return <DownloadAppLink />
}
See the SessionState type reference and the
AuthMethod type reference for full
documentation.
New Features (Non-Breaking)
New Utility: getActiveAuthMethod
A new utility function getActiveAuthMethod is available to determine the
active authentication method based on cookie priority without loading the full
session state:
const { getActiveAuthMethod, getAuthCookies } = createAuthCookieUtils({
context,
});
const cookies = await getAuthCookies();
const authMethod = getActiveAuthMethod(cookies);
See the
createAuthCookieUtils API reference
for details on the available utility functions including getActiveAuthMethod.