Movatterモバイル変換


[0]ホーム

URL:


CodevoWeb

PressESC to close

GitHub OAuth Authentication Vue.js and Node.js (No Passport)

0Comments47

This article will teach you how to implement GitHub OAuth Authentication withVue.js and Node.js without Passport.js.

Also, after the user is authenticated, we’ll return JWT access and refresh token cookies to the user’s browser or client.

Related Articles:

GitHub OAuth Authentication Vue.js and Node.js (No Passport)

Prerequisites

  • Basic knowledge of HTML, CSS, Vue.js and Golang is need.
  • You should have Golang installed on your computer.

Create an OAuth App on GitHub

First, you need to log into yourGitHub account, and at the top-right corner, click on the Profile icon and select“Settings”.

On the profile page, scroll to the bottom, and from the left-sidebar click“Developer settings”.

GitHub OAuth with react and node click on the developer settings

Now, selectOAuth Apps on the Developer settings page and click on the“New OAuth App” button on the right side.

GitHub OAuth with react and node developer settings

Next, provide the necessary information about the OAuth app and click on the“Register application” button.

The authorization callback URL should point to a route on your server. It’s similar to theGoogle OAuth implementation in the previous article.

GitHub OAuth with react and node register application

Next, click on the“Generate a new client secret” button to generate the secret key after GitHub has successfully created the OAuth app.

Don’t be alarmed when GitHub redirects you to provide your password again.

GitHub OAuth with react and node create credentials

Edit the.env file in your server folder and add the client ID, client secret, and the authorization callback URL.

.env

GITHUB_OAUTH_CLIENT_ID=your client Id hereGITHUB_OAUTH_CLIENT_SECRET=your client secret hereGITHUB_OAUTH_REDIRECT_URL=http://localhost:8000/api/sessions/oauth/github

Also, remember to update the.env.local file in the Vue.js app.

VITE_GITHUB_OAUTH_CLIENT_ID=your client Id hereVITE_GITHUB_OAUTH_CLIENT_SECRET=your client secret hereVITE_GITHUB_OAUTH_REDIRECT_URL=http://localhost:8000/api/sessions/oauth/github

Note: useVue_App_ prefix if you generated the app with the@vue/cli .

Generate the Consent Screen URL

Now, we are ready to add GitHub OAuth Authentication to our Vue.js application.

First, we need to generate the GitHub OAuth consent screen URL based on the client ID, scopes, and the authorization callback URL.

The scopes give us read access to specific resources on the GitHub API. In this mini-app, we are only interested the user’s email address.

Feel free to play around with the different scopes to better understand how to use them.

src/utils/getGithubUrl.js

export function getGitHubUrl(from) {  const rootURl = 'https://github.com/login/oauth/authorize';  const options = {    client_id: import.meta.env.VITE_GITHUB_OAUTH_CLIENT_ID,    redirect_uri: import.meta.env.VITE_GITHUB_OAUTH_REDIRECT_URL,    scope: 'user:email',    state: from,  };  const qs = new URLSearchParams(options);  return `${rootURl}?${qs.toString()}`;}

Build a Simple GitHub OAuth Button with Vue.js

Now let’s create a simple GitHub OAuth button with Vue.js.

github oauth with vuejs and nodejs

src/App.vue

<script setup>import GoogleLogo from './assets/google.svg';import GitHubLogo from './assets/github.svg';import { getGoogleUrl } from './utils/getGoogleUrl';import { getGitHubUrl } from './utils/getGitHubUrl';const from = '/';</script><template>  <div class="container">    <div class="social-auth">      <!-- Google OAuth -->      <a :href="getGoogleUrl(from)" class="auth-btn google-auth">        <img :src="GoogleLogo" alt="Google Logo" />        <span>Google</span>      </a>      <!-- GitHub OAuth -->      <a :href="getGitHubUrl(from)" class="auth-btn github-auth">        <img :src="GitHubLogo" alt="GitHub Logo" />        <span>Google</span>      </a>    </div>  </div></template><style>* {  margin: 0;  padding: 0;  box-sizing: border-box;}a {  text-decoration: none;  color: inherit;}html {  font-size: 62.5%;}body {  font-family: Roboto, sans-serif;  color: #222;  font-size: 1.6rem;}.container {  background-color: #2363eb;  height: 100vh;  width: 100vw;  display: flex;  align-items: center;  justify-content: center;}.social-auth {  max-width: 27rem;  width: 100%;  display: flex;  align-items: center;  flex-direction: column;}.auth-btn {  background-color: #fff;  border-radius: 5px;  padding: 0.6rem 0;  width: 100%;  display: flex;  align-items: center;  justify-content: center;  transition: all 0.3s ease-in-out;}.auth-btn img {  height: 4rem;  margin-right: 1rem;}.auth-btn span {  font-size: 1.8rem;}.auth-btn:hover {  box-shadow: 0 1px 13px 0 rgb(0 0 0 / 15%);}.auth-btn.google-auth {  margin-bottom: 1.5rem;}</style>

Next, click on the GitHub Oauth button and you should be taken to the GitHub consent screen.

GitHub OAuth with react and node consent screen

Click on the greenAuthorize button on the GitHub consent screen or log in with your email and password if you haven’t done that.

A404 error should be returned by your NodeJs server assuming the server is running.

The important part of the authorization callback URL is the code in the query string. We’ll later use the code to obtain the access token on the server.

GitHub OAuth with react and node error route not implemented

The server returned a404 error because we haven’t implemented the GitHub OAuth logic on the server yet.

Implement GitHub OAuth on the Server

Now, run the following command in the terminal to install theAxios package. TheAxios package will enable us to make HTTP requests to the GitHub API.

yarn add axios

Update theconfig/custom-environment-variables.ts file to have the GitHub OAuth client Id, client secret, and the authorization callback URL.

config/custom-environment-variables.ts

export default {  dbName: 'MONGODB_USERNAME',  dbPass: 'MONGODB_PASSWORD',    accessTokenPrivateKey: 'ACCESS_TOKEN_PRIVATE_KEY',  accessTokenPublicKey: 'ACCESS_TOKEN_PUBLIC_KEY',  refreshTokenPrivateKey: 'REFRESH_TOKEN_PRIVATE_KEY',  refreshTokenPublicKey: 'REFRESH_TOKEN_PUBLIC_KEY',  googleClientId: 'GOOGLE_OAUTH_CLIENT_ID',  googleClientSecret: 'GOOGLE_OAUTH_CLIENT_SECRET',  googleOauthRedirect: 'GOOGLE_OAUTH_REDIRECT_URL',  githubClientId: 'GITHUB_OAUTH_CLIENT_ID',  githubClientSecret: 'GITHUB_OAUTH_CLIENT_SECRET',  githubOauthRedirect: 'GITHUB_OAUTH_REDIRECT_URL',};

Retrieve the GitHub OAuth Access Token and User’s Profile

Add these two functions to thesession.service.ts file in the services folder.

  • getGithubOathToken() – To obtain the OAuth Access Token from GitHub
  • getGithubUser() – To retrieve the user’s profile data with the Access Token

Below are the functions to retrieve the GitHub access token and the user’s profile data:

src/services/session.service.ts

// ? GitHub OAuthtype GitHubOauthToken = {  access_token: string;};interface GitHubUser {  login: string;  id: number;  node_id: string;  avatar_url: string;  gravatar_id: string;  url: string;  html_url: string;  followers_url: string;  following_url: string;  gists_url: string;  starred_url: string;  subscriptions_url: string;  organizations_url: string;  repos_url: string;  events_url: string;  received_events_url: string;  type: string;  site_admin: boolean;  name: string;  company: string;  blog: string;  location: null;  email: string;  hireable: boolean;  bio: string;  twitter_username: string;  public_repos: number;  public_gists: number;  followers: number;  following: number;  created_at: Date;  updated_at: Date;}export const getGithubOathToken = async ({  code,}: {  code: string;}): Promise<GitHubOauthToken> => {  const rootUrl = 'https://github.com/login/oauth/access_token';  const options = {    client_id: config.get<string>('githubClientId'),    client_secret: config.get<string>('githubClientSecret'),    code,  };  const queryString = qs.stringify(options);  try {    const { data } = await axios.post(`${rootUrl}?${queryString}`, {      headers: {        'Content-Type': 'application/x-www-form-urlencoded',      },    });    const decoded = qs.parse(data) as GitHubOauthToken;    return decoded;  } catch (err: any) {    throw Error(err);  }};export const getGithubUser = async ({  access_token,}: {  access_token: string;}): Promise<GitHubUser> => {  try {    const { data } = await axios.get<GitHubUser>(      'https://api.github.com/user',      {        headers: {          Authorization: `Bearer ${access_token}`,        },      }    );    return data;  } catch (err: any) {    throw Error(err);  }};

Complete code consisting of Google and GitHub OAuth implementation logic:

src/services/session.service.ts

import config from 'config';import axios from 'axios';import qs from 'qs';interface GoogleOauthToken {  access_token: string;  id_token: string;  expires_in: number;  refresh_token: string;  token_type: string;  scope: string;}export const getGoogleOauthToken = async ({  code,}: {  code: string;}): Promise<GoogleOauthToken> => {  const rootURl = 'https://oauth2.googleapis.com/token';  const options = {    code,    client_id: config.get<string>('googleClientId'),    client_secret: config.get<string>('googleClientSecret'),    redirect_uri: config.get<string>('googleOauthRedirect'),    grant_type: 'authorization_code',  };  try {    const { data } = await axios.post<GoogleOauthToken>(      rootURl,      qs.stringify(options),      {        headers: {          'Content-Type': 'application/x-www-form-urlencoded',        },      }    );    return data;  } catch (err: any) {    console.log('Failed to fetch Google Oauth Tokens');    throw new Error(err);  }};interface GoogleUserResult {  id: string;  email: string;  verified_email: boolean;  name: string;  given_name: string;  family_name: string;  picture: string;  locale: string;}export async function getGoogleUser({  id_token,  access_token,}: {  id_token: string;  access_token: string;}): Promise<GoogleUserResult> {  try {    const { data } = await axios.get<GoogleUserResult>(      `https://www.googleapis.com/oauth2/v1/userinfo?alt=json&access_token=${access_token}`,      {        headers: {          Authorization: `Bearer ${id_token}`,        },      }    );    return data;  } catch (err: any) {    console.log(err);    throw Error(err);  }}// ? GitHub OAuthtype GitHubOauthToken = {  access_token: string;};interface GitHubUser {  login: string;  id: number;  node_id: string;  avatar_url: string;  gravatar_id: string;  url: string;  html_url: string;  followers_url: string;  following_url: string;  gists_url: string;  starred_url: string;  subscriptions_url: string;  organizations_url: string;  repos_url: string;  events_url: string;  received_events_url: string;  type: string;  site_admin: boolean;  name: string;  company: string;  blog: string;  location: null;  email: string;  hireable: boolean;  bio: string;  twitter_username: string;  public_repos: number;  public_gists: number;  followers: number;  following: number;  created_at: Date;  updated_at: Date;}export const getGithubOathToken = async ({  code,}: {  code: string;}): Promise<GitHubOauthToken> => {  const rootUrl = 'https://github.com/login/oauth/access_token';  const options = {    client_id: config.get<string>('githubClientId'),    client_secret: config.get<string>('githubClientSecret'),    code,  };  const queryString = qs.stringify(options);  try {    const { data } = await axios.post(`${rootUrl}?${queryString}`, {      headers: {        'Content-Type': 'application/x-www-form-urlencoded',      },    });    const decoded = qs.parse(data) as GitHubOauthToken;    return decoded;  } catch (err: any) {    throw Error(err);  }};export const getGithubUser = async ({  access_token,}: {  access_token: string;}): Promise<GitHubUser> => {  try {    const { data } = await axios.get<GitHubUser>(      'https://api.github.com/user',      {        headers: {          Authorization: `Bearer ${access_token}`,        },      }    );    return data;  } catch (err: any) {    throw Error(err);  }};

User Model

If you came from a previous tutorial in this series then update theuser.model.ts file to have theprovider field.

When a user creates an account with GitHub OAuth, we’ll set theprovider field toGitHub whereas a user who registers with email and password will have theprovider to belocal .

src/models/user.model.ts

import {  DocumentType,  getModelForClass,  index,  modelOptions,  pre,  prop,} from '@typegoose/typegoose';import bcrypt from 'bcryptjs';@index({ email: 1 })@pre<User>('save', async function () {  // Hash password if the password is new or was updated  if (!this.isModified('password')) return;  // Hash password with costFactor of 12  this.password = await bcrypt.hash(this.password, 12);})@modelOptions({  schemaOptions: {    // Add createdAt and updatedAt fields    timestamps: true,  },})// Export the User class to be used as TypeScript typeexport class User {  @prop()  name: string;  @prop({ unique: true, required: true })  email: string;  @prop({ required: true, minlength: 8, maxLength: 32, select: false })  password: string;  @prop({ default: 'user' })  role: string;  @prop({ default: 'default.png' })  photo: string;  @prop({ default: false })  verified: boolean;  @prop({ default: 'local' })  provider: string;  // Instance method to check if passwords match  async comparePasswords(hashedPassword: string, candidatePassword: string) {    return await bcrypt.compare(candidatePassword, hashedPassword);  }}// Create the user model from the User classconst userModel = getModelForClass(User);export default userModel;

Service to Upsert the User

Now, add this service function to enable us to upsert the user’s information in the MongoDB database.

src/services/user.service.ts

export const findAndUpdateUser = async (  query: FilterQuery<User>,  update: UpdateQuery<User>,  options: QueryOptions) => {  return await userModel.findOneAndUpdate(query, update, options);};

Create the GitHub OAuth Controller

Next, let’s create agithubOauthHandler function in theauth.controller.ts file. This controller will be evoked when GitHub redirects the user to the server.

src/controllers/auth.controller.ts

export const githubOauthHandler = async (  req: Request,  res: Response,  next: NextFunction) => {  try {    // Get the code from the query    const code = req.query.code as string;    const pathUrl = (req.query.state as string) ?? '/';    if (req.query.error) {      return res.redirect(`${config.get<string>('origin')}/login`);    }    if (!code) {      return next(new AppError('Authorization code not provided!', 401));    }    // Get the user the access_token with the code    const { access_token } = await getGithubOathToken({ code });    // Get the user with the access_token    const { email, avatar_url, login } = await getGithubUser({ access_token });    // Create new user or update user if user already exist    const user = await findAndUpdateUser(      { email },      {        email,        photo: avatar_url,        name: login,        provider: 'GitHub',        verified: true,      },      { runValidators: false, new: true, upsert: true }    );    if (!user) {      return res.redirect(`${config.get<string>('origin')}/oauth/error`);    }    // Create access and refresh tokens    const { access_token: accessToken, refresh_token } = await signToken(user);    res.cookie('access_token', accessToken, accessTokenCookieOptions);    res.cookie('refresh_token', refresh_token, refreshTokenCookieOptions);    res.cookie('logged_in', true, {      ...accessTokenCookieOptions,      httpOnly: false,    });    res.redirect(`${config.get<string>('origin')}${pathUrl}`);  } catch (err: any) {    return res.redirect(`${config.get<string>('origin')}/oauth/error`);  }};

In the above code, you’ll notice that when we called thefindAndUpdateUser service we defined above, we addedupsert: true in the query options.

Addingupsert: true will instruct Mongoose to create the user if the email used in the query doesn’t exist in the database. On the other hand, Mongoose will only update the user’s credentials if that email already exists in the database.

Create the Route

Edit thesession.routes.ts file and add the GitHub OAuth handler.

src/routes/session.routes.ts

import express from 'express';import { googleOauthHandler,githubOauthHandler } from '../controllers/auth.controller';const router = express.Router();router.get('/oauth/google', googleOauthHandler);router.get('/oauth/github', githubOauthHandler);export default router;

Add the Router To the Middleware Stack

Finally, add the session router defined above to the Express middleware stack in thesrc/app.ts file.

src/app.ts

import sessionRouter from './routes/session.route';// ? Register the session routerapp.use('/api/sessions', sessionRouter);

Conclusion

Congrats for reaching the end. In this article, you learned how to add GitHub OAuth to a Node.js and MongoDB application without using Passport.

You can find the complete code used in thistutorial on GitHub

Share Article:

Google OAuth Authentication Vue.js and Node.js (No Passport)

Left Arrow

Google OAuth Authentication React.js and Golang

Right Arrow

Leave a ReplyCancel reply

This site is protected by reCAPTCHA and the GooglePrivacy Policy andTerms of Service apply.

This site uses Akismet to reduce spam.Learn how your comment data is processed.

Support Me!

paypal donate button

Recent posts

Categories


[8]ページ先頭

©2009-2025 Movatter.jp