View as Json

GitHub OAuth (Passport)

The GitHub OAuth component provides a secure and standardized way to integrate GitHub authentication into your Servercn Express applications using the official passport, passport-github2.

Official Docs


Installation Guide

npx servercn-cli 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

  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


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-flow/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

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);
    }
  )
);

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
    });
  }
);
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;

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

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

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...

Installation

npx servercn-cli add oauth