Implementing OAuth is painful. Redirect URIs, PKCE, token refresh, provider quirks — each provider has subtle differences. Arctic handles all of it with a clean, typed API.
What Is Arctic?
Arctic is a TypeScript library for OAuth 2.0 with built-in support for 50+ providers. No heavy dependencies, no framework lock-in — just OAuth done right.
Quick Start
npm install arctic
import { GitHub, Google, Discord } from "arctic";
// Initialize providers
const github = new GitHub(clientId, clientSecret);
const google = new Google(clientId, clientSecret, redirectURI);
const discord = new Discord(clientId, clientSecret, redirectURI);
How It Works
Step 1: Generate Authorization URL
import { generateState, generateCodeVerifier } from "arctic";
const state = generateState();
const codeVerifier = generateCodeVerifier();
const url = await google.createAuthorizationURL(state, codeVerifier, {
scopes: ["openid", "profile", "email"]
});
// Store state + codeVerifier in cookies
// Redirect user to url
Step 2: Handle Callback
const code = url.searchParams.get("code");
const state = url.searchParams.get("state");
// Verify state matches stored value
const tokens = await google.validateAuthorizationCode(code, codeVerifier);
// tokens.accessToken — use to fetch user info
// tokens.refreshToken — store for later
// tokens.idToken — decode for user info (OpenID providers)
Step 3: Get User Info
const response = await fetch("https://www.googleapis.com/oauth2/v3/userinfo", {
headers: { Authorization: `Bearer ${tokens.accessToken}` }
});
const user = await response.json();
// { sub: "123", name: "John", email: "john@gmail.com", picture: "..." }
Supported Providers (50+)
GitHub, Google, Discord, Apple, Microsoft, Facebook, Twitter/X, LinkedIn, Spotify, Slack, Twitch, Notion, Figma, Bitbucket, GitLab, Dropbox, Zoom, Salesforce, and many more.
Why Arctic Over Alternatives
| Arctic | Passport.js | NextAuth | |
|---|---|---|---|
| Bundle size | ~5KB | ~50KB + strategies | ~200KB |
| Framework | Agnostic | Express-oriented | Next.js-focused |
| PKCE support | Built-in | Varies by strategy | Yes |
| TypeScript | First-class | Types added later | Yes |
| Complexity | Minimal | Middleware chain | Config heavy |
Real-World Pattern
// Express route handler
app.get("/login/github", async (req, res) => {
const state = generateState();
const url = await github.createAuthorizationURL(state, {
scopes: ["user:email"]
});
res.cookie("oauth_state", state, { httpOnly: true, maxAge: 600000 });
res.redirect(url.toString());
});
app.get("/login/github/callback", async (req, res) => {
const { code, state } = req.query;
const storedState = req.cookies.oauth_state;
if (state !== storedState) return res.status(400).send("Invalid state");
const tokens = await github.validateAuthorizationCode(code);
const userResponse = await fetch("https://api.github.com/user", {
headers: { Authorization: `Bearer ${tokens.accessToken}` }
});
const githubUser = await userResponse.json();
// Create or update user in your database
// Create session
// Redirect to dashboard
});
Get Started
- Documentation
- GitHub — 3K+ stars
- Provider list
Building an app with social login? My Apify scraping tools help you gather competitive data for your product. Custom scraping: spinov001@gmail.com
Top comments (0)