Skip to main content

Email & password

Use this flow when your Kraken environment still supports signing in with an email address and password. For new integrations, prefer Kraken OAuth: Kraken's authorization server handles the login screen instead of your application collecting passwords.

Deprecation notice

Email & Password authentication through the ObtainKrakenToken mutation is deprecated and will be removed in the near future. We'll be replacing it with the Kraken Auth Server soon.

Blueprint Auth sends the credentials to Kraken, verifies the returned access token, and writes the authentication cookies. Your application supplies the form and decides how to present failures; it should not manage tokens itself.

Before you begin​

Complete the setup guide for your router:

  • App Router setup: export login from your server-side auth module. The example below uses Next.js 15+ and React 19.
  • Pages Router setup: export useLogin from your client-side auth module and install AuthProvider inside QueryClientProvider.

Both paths require the Kraken endpoints, client IP secret, and exact trusted access-token issuers described in those guides. Keep credentials and server-only configuration out of client modules.

RouterForm submissionServer entry point
App RouteruseActionState submits a Server Actionlogin from createAppRouterAuth
Pages RouteruseLogin sends a JSON POST to apiRoutes.logincreateLoginHandler

The Server Action approach does not need a login API route.

Implementation​

Create the login action​

Validate the submitted fields on the server before calling login. Pass the page's search parameters so Blueprint Auth can honor a nextPage return path.

app/login/actions.ts
"use server";

import type { AppRouterLoginParams } from "@krakentech/blueprint-auth/server";
import { unstable_rethrow } from "next/navigation";
import { login } from "@/lib/auth/server";

export type LoginState = { error?: string };

export async function loginAction(
searchParams: AppRouterLoginParams.Redirect["searchParams"],
_previousState: LoginState,
formData: FormData,
): Promise<LoginState> {
const email = formData.get("email");
const password = formData.get("password");

if (
typeof email !== "string" || !email.trim() ||
typeof password !== "string" || !password
) {
return { error: "Enter your email address and password." };
}

try {
return await login({
input: { email: email.trim(), password },
searchParams,
});
} catch (error) {
unstable_rethrow(error);
return { error: "Unable to sign in. Check your details and try again." };
}
}

On success, login normally redirects by throwing Next.js's internal redirect error. Call unstable_rethrow before handling application errors; otherwise a successful login can be mistaken for a failure. Do not return credentials or raw error objects in the action state.

Connect the form​

useActionState supplies the action result and pending state. Disable submission while pending and display a generic error without exposing Kraken's response.

app/login/LoginForm.tsx
"use client";

import { useActionState } from "react";
import type { LoginState } from "./actions";

type LoginFormProps = {
action: (state: LoginState, formData: FormData) => Promise<LoginState>;
};

export function LoginForm({ action }: LoginFormProps) {
const [state, formAction, pending] = useActionState(action, {});

return (
<form action={formAction}>
<label htmlFor="email">Email</label>
<input id="email" name="email" type="email" autoComplete="username" required />

<label htmlFor="password">Password</label>
<input
id="password"
name="password"
type="password"
autoComplete="current-password"
required
/>

{state.error && <p role="alert">{state.error}</p>}
<button type="submit" disabled={pending}>
{pending ? "Signing in..." : "Sign in"}
</button>
</form>
);
}

Resolve the page's asynchronous searchParams and bind them to the action:

app/login/page.tsx
import { loginAction } from "./actions";
import { LoginForm } from "./LoginForm";

type LoginPageProps = {
searchParams: Promise<Record<string, string | string[] | undefined>>;
};

export default async function LoginPage({ searchParams }: LoginPageProps) {
const action = loginAction.bind(null, await searchParams);
return <LoginForm action={action} />;
}

Redirects and errors​

BehaviorApp RouterPages Router
Default success destinationnextPage from the supplied search parameters, otherwise the configured dashboardnextPage from the URL, otherwise the configured dashboard
Override the destinationPass nextPage to loginPass nextPage to useLogin
Stay on the page after successPass nextPage: null to login and return your own success stateCall useLogin({ nextPage: null }) and render a success state
Handle failureCatch application errors in the action, preserving Next.js control-flow errorsRead the mutation error and URL error code, or override AuthProvider.onLoginError

Blueprint Auth validates redirect destinations against the request origin. Use its redirect handling rather than redirecting directly to an unchecked query parameter. An invalid destination does not result in an external redirect.

A failed attempt can mean invalid credentials, a rejected token, a service failure, or a configuration error. Show a safe message to the user and use auth logging to investigate; do not assume every failure means the password is wrong. If Kraken requires CAPTCHA, both login and useLogin accept captchaResponse alongside the credentials.

Security requirements​

  • Use HTTPS in deployed environments. Never put credentials in URLs, logs, browser storage, or returned action state.
  • Validate input on the server. Browser validation and disabled buttons are UI aids, not security boundaries. createLoginHandler validates its request body; a custom Server Action must validate its own input.
  • For the API-handler path, configure a non-empty list of trusted origins. Requests must use POST with Content-Type: application/json and include a trusted Origin or Referer. If both headers are present, both must be trusted. useLogin supplies the JSON content type; the browser supplies source headers. Follow the trusted request origins setup.
  • The Server Action example calls login directly, not createLoginHandler. Next.js handles the Server Action transport and its origin checks; the API handler's allowedRequestOrigins setting does not configure those checks.
  • Login establishes a session; it does not protect every subsequent operation. At a protected page, handler, or Server Action, call getAuth.user and deny access if it returns null. Only then create getGraphQLClient.user({ auth }), and enforce resource-specific authorization on the server.