Arctic

GitHub

OAuth 2.0 provider for GitHub Apps and OAuth Apps.

Also see the OAuth 2.0 guide.

Initialization

The redirect URI is optional but required by GitHub if there are multiple URIs defined.

import { GitHub } from "arctic";

const github = new GitHub(clientId, clientSecret, null);
const github = new GitHub(clientId, clientSecret, redirectURI);

Create authorization URL

import { generateState } from "arctic";

const state = generateState();
const scopes = ["user:email", "repo"];
const url = github.createAuthorizationURL(state, scopes);

Validate authorization code

validateAuthorizationCode() will either return an OAuth2Tokens, or throw one of OAuth2RequestError, ArcticFetchError, or a standard Error (parse errors). OAuth Apps will only return an access token (no expiration).

import { OAuth2RequestError, ArcticFetchError } from "arctic";

try {
	const tokens = await github.validateAuthorizationCode(code);
	const accessToken = tokens.accessToken();
} catch (e) {
	if (e instanceof OAuth2RequestError) {
		// Invalid authorization code, credentials, or redirect URI
		const code = e.code;
		// ...
	}
	if (e instanceof ArcticFetchError) {
		// Failed to call `fetch()`
		const cause = e.cause;
		// ...
	}
	// Parse error
}

If you're using GitHub Apps, GitHub will provide an expiration for the access token alongside a refresh token.

const tokens = await github.validateAuthorizationCode(code);
const accessToken = tokens.accessToken();
const accessTokenExpiresAt = tokens.accessTokenExpiresAt();
const refreshToken = tokens.refreshToken();

The refresh token expiration is returned as refresh_token_expires_in.

const tokens = await github.validateAuthorizationCode(code);
if (
	"refresh_token_expires_in" in tokens.data &&
	typeof tokens.data.refresh_token_expires_in === "number"
) {
	const refreshTokenExpiresIn = tokens.data.refresh_token_expires_in;
}

Refresh access tokens

For GitHub Apps, use refreshAccessToken() to get a new access token using a refresh token. The behavior is identical to validateAuthorizationCode().

import { OAuth2RequestError, ArcticFetchError } from "arctic";

try {
	const tokens = await github.refreshAccessToken(refreshToken);
	const accessToken = tokens.accessToken();
	const accessTokenExpiresAt = tokens.accessTokenExpiresAt();
	const refreshToken = tokens.refreshToken();
} catch (e) {
	if (e instanceof OAuth2RequestError) {
		// Invalid authorization code, credentials, or redirect URI
	}
	if (e instanceof ArcticFetchError) {
		// Failed to call `fetch()`
	}
	// Parse error
}

Get user profile

Use the /user endpoint.

const response = await fetch("https://api.github.com/user", {
	headers: {
		Authorization: `Bearer ${accessToken}`
	}
});
const user = await response.json();

Get user email

Add the email scope and use the /user/emails endpoint.

const scopes = ["user:email"];
const url = github.createAuthorizationURL(state, scopes);
const response = await fetch("https://api.github.com/user/emails", {
	headers: {
		Authorization: `Bearer ${accessToken}`
	}
});
const emails = await response.json();