Skip to main content

Authentication

The SDK supports multiple authentication methods. Choose the one that best fits your use case.

Authentication Methods

MethodBest ForSecurity
API KeyServer-side apps, scripts, automationHigh (keep secret)
Username/PasswordClient-side apps with user loginMedium
Access TokenWhen you already have a tokenHigh

Use API keys for server-to-server communication, scripts, and automation.

Path Adapter

For current backends, add enablePathAdapter: true. See Path Adapter for details.

import { initSDK, getUser, chat } from "@cpod/sdk";

// Initialize with API key
initSDK({
baseUrl: process.env.NEXT_PUBLIC_CPOD_BASE_URL!,
apiKey: process.env.CPOD_API_KEY!,
enablePathAdapter: true, // Required for current backends
});

// Now all API calls are authenticated
const user = await getUser();
console.log(`Authenticated as: ${user.email}`);

// Use any SDK function
const response = await chat.complete({
message: "Hello!",
});

Environment Variables

.env
NEXT_PUBLIC_CPOD_BASE_URL=https://api.cyberpod.ai
CPOD_API_KEY=your-api-key-here
Keep API Keys Secret

Never expose API keys in client-side code or commit them to version control.


Method 2: Username/Password Authentication

Use this for client-side applications where users log in with their credentials.

import { setBaseUrl, authenticateUser, getUser } from "@cpod/sdk";

// Initialize SDK
setBaseUrl(process.env.NEXT_PUBLIC_CPOD_BASE_URL!);

// Authenticate user
const tokens = await authenticateUser({
username: "user@example.com",
password: "password",
});

console.log("Access Token:", tokens.access_token);
console.log("Refresh Token:", tokens.refresh_token);

// Tokens are automatically stored - subsequent calls are authenticated
const user = await getUser();
console.log(`Welcome, ${user.full_name}!`);

Complete Login Flow

import {
setBaseUrl,
authenticateUser,
getUser,
signOutUser,
CyberPodError
} from "@cpod/sdk";

setBaseUrl(process.env.NEXT_PUBLIC_CPOD_BASE_URL!);

async function login(username: string, password: string) {
try {
// Authenticate
const tokens = await authenticateUser({ username, password });

// Get user profile
const user = await getUser();

console.log(`Logged in as ${user.full_name}`);
return { tokens, user };

} catch (error) {
if (error instanceof CyberPodError) {
if (error.status === 401) {
throw new Error("Invalid username or password");
}
if (error.status === 429) {
throw new Error("Too many login attempts. Please try again later.");
}
}
throw error;
}
}

async function logout() {
await signOutUser();
console.log("Logged out successfully");
}

Method 3: Access Token (Manual Token Management)

Use this when you already have an access token (e.g., from OAuth flow or stored session).

import { setBaseUrl, setAccessToken, getUser } from "@cpod/sdk";

// Initialize SDK
setBaseUrl(process.env.NEXT_PUBLIC_CPOD_BASE_URL!);

// Set access token manually
setAccessToken("your-jwt-access-token");

// Now all calls are authenticated
const user = await getUser();

With Stored Tokens

import { setBaseUrl, setAccessToken, getUser } from "@cpod/sdk";

setBaseUrl(process.env.NEXT_PUBLIC_CPOD_BASE_URL!);

// Retrieve token from storage
const storedToken = localStorage.getItem("access_token");

if (storedToken) {
setAccessToken(storedToken);

try {
const user = await getUser();
console.log("Session restored for:", user.email);
} catch (error) {
// Token expired or invalid
localStorage.removeItem("access_token");
console.log("Please log in again");
}
}

Token Management

Check Current Session

import { getSession, validateToken } from "@cpod/sdk";

// Get session info
const session = await getSession();
console.log("User:", session.user.email);
console.log("Expires:", session.expires_at);

// Check if token is still valid
const isValid = await validateToken();
if (!isValid) {
console.log("Token expired, need to refresh");
}

Refresh Expired Tokens

import { refreshToken } from "@cpod/sdk";

// When access token expires, use refresh token to get new tokens
const storedRefreshToken = localStorage.getItem("refresh_token");

try {
const newTokens = await refreshToken(storedRefreshToken);

// Store new tokens
localStorage.setItem("access_token", newTokens.access_token);
localStorage.setItem("refresh_token", newTokens.refresh_token);

console.log("Tokens refreshed successfully");
} catch (error) {
// Refresh token also expired - need full re-authentication
console.log("Session expired. Please log in again.");
}

Auto-Refresh Pattern

import {
setBaseUrl,
setAccessToken,
refreshToken,
getUser,
CyberPodError
} from "@cpod/sdk";

async function makeAuthenticatedRequest<T>(
request: () => Promise<T>
): Promise<T> {
try {
return await request();
} catch (error) {
if (error instanceof CyberPodError && error.status === 401) {
// Try to refresh token
const storedRefreshToken = localStorage.getItem("refresh_token");

if (storedRefreshToken) {
try {
const newTokens = await refreshToken(storedRefreshToken);
localStorage.setItem("access_token", newTokens.access_token);
localStorage.setItem("refresh_token", newTokens.refresh_token);
setAccessToken(newTokens.access_token);

// Retry the request
return await request();
} catch {
// Refresh failed
throw new Error("Session expired. Please log in again.");
}
}
}
throw error;
}
}

// Usage
const user = await makeAuthenticatedRequest(() => getUser());

React Integration

Auth Context Provider

import {
createContext,
useContext,
useState,
useEffect,
ReactNode
} from "react";
import {
setBaseUrl,
authenticateUser,
getUser,
signOutUser,
setAccessToken,
User
} from "@cpod/sdk";

interface AuthContextType {
user: User | null;
loading: boolean;
login: (username: string, password: string) => Promise<void>;
logout: () => Promise<void>;
}

const AuthContext = createContext<AuthContextType | null>(null);

export function AuthProvider({ children }: { children: ReactNode }) {
const [user, setUser] = useState<User | null>(null);
const [loading, setLoading] = useState(true);

useEffect(() => {
// Initialize SDK
setBaseUrl(process.env.NEXT_PUBLIC_CPOD_BASE_URL!);

// Check for stored token
const token = localStorage.getItem("access_token");
if (token) {
setAccessToken(token);
getUser()
.then(setUser)
.catch(() => {
localStorage.removeItem("access_token");
localStorage.removeItem("refresh_token");
})
.finally(() => setLoading(false));
} else {
setLoading(false);
}
}, []);

const login = async (username: string, password: string) => {
const tokens = await authenticateUser({ username, password });
localStorage.setItem("access_token", tokens.access_token);
localStorage.setItem("refresh_token", tokens.refresh_token);

const user = await getUser();
setUser(user);
};

const logout = async () => {
await signOutUser();
localStorage.removeItem("access_token");
localStorage.removeItem("refresh_token");
setUser(null);
};

return (
<AuthContext.Provider value={{ user, loading, login, logout }}>
{children}
</AuthContext.Provider>
);
}

export function useAuth() {
const context = useContext(AuthContext);
if (!context) {
throw new Error("useAuth must be used within AuthProvider");
}
return context;
}

Login Component

import { useState } from "react";
import { useAuth } from "./AuthProvider";

export function LoginForm() {
const { login } = useAuth();
const [username, setUsername] = useState("");
const [password, setPassword] = useState("");
const [error, setError] = useState("");
const [loading, setLoading] = useState(false);

const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
setError("");
setLoading(true);

try {
await login(username, password);
} catch (err) {
setError(err instanceof Error ? err.message : "Login failed");
} finally {
setLoading(false);
}
};

return (
<form onSubmit={handleSubmit}>
{error && <div className="error">{error}</div>}

<input
type="email"
value={username}
onChange={(e) => setUsername(e.target.value)}
placeholder="Email"
required
/>

<input
type="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
placeholder="Password"
required
/>

<button type="submit" disabled={loading}>
{loading ? "Logging in..." : "Log In"}
</button>
</form>
);
}

Protected Route

import { useAuth } from "./AuthProvider";
import { Navigate } from "react-router-dom";

export function ProtectedRoute({ children }: { children: ReactNode }) {
const { user, loading } = useAuth();

if (loading) {
return <div>Loading...</div>;
}

if (!user) {
return <Navigate to="/login" />;
}

return <>{children}</>;
}

Next.js Integration

Server-Side with API Key

app/api/chat/route.ts
import { initSDK, chat } from "@cpod/sdk";

// Initialize once
initSDK({
baseUrl: process.env.NEXT_PUBLIC_CPOD_BASE_URL!,
apiKey: process.env.CPOD_API_KEY!,
enablePathAdapter: true, // Required for current backends
});

export async function POST(request: Request) {
const { message } = await request.json();

const response = await chat.complete({ message });

return Response.json({ content: response.content });
}

Client-Side with User Auth

lib/cpod-client.ts
"use client";

import { setBaseUrl } from "@cpod/sdk";

let initialized = false;

export function initCpodClient() {
if (!initialized) {
setBaseUrl(process.env.NEXT_PUBLIC_CPOD_BASE_URL!);
initialized = true;
}
}
app/providers.tsx
"use client";

import { useEffect } from "react";
import { initCpodClient } from "@/lib/cpod-client";

export function Providers({ children }: { children: React.ReactNode }) {
useEffect(() => {
initCpodClient();
}, []);

return <>{children}</>;
}

Error Handling

import { authenticateUser, CyberPodError } from "@cpod/sdk";

try {
await authenticateUser({ username, password });
} catch (error) {
if (error instanceof CyberPodError) {
switch (error.status) {
case 400:
console.error("Invalid request format");
break;
case 401:
console.error("Invalid credentials");
break;
case 403:
console.error("Account disabled or locked");
break;
case 429:
console.error("Rate limited - too many attempts");
break;
case 500:
console.error("Server error - try again later");
break;
default:
console.error(`Error: ${error.message}`);
}
}
}