View as Json

GitHub OAuth

Secure GitHub OAuth authentication integration using passport, passport-github2 for Express applications.

Official Docs

Installation Guide

npx servercn-cli@latest add oauth

You will be prompted to select a file upload provider:

? Select OAuth provider:  » - Use arrow-keys. Return to submit.
    Google
>   GitHub
    Google + GitHub

The CLI will then automatically configure the component based on your selected provider.


Prerequisites

GitHub OAuth Setup

  1. Go to your GitHub Settings
  2. Developer settings -> OAuth Apps -> New OAuth App
  3. Fill in the required information
  4. Set Homepage URL to http://localhost:9000 (or your production URL)
  5. Set Authorization callback URL to http://localhost:9000/api/auth/github/callback (or your production URL)
  6. Register application
  7. Generate a Client Secret
  8. Copy the Client ID and Client Secret

Environment Variables

Add the following to your .env file:

# replace all with your values
GITHUB_CLIENT_ID="your-github-client-id"
GITHUB_CLIENT_SECRET="your-github-client-secret"
GITHUB_REDIRECT_URI="your-github-redirectUri"
src/configs/env.ts
import dotenv from "dotenv-flow";
dotenv.config();
 
interface Config {
  PORT: number;
  NODE_ENV: string;
  LOG_LEVEL: string;
  CORS_ORIGIN: string;
 
  GITHUB_CLIENT_ID: string;
  GITHUB_CLIENT_SECRET: string;
  GITHUB_REDIRECT_URI: string;
}
 
const env: Config = {
  PORT: Number(process.env.PORT) || 3000,
  NODE_ENV: process.env.NODE_ENV || "development",
  LOG_LEVEL: process.env.LOG_LEVEL || "info",
  CORS_ORIGIN: process.env.CORS_ORIGIN || "*",
 
  GITHUB_CLIENT_ID: process.env.GITHUB_CLIENT_ID!,
  GITHUB_CLIENT_SECRET: process.env.GITHUB_CLIENT_SECRET!,
  GITHUB_REDIRECT_URI: process.env.GITHUB_REDIRECT_URI!
};
 
export default env;

Basic Implementation

1. Configure Passport Strategy

src/configs/passport.ts
import passport from "passport";
import { Strategy as GitHubStrategy, Profile } from "passport-github2";
import env from "./env";
 
passport.use(
  new GitHubStrategy(
    {
      clientID: env.GITHUB_CLIENT_ID,
      clientSecret: env.GITHUB_CLIENT_SECRET,
      callbackURL: env.GITHUB_REDIRECT_URI
    },
    function (
      accessToken: string,
      refreshToken: string,
      profile: Profile,
      cb: (error: Error | null, user?: any) => void
    ) {
      // console.log({ profile });
      return cb(null, profile);
    }
  )
);

2. Create a GitHub OAuth Controller

src/controllers/github-oauth.controller.ts
import { NextFunction, Request, Response } from "express";
import { Profile } from "passport-github2";
 
import { ApiResponse } from "../utils/api-response";
import { AsyncHandler } from "../utils/async-handler";
import { ApiError } from "../utils/api-error";
 
//? login with github
export const githubOAuth = AsyncHandler(
  async (req: Request, res: Response, next: NextFunction) => {
    const data = req.user as Profile | undefined;
 
    if (!data) {
      return next(ApiError.unauthorized("Authenticated failed!"));
    }
 
    // console.log(data);
 
    const user = {
      provider: data?.provider,
      providerId: data.id,
      name: data.displayName,
      email: data?.emails && data?.emails[0]?.value,
      isEmailVerified: true,
      avatar: data.photos && data.photos[0].value
    };
 
    //? save the data into your databases
 
    ApiResponse.ok(res, "Auth Successfull", {
      user
    });
  }
);

3. Create a GitHub OAuth Routes

src/routes/github-oauth.routes.ts
import { Router } from "express";
import passport from "passport";
import { githubOAuth } from "../controllers/github-oauth.controller";
 
const router = Router();
 
router.get(
  "/github",
  passport.authenticate("github", { scope: ["user:email"] })
);
 
router.get(
  "/github/callback",
  passport.authenticate("github", {
    failureRedirect: "/login", //? redirect route if authenticated is failed,
    session: false
  }),
  githubOAuth
);
 
export default router;

4. Initialize passport in app

src/app.ts
import express, { Express, Request, Response } from "express";
 
import { notFoundHandler } from "./middlewares/not-found-handler";
import { errorHandler } from "./middlewares/error-handler";
 
import AuthRoutes from "./routes/github-oauth.routes";
 
import "./configs/passport";
 
const app: Express = express();
 
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
 
// Routes
app.use("/api/auth", AuthRoutes);
 
// Not found handler (should be after routes)
app.use(notFoundHandler);
 
// Global error handler (should be last)
app.use(errorHandler);
 
export default app;

Success Response

{
  "success": true,
  "message": "Auth Successfull",
  "statusCode": 200,
  "data": {
    "user": {
      "provider": "github",
      "providerId": "170957638",
      "name": "Akkal Dhami",
      "email": "dhamiakkal21@gmail.com",
      "isEmailVerified": true,
      "avatar": "https://avatars.githubusercontent.com/u/170957638?v=4"
    }
  }
}

This response is formated by ApiResponse component.


Common Issues

"redirect_uri_mismatch"

Ensure your redirect URI in .env exactly matches the one configured in GitHub Developer Settings.

"bad_verification_code"

The code passed is incorrect or expired.

  • The authorization code has expired (codes expire after 10 minutes)
  • The code has already been used
google
github
facebook
google-github
github-facebook
google-facebook
google-facebook-github

File & Folder Structure

Loading files...
npx servercn-cli@latest add oauth

Contributed by