Authentication
The SDK supports multiple authentication methods. Choose the one that best fits your use case.
Authentication Methods
| Method | Best For | Security |
|---|---|---|
| API Key | Server-side apps, scripts, automation | High (keep secret) |
| Username/Password | Client-side apps with user login | Medium |
| Access Token | When you already have a token | High |
Method 1: API Key (Recommended for Server-Side)
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}`);
}
}
}