@passkeyme/auth
v2.0.10
Published
Passkeyme Authentication SDK - Simpler than Firebase Auth
Maintainers
Readme
@passkeyme/auth
A simple, powerful authentication SDK that makes authentication easier than Firebase Auth.
🚀 Features
- 🔐 Multiple Auth Methods: OAuth (Google, GitHub), Passkeys (WebAuthn), Username/Password
- � Hosted Authentication: Secure, pre-built auth pages - no UI to build
- 🔄 Automatic Token Management: Auto-refresh, HTTP interceptors
- 🏠 Privacy-First: Self-hosted option, no vendor lock-in
- 📱 Modern: Built-in passkey support, TypeScript-first
- ⚡ Lightweight: Pure JavaScript, no heavy dependencies
📦 Installation
npm install @passkeyme/auth🔧 Quick Start
1. Initialize the SDK
import { PasskeymeAuth } from "@passkeyme/auth";
const auth = new PasskeymeAuth({
appId: "your-passkeyme-app-id",
redirectUri: "http://localhost:3000/auth/callback",
});
// Initialize on app start
await auth.init();2. Choose Your Authentication Approach
Approach 1: Hosted Auth Pages ✨ (Recommended)
// Redirects to your branded hosted login page
// User sees all available login methods (OAuth, Passkey, Username/Password)
auth.redirectToLogin();
// With options
auth.redirectToLogin({
authMethod: "passkey", // Pre-select passkey method
state: "custom-state", // Custom state for callback
});Approach 2: Direct OAuth ⚡
// Skip hosted page, go directly to OAuth provider
auth.redirectToOAuth("google");
auth.redirectToOAuth("github");
auth.redirectToOAuth("facebook");
// With custom redirect URI
auth.redirectToOAuth("google", "http://localhost:3000/custom/callback");The Key Difference:
redirectToLogin()→ Shows your branded page with all auth optionsredirectToOAuth('google')→ Goes directly to Google OAuth (skips hosted page)
Direct OAuth Provider
// Skip login page, go directly to OAuth provider
auth.redirectToOAuth("google");
auth.redirectToOAuth("github");
auth.redirectToOAuth("facebook");3. Handle Authentication Callback
On your callback page (/auth/callback):
// This handles the authentication return and gets the user
const user = await auth.handleAuthCallback();
if (user) {
// Redirect to your app
window.location.href = "/dashboard";
} else {
// Handle auth failure
window.location.href = "/login?error=auth_failed";
}4. Check Authentication Status
// Check if user is logged in
const user = auth.getCurrentUser();
if (user) {
console.log("Logged in as:", user.email);
} else {
console.log("Not logged in");
}
// Or check auth state
console.log("Auth state:", auth.state);
// Check on page load
await auth.init(); // This will restore session if available5. Logout
await auth.logout();🔗 HTTP Client Integration
Automatically add auth headers to your API calls:
With Axios
import axios from "axios";
import { setupHttpInterceptor } from "@passkeyme/auth";
const apiClient = axios.create({
baseURL: "https://your-api.com",
});
// Setup automatic token management
const cleanup = setupHttpInterceptor(auth, apiClient);
// Now all requests automatically include auth headers
const response = await apiClient.get("/protected-data");With Fetch
import { createAuthenticatedFetch } from "@passkeyme/auth";
const authenticatedFetch = createAuthenticatedFetch(auth);
// Use like regular fetch, but with automatic auth headers
const response = await authenticatedFetch("/api/protected-data");Manual Header Management
import { addAuthHeader } from "@passkeyme/auth";
const headers = await addAuthHeader(auth, {
"Content-Type": "application/json",
});
fetch("/api/data", { headers });🎭 Event Handling
Listen to authentication events:
const unsubscribe = auth.addEventListener((event) => {
switch (event.type) {
case "login":
console.log("User logged in:", event.user);
break;
case "logout":
console.log("User logged out");
break;
case "token_refresh":
console.log("Token refreshed");
break;
case "error":
console.error("Auth error:", event.error);
break;
}
});
// Clean up when done
unsubscribe();⚙️ Configuration Options
Basic Configuration
const auth = new PasskeymeAuth({
appId: "your-app-id", // Required: From Passkeyme dashboard
redirectUri: "http://localhost:3000/callback", // Optional: Default callback URL
debug: true, // Optional: Enable debug logging
});Custom Domain Configuration
const auth = new PasskeymeAuth({
appId: "your-app-id",
apiUrl: "https://auth.yourdomain.com", // Your custom domain
redirectUri: "https://yourapp.com/callback",
});Self-Hosted Configuration
const auth = new PasskeymeAuth({
appId: "your-app-id",
apiUrl: "http://localhost:8000", // Your self-hosted server
redirectUri: "http://localhost:3000/callback",
debug: true, // Helpful for development
});Storage Configuration
import { PasskeymeAuth, BrowserStorageProvider } from "@passkeyme/auth";
// Default behavior (localStorage)
const auth = new PasskeymeAuth({
appId: "your-app-id",
});
// Use sessionStorage instead
const auth = new PasskeymeAuth({
appId: "your-app-id",
storage: new BrowserStorageProvider(true), // true = use sessionStorage
});
// Custom storage implementation
class CustomStorageProvider {
async getItem(key) {
/* your implementation */
}
async setItem(key, value) {
/* your implementation */
}
async removeItem(key) {
/* your implementation */
}
}
const auth = new PasskeymeAuth({
appId: "your-app-id",
storage: new CustomStorageProvider(),
});State Subscription
// Subscribe to authentication state changes
const unsubscribe = auth.subscribe((state) => {
console.log("Auth state changed:", state);
// { user, loading, error, isAuthenticated }
});
// Get current state
const currentState = auth.getState();
// Clean up when done
unsubscribe();🆚 vs Firebase Auth
| Feature | Passkeyme Auth | Firebase Auth | | -------------------- | -------------------------- | --------------------------- | | Setup Complexity | ✅ Single provider | ❌ Multiple services needed | | Hosted Auth UI | ✅ Pre-built, customizable | ❌ Build your own | | Passkey Support | ✅ Built-in WebAuthn | ❌ Manual implementation | | Self-Hosting | ✅ Full control | ❌ Google-only | | Vendor Lock-in | ✅ Standard JWT | ❌ Firebase-specific | | Privacy | ✅ Your data | ❌ Google's servers | | Bundle Size | ✅ Lightweight | ❌ Heavy SDK |
📋 API Reference
PasskeymeAuth Class
class PasskeymeAuth {
// Properties
readonly state: AuthState;
// Methods
init(): Promise<void>;
redirectToLogin(options?: RedirectOptions): void;
redirectToOAuth(
provider: "google" | "github" | "facebook",
options?: RedirectOptions
): void;
handleAuthCallback(): Promise<User | null>;
logout(): Promise<void>;
getCurrentUser(): User | null;
getAccessToken(): Promise<string | null>;
refreshToken(): Promise<string>;
addEventListener(listener: AuthEventListener): () => void;
removeEventListener(listener: AuthEventListener): void;
}Types
interface User {
id: string;
email?: string;
name?: string;
picture?: string;
provider?: string;
createdAt?: string;
lastLoginAt?: string;
}
interface AuthState {
user: User | null;
loading: boolean;
error: string | null;
isAuthenticated: boolean;
}
interface RedirectOptions {
redirectTo?: string; // Where to go after successful auth
state?: string; // Custom state parameter
}🔧 Advanced Usage
Handling the Complete Flow
// 1. App initialization
const auth = new PasskeymeAuth({
appId: "your-app-id",
redirectUri: "http://localhost:3000/auth/callback",
});
await auth.init();
// 2. Login button click
document.getElementById("login-btn").addEventListener("click", () => {
auth.redirectToLogin({ redirectTo: "/dashboard" });
});
// 3. On callback page (/auth/callback)
const user = await auth.handleAuthCallback();
if (user) {
// Redirect to the intended page
const urlParams = new URLSearchParams(window.location.search);
const redirectTo = urlParams.get("redirectTo") || "/";
window.location.href = redirectTo;
}🔗 TypeScript Integration
This package is written in TypeScript and provides comprehensive type definitions.
Type Imports
import type {
PasskeymeConfig,
User,
AuthTokens,
OAuthProvider,
PasskeyCredential,
AuthState,
TokenStorage,
HttpInterceptor,
} from "@passkeyme/auth";
// Configuration interface
interface PasskeymeConfig {
appId: string;
baseUrl?: string; // Default: 'https://auth.passkeyme.com'
redirectUri: string;
debug?: boolean; // Enable debug logging
tokenStorage?: TokenStorage; // Custom token storage
httpInterceptors?: HttpInterceptor[]; // Custom HTTP interceptors
}
// User object structure
interface User {
id: string;
email: string;
name?: string;
avatar?: string;
emailVerified: boolean;
createdAt: string;
lastLoginAt: string;
metadata?: Record<string, any>;
}
// Authentication tokens
interface AuthTokens {
accessToken: string;
refreshToken: string;
expiresAt: number;
tokenType: string;
}Advanced Configuration
import { PasskeymeAuth, LocalStorageTokenStorage } from "@passkeyme/auth";
// Custom token storage implementation
class SecureTokenStorage implements TokenStorage {
async setTokens(tokens: AuthTokens): Promise<void> {
// Store in encrypted storage or secure keychain
await secureStorage.set("auth_tokens", encrypt(tokens));
}
async getTokens(): Promise<AuthTokens | null> {
const encrypted = await secureStorage.get("auth_tokens");
return encrypted ? decrypt(encrypted) : null;
}
async clearTokens(): Promise<void> {
await secureStorage.remove("auth_tokens");
}
}
// HTTP interceptor for API calls
const apiInterceptor: HttpInterceptor = {
request: async (config) => {
// Add custom headers or modify requests
config.headers = {
...config.headers,
"X-Client-Version": "1.0.0",
"X-Client-Platform": "web",
};
return config;
},
response: async (response) => {
// Handle responses globally
if (response.status === 401) {
// Handle unauthorized responses
await auth.logout();
window.location.href = "/login";
}
return response;
},
};
const auth = new PasskeymeAuth({
appId: "your-app-id",
redirectUri: "https://yourapp.com/auth/callback",
debug: process.env.NODE_ENV === "development",
tokenStorage: new SecureTokenStorage(),
httpInterceptors: [apiInterceptor],
});API Client Integration
import { PasskeymeAuth } from "@passkeyme/auth";
class ApiClient {
private auth: PasskeymeAuth;
constructor(auth: PasskeymeAuth) {
this.auth = auth;
}
async makeAuthenticatedRequest<T>(
url: string,
options: RequestInit = {}
): Promise<T> {
const authHeader = await this.auth.getAuthHeader();
const response = await fetch(url, {
...options,
headers: {
...options.headers,
Authorization: authHeader,
"Content-Type": "application/json",
},
});
if (!response.ok) {
if (response.status === 401) {
// Try to refresh token
await this.auth.refreshToken();
// Retry request with new token
return this.makeAuthenticatedRequest(url, options);
}
throw new Error(`API Error: ${response.status}`);
}
return response.json();
}
// Type-safe API methods
async getUserProfile(): Promise<User> {
return this.makeAuthenticatedRequest<User>("/api/user/profile");
}
async updateUserProfile(updates: Partial<User>): Promise<User> {
return this.makeAuthenticatedRequest<User>("/api/user/profile", {
method: "PATCH",
body: JSON.stringify(updates),
});
}
}Framework Integration Examples
Vanilla JavaScript (SPA)
// main.js
const auth = new PasskeymeAuth({
appId: "your-app-id",
redirectUri: window.location.origin + "/auth/callback",
});
await auth.init();
// Router logic
if (window.location.pathname === "/auth/callback") {
const user = await auth.handleAuthCallback();
if (user) {
window.location.href = "/dashboard";
}
} else if (!auth.getCurrentUser() && window.location.pathname !== "/login") {
window.location.href = "/login";
}Next.js Pages Router
// pages/auth/callback.js
import { useEffect } from "react";
import { useRouter } from "next/router";
import { auth } from "../lib/auth";
export default function AuthCallback() {
const router = useRouter();
useEffect(() => {
auth.handleAuthCallback().then((user) => {
if (user) {
router.push("/dashboard");
} else {
router.push("/login?error=auth_failed");
}
});
}, []);
return <div>Completing authentication...</div>;
}🛠️ Development
This package is part of the Passkeyme project. For development:
# Install dependencies
npm install
# Build the package
npm run build
# Run tests
npm test
# Development mode (watch)
npm run dev📄 License
MIT - see LICENSE file for details.
🤝 Support
- Documentation: https://docs.passkeyme.com
- Issues: https://github.com/passkeyme/passkeyme/issues
- Discord: https://discord.gg/passkeyme
