OAuth2
Kyrin provides built-in OAuth2 client support for social login providers. You can authenticate users via Google, GitHub, Facebook, and Discord.
Supported Providers
| Provider | OAuthProvider value |
|---|---|
"google" | |
| GitHub | "github" |
"facebook" | |
| Discord | "discord" |
Configuration
Before using OAuth2, you need to register your application with each provider to get your clientId and clientSecret.
Using the Auth Class
typescript
import { createAuth } from "kyrin";
const auth = createAuth({ jwtSecret: process.env.JWT_SECRET! });
auth.oauth.configure({
provider: "google",
clientId: process.env.GOOGLE_CLIENT_ID!,
clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
redirectUri: "https://your-app.com/auth/google/callback",
scope: ["openid", "email", "profile"],
});OAuthConfig
typescript
interface OAuthConfig {
provider: OAuthProvider; // "google" | "github" | "facebook" | "discord"
clientId: string; // OAuth app client ID
clientSecret: string; // OAuth app client secret
redirectUri: string; // Callback URL after authentication
scope?: string[]; // Requested permissions (provider-dependent)
}Default Scopes by Provider
| Provider | Default Scopes |
|---|---|
["openid", "email", "profile"] | |
| GitHub | ["read:user", "user:email"] |
["email", "public_profile"] | |
| Discord | ["identify", "email"] |
Authorization Flow
The OAuth2 flow has three steps:
1. Redirect to Provider
Generate the authorization URL and redirect the user.
typescript
import { createAuth } from "kyrin";
const auth = createAuth({ jwtSecret: process.env.JWT_SECRET! });
// Configure the provider
auth.oauth.configure({
provider: "github",
clientId: process.env.GITHUB_CLIENT_ID!,
clientSecret: process.env.GITHUB_CLIENT_SECRET!,
redirectUri: "https://your-app.com/auth/github/callback",
});
// Generate a random state value for CSRF protection
const state = auth.oauth.createState();
// Get the authorization URL
const url = auth.oauth.getAuthUrl("github", state);
// Redirect the user
// Response.redirect(url);2. Handle the Callback
After the user authenticates, the provider redirects back to your redirectUri with an authorization code. Exchange this code for tokens.
typescript
// In your callback route (e.g., /auth/github/callback)
app.get("/auth/github/callback", async (c) => {
const code = c.query("code");
const state = c.query("state");
if (!code || !state) {
return c.json({ error: "Missing code or state" }, 400);
}
// Exchange the authorization code for tokens
const tokens = await auth.oauth.exchangeToken("github", code);
if (!tokens) {
return c.json({ error: "Failed to exchange token" }, 400);
}
// Get the user's profile information
const user = await auth.oauth.getUser("github", tokens.accessToken);
if (!user) {
return c.json({ error: "Failed to get user info" }, 400);
}
// Create a session or JWT for your app
const session = auth.createSession(user.id, {
provider: "github",
email: user.email,
name: user.name,
});
return c.json({
sessionId: session.id,
user,
});
});3. Token Refresh
Access tokens expire. Use the refresh token to get a new one.
typescript
const newTokens = await auth.oauth.refresh("github", refreshToken);
if (newTokens) {
console.log("New access token:", newTokens.accessToken);
}Standalone OAuth2 Functions
For more control, use the standalone functions directly.
typescript
import {
setOAuthConfig,
getOAuthConfig,
getAuthorizationUrl,
exchangeCodeForToken,
getUserInfo,
refreshOAuthToken,
generateState,
} from "kyrin";
// Configure
setOAuthConfig({
provider: "google",
clientId: process.env.GOOGLE_CLIENT_ID!,
clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
redirectUri: "https://your-app.com/auth/google/callback",
});
// Get config
const config = getOAuthConfig("google");
// Generate state and get URL
const state = generateState();
const url = getAuthorizationUrl("google", state);
// Exchange code for tokens
const tokens = await exchangeCodeForToken("google", code);
// Get user info
const user = await getUserInfo("google", tokens.accessToken);
// Refresh token
const refreshed = await refreshOAuthToken("google", tokens.refreshToken!);Types
OAuthTokens
typescript
interface OAuthTokens {
accessToken: string;
refreshToken?: string; // Some providers may not return a refresh token
tokenType: string; // e.g., "bearer"
expiresIn: number; // Seconds until the access token expires
}OAuthUserInfo
typescript
interface OAuthUserInfo {
id: string; // Provider-specific user ID
email?: string;
name?: string;
picture?: string;
provider: OAuthProvider;
avatar?: string; // Alias for picture
}Complete Example
typescript
import { createAuth } from "kyrin";
import { createApp } from "kyrin";
const auth = createAuth({ jwtSecret: process.env.JWT_SECRET! });
const app = createApp();
// --- Configure providers ---
auth.oauth.configure({
provider: "google",
clientId: process.env.GOOGLE_CLIENT_ID!,
clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
redirectUri: "https://your-app.com/auth/google/callback",
});
auth.oauth.configure({
provider: "github",
clientId: process.env.GITHUB_CLIENT_ID!,
clientSecret: process.env.GITHUB_CLIENT_SECRET!,
redirectUri: "https://your-app.com/auth/github/callback",
});
// --- Login page ---
app.get("/login", async (c) => {
const googleState = auth.oauth.createState();
const githubState = auth.oauth.createState();
return c.json({
google: auth.oauth.getAuthUrl("google", googleState),
github: auth.oauth.getAuthUrl("github", githubState),
});
});
// --- OAuth callback handler ---
async function handleOAuthCallback(provider: "google" | "github", c: any) {
const { code, state } = c.query();
if (!code) return c.json({ error: "Missing authorization code" }, 400);
const tokens = await auth.oauth.exchangeToken(provider, code);
if (!tokens) return c.json({ error: "Token exchange failed" }, 400);
const user = await auth.oauth.getUser(provider, tokens.accessToken);
if (!user) return c.json({ error: "Failed to get user info" }, 400);
const session = auth.createSession(user.id, {
provider,
email: user.email,
name: user.name,
});
const jwt = await auth.signToken(user.id, {
role: "user",
});
return c.json({ sessionId: session.id, ...jwt, user });
}
app.get("/auth/google/callback", (c) => handleOAuthCallback("google", c));
app.get("/auth/github/callback", (c) => handleOAuthCallback("github", c));
// --- Protected route ---
app.get("/me", async (c) => {
const sessionId = c.req.headers.get("X-Session-Id");
if (!sessionId) return c.json({ error: "Not authenticated" }, 401);
const session = auth.getSession(sessionId);
if (!session) return c.json({ error: "Session expired" }, 401);
return c.json({
userId: session.userId,
profile: session.data,
});
});Next Steps
- Authentication — JWT tokens and the Auth class
- Session Management — User session handling
- Role-Based Access Control — Define roles and permissions