OAuth client for TypeScript: authorization via GitHub, Google and other providers

Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

OAuth client for TypeScript: implementing authorization on an educational website



In modern web development, OAuth (Open Authorization) has become the de facto standard for authenticating users through third-party services. For the educational website pythonlib.ru, we have prepared a description of a universal OAuth client in TypeScript that allows implementing login via GitHub, Google, and other popular providers.



What is an OAuth client?

An OAuth client is a software component that manages the process of obtaining permissions from a user to access their data on a third-party service. In the context of TypeScript, such a client typically implements the OAuth 2.0 protocol and includes:

  • Initialization of the authorization process
  • Handling callback requests
  • Exchanging an authorization code for an access token
  • Token refresh (refresh token)
  • Working with session state


Architecture of an OAuth client in TypeScript

Our client is built on the principles of modularity and type safety. Main components:



1. Provider configuration

interface OAuthProviderConfig {  clientId: string;  clientSecret: string;  redirectUri: string;  authorizationEndpoint: string;  tokenEndpoint: string;  scopes: string[];}

const githubConfig: OAuthProviderConfig = { clientId: process.env.GITHUB_CLIENT_ID!, clientSecret: process.env.GITHUB_CLIENT_SECRET!, redirectUri: 'https://pythonlib.ru/auth/callback/github', authorizationEndpoint: 'https://github.com/login/oauth/authorize', tokenEndpoint: 'https://github.com/login/oauth/access_token', scopes: ['read:user', 'user:email']};


2. Generating the authorization URL

function generateAuthUrl(config: OAuthProviderConfig, state: string): string {  const params = new URLSearchParams({    client_id: config.clientId,    redirect_uri: config.redirectUri,    scope: config.scopes.join(' '),    state: state,    response_type: 'code'  });    return `${config.authorizationEndpoint}?${params.toString()}`;}


3. Exchanging the code for a token

async function exchangeCodeForToken(  config: OAuthProviderConfig,  code: string): Promise<OAuthTokenResponse> {  const response = await fetch(config.tokenEndpoint, {    method: 'POST',    headers: {      'Content-Type': 'application/json',      'Accept': 'application/json'    },    body: JSON.stringify({      client_id: config.clientId,      client_secret: config.clientSecret,      code: code,      redirect_uri: config.redirectUri    })  });

if (!response.ok) { throw new Error(`Token exchange failed: ${response.statusText}`); }

return response.json();}


Working with tokens

After obtaining tokens, it is necessary to ensure their secure storage and refresh. It is recommended to use httpOnly cookies or a server-side session store.



Refreshing the access token

async function refreshAccessToken(  config: OAuthProviderConfig,  refreshToken: string): Promise<OAuthTokenResponse> {  const response = await fetch(config.tokenEndpoint, {    method: 'POST',    headers: {      'Content-Type': 'application/json'    },    body: JSON.stringify({      client_id: config.clientId,      client_secret: config.clientSecret,      refresh_token: refreshToken,      grant_type: 'refresh_token'    })  });

return response.json();}


Complete implementation example for GitHub OAuth

import { randomBytes } from 'crypto';

class GitHubOAuthClient { private config: OAuthProviderConfig; private stateStore: Map<string, string> = new Map();

constructor() { this.config = { clientId: process.env.GITHUB_CLIENT_ID!, clientSecret: process.env.GITHUB_CLIENT_SECRET!, redirec

Recommendations