Skip to main content

Project Integration

This guide shows how to integrate Kyqu authentication into your product.

Setup

Each project in Kyqu has:

  • A Project ID (UUID)
  • A Public Key (pk_...) — identifies the project
  • A Secret Key (sk_...) — shown once, stored as hash

Store the project ID and public key in your application's configuration:

const KYQU_CONFIG = {
baseUrl: "https://api.kyqu.dev", // Kyqu API host
projectId: "a1b2c3d4-...",
publicKey: "pk_abc123..."
};

`baseUrl` must point to where the Kyqu **API** is served.

## Using the Client SDK

The `@kyqu/client` package is the recommended way to integrate:

```bash
npm install @kyqu/client
import { KyquClient } from "@kyqu/client";

const kyqu = new KyquClient({
baseUrl: "https://api.kyqu.dev",
projectId: "your-project-id",
publicKey: "pk_your-public-key"
});

Direct API Usage (Vanilla JS)

class KyquAuth {
constructor(baseUrl, projectId, publicKey) {
this.baseUrl = baseUrl;
this.projectId = projectId;
this.publicKey = publicKey;
this.authBase = `${baseUrl}/api/projects/${projectId}/auth`;
}

async request(path, options = {}) {
const headers = {
"Content-Type": "application/json",
"x-kyqu-public-key": this.publicKey,
...options.headers
};

const res = await fetch(`${this.authBase}/${path}`, {
method: options.method || "GET",
headers,
body: options.body ? JSON.stringify(options.body) : undefined
});

const data = await res.json();
if (!res.ok) throw new Error(data.error || "Request failed");
return data;
}

signup(email, password, name) {
return this.request("signup", {
method: "POST",
body: { email, password, name }
});
}

login(email, password) {
return this.request("login", {
method: "POST",
body: { email, password }
});
}

getUser(token) {
return this.request("me", {
headers: { Authorization: `Bearer ${token}` }
});
}
}

Auth Flows

Email/Password Signup

try {
const { user, session, verificationRequired } = await kyqu.signup({
email: "user@example.com",
password: "correct-horse-battery-staple",
name: "User Name"
});

if (verificationRequired) {
// Show "Check your email" message
// User must verify before they can log in
} else {
// User is automatically logged in
localStorage.setItem("session", session.token);
}
} catch (err) {
// Handle error (validation, duplicate email, etc.)
}

Email/Password Login

try {
const { user, session } = await kyqu.login({
email: "user@example.com",
password: "correct-horse-battery-staple"
});

localStorage.setItem("session", session.token);
} catch (err) {
if (err.message === "TOTP_REQUIRED") {
// Prompt user for 2FA code
showTotpPrompt();
} else {
showError(err.message);
}
}

Login with TOTP 2FA

// First call returns TOTP_REQUIRED
// Then retry with TOTP code:
const { user, session } = await kyqu.login({
email: "user@example.com",
password: "correct-horse-battery-staple",
totpCode: "123456"
});

// Or use a backup code:
const { user, session } = await kyqu.login({
email: "user@example.com",
password: "correct-horse-battery-staple",
backupCode: "abcd-efgh-ijkl-mnop"
});

Session Management

// Check current user
const token = localStorage.getItem("session");
if (token) {
try {
const user = await kyqu.getUser(token);
// User is authenticated
} catch {
// Token expired or revoked
localStorage.removeItem("session");
redirectToLogin();
}
}

// Logout
await kyqu.logout(token);
localStorage.removeItem("session");

// List active sessions
const sessions = await kyqu.listSessions(token);

// Revoke a specific session (e.g., "log out other devices")
await kyqu.revokeSession(token, sessionId);

Email Verification

// Request verification email
await kyqu.requestEmailVerification("user@example.com");

// Confirm verification (from link click)
// GET /api/projects/{id}/auth/verify-email/confirm?token=...
// Or via API:
const { user } = await kyqu.confirmEmailVerification(token);

Password Reset

// Request reset email
await kyqu.requestPasswordReset("user@example.com");

// Confirm reset
const { user, session } = await kyqu.confirmPasswordReset({
token: "reset-token-from-email",
password: "new-password-42"
});
// Request magic link email
await kyqu.requestMagicLink("user@example.com");

// Option A: API confirm (POST) — returns session directly
const { user, session } = await kyqu.confirmMagicLink(tokenFromEmail);

// Option B: Browser email link (GET) — user lands on your login_url with session_code
// On your login page:
const params = new URLSearchParams(window.location.search);
const sessionCode = params.get("session_code");

if (sessionCode) {
const { user, session } = await kyqu.exchangeSessionCode(sessionCode);
localStorage.setItem("session", session.token);
// Clean up URL: remove session_code from address bar
}

Configure loginUrl in project auth settings to your app login page.

Password change

const token = localStorage.getItem("session");

await kyqu.changePassword(token, {
currentPassword: "old-password",
newPassword: "new-secure-password"
});

TOTP Enrollment

// Step 1: Start enrollment
const { enrollmentUrl } = await kyqu.enrollTotp(token);
// Show QR code to user (render enrollmentUrl as QR)

// Step 2: Confirm with code from authenticator app
const { backupCodes } = await kyqu.confirmTotp(token, "123456");
// Display backup codes to user — they're shown once!

Passkey Authentication

// Registration
const options = await kyqu.getPasskeyRegistrationOptions(token);
const credential = await navigator.credentials.create({ publicKey: options });
await kyqu.registerPasskey(token, credential, "My MacBook");

// Authentication
const authOptions = await kyqu.getPasskeyAuthenticationOptions();
const assertion = await navigator.credentials.get({ publicKey: authOptions });
const { user, session } = await kyqu.authenticateWithPasskey(assertion);

OAuth Social Login

// Redirect to Google
window.location.href = kyqu.getOAuthAuthorizeUrl("google");
// Kyqu handles the callback; user lands back with a session

ID Token (OIDC)

const { idToken } = await kyqu.getIdToken(token);
// RS256-signed JWT with sub, email, name, iss, aud, iat, exp

Auto-Refresh Session

const managed = kyqu.withAutoRefresh(session, {
storage: localStorage,
marginSeconds: 60,
onRefresh: (newSession) => console.log("Token refreshed"),
onError: () => redirectToLogin()
});

// Always returns a fresh token
fetch("/api/data", {
headers: { Authorization: `Bearer ${managed.getToken()}` }
});

React Integration

import { KyquProvider, useKyqu, useKyquUser } from "@kyqu/client/react";

function App() {
return (
<KyquProvider client={kyqu} storage={localStorage}>
<Main />
</KyquProvider>
);
}

function Main() {
const { user, loading, login, logout } = useKyqu();
if (loading) return <div>Loading...</div>;
if (!user) return <LoginPage onLogin={login} />;
return <Dashboard user={user} onLogout={logout} />;
}

Best Practices

  1. Store session tokens securely — Use HttpOnly cookies if possible, or secure client-side storage
  2. Handle 401 responses — Redirect to login when token is invalid/expired
  3. Use auto-refresh — Wrap sessions with withAutoRefresh() instead of re-authenticating
  4. Use HTTPS — Never send tokens over plain HTTP
  5. Validate on page load — React hook useKyqu() handles session restore automatically
  6. Monitor webhooks — Listen for auth events to sync user status