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 * as arctic from "arctic";

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

Create authorization URL

import * as arctic from "arctic";

const state = arctic.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, UnexpectedResponseError, or UnexpectedErrorResponseBodyError. OAuth Apps will only return an access token (no expiration).

import * as arctic from "arctic";

try {
	const tokens = await github.validateAuthorizationCode(code);
	const accessToken = tokens.accessToken();
} catch (e) {
	if (e instanceof arctic.OAuth2RequestError) {
		// Invalid authorization code, credentials, or redirect URI
		const code = e.code;
		// ...
	}
	if (e instanceof arctic.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 * as arctic 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 arctic.OAuth2RequestError) {
		// Invalid authorization code, credentials, or redirect URI
	}
	if (e instanceof arctic.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();