GitHub OAuth Authentication Vue.js and Node.js (No Passport)
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)
- Google OAuth Authentication React.js and Node.js(No Passport)
- GitHub OAuth Authentication React.js and Node.js(No Passport)
- How to Implement GitHub OAuth in React.js
- How to Implement Google OAuth2 in React.js

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

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

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.

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.

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: use
Vue_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.

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.

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.

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 GitHubgetGithubUser()
– 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
Google OAuth Authentication Vue.js and Node.js (No Passport)
Google OAuth Authentication React.js and Golang
Leave a ReplyCancel reply
This site uses Akismet to reduce spam.Learn how your comment data is processed.
Support Me!

Recent posts
Categories
- C#(2)
- C++(1)
- CSS / SCSS(3)
- Deno(8)
- Golang(31)
- JavaScript(5)
- NextJs(38)
- NodeJS(32)
- Programming(19)
- Python(19)
- React(38)
- Rust(35)
- Svelte(5)
- Vue(7)