Skip to main content

How to build oAuth Authentication with React & Altogic?

· 19 min read
Yasin Kuyuk

Introduction

This article will cover OAuth Provider Authentication basics using React and Altogic, a backend-as-a-service platform using its client library. You can checkout the Github repository and the demo application.

So, what is OAuth provider authentication?

The term OAuth stands for Open Authorization with 3rd party application such as social providers. Users can sign in to the application using their Google, Facebook, Twitter, Discord, Github accounts.

Provider redirects us to the specific route in the application with authorization token. It is critical to emphasize that the shared content is authorization token, not your password data.

Watch the video

How does the OAuth Provider Authentication work?

  1. Users select the OAuth provider from the frontend application

  2. After clicking the provider’s button, user is redirected related authorization page of the provider

  3. Users type their credentials on the providers sign in page

  4. The provider redirects user to the frontend application’s specific route called as Development-Callback URL

  5. The frontend application fetches and handles the response comes from the provider

Creating App in Altogic

We can create an app with the Altogic Designer really fast. To create an app via the Designer**:

  1. Log in to Altogic with your credentials.

  2. Select New app.

  3. In the App name field, enter a name for the app.

  4. And click Create.

Creating App in Altogic

Here, you can customize your subdomain, but not necessarily to do, Altogic automatically creates one for you, which will be your envUrl. You don’t need to worry if you lost your envUrl; you can get it from the Environments view of Designer.

Getting the environment URL

After creating our app, we need envUrl and clientKey to access our app via Altogic Client Library to create a web application.

In order to get the clientKey we need to enter the app which we have created before and;

  1. Click on App Settings at the left-bottom of the designer.

  2. And click on Client library keys section.

Getting Client Library Key

We can create new clientKey from that page, but thanks to Altogic for creating one clientKey automatically for us, so let’s copy the existing clientKey from the list.

How to Setup OAuth Provider Authentication with Altogic ?

Altogic currently supports 5 different OAuth providers:

We can configure any of these providers and combine them. In this tutorial, we will learn the entire key points to configure the OAuth providers with our app.

The Redirect URL is the link to which your front-end application is redirected after the user signs in with the provider. Firstly, we will configure our frontend application’s Redirect URL with Altogic.

  1. Click on App Settings at the left-bottom of the Designer.

  2. And click on Authentication section.

  3. Type your Redirect URL to the input field.

The remaining part is the configuration of the oAuth providers’ client with our app. We have to create an account with the providers we will use. As we indicate early, Altogic currently supports 5 different oAuth providers such as, Google, Twitter, Facebook, LinkedIn, and Github. We can view their configuration details at the bottom of this page. Before moving on to the self-configuration of each provider, there are 4 core concepts to explain:

  • Client ID: The ID of the application on the provider side

  • Client Secret: The secret client key of the application on the provider side

  • Development-Callback URL: The Redirect URL that we will pass to the application on the provider side

  • Client Sign In URL: The URL of the providers sign in page linked with our app. Since we have been using the Altogic Client Library, we won’t need this URL to redirect to the providers sign in page

We covered the fundamental concepts of the OAuth provider authentication. Let’s move on to the providers’ self configuration on their consoles.

Google

  1. Sign in to your Google Developer account

  2. Click on Create Project

  3. After creating the project select OAuth Consent Screen on the left sidebar

  4. Select user type as External and click on Create button

  5. Fill in the necessary blanks such as App Domain, Test Users , etc.

OAuth Consent Screen

  1. After setting up the OAuth Consent Screen, select Credentials on the left sidebar

  2. Click on Create Credentials → OAuth Client ID

  3. Select application type as Web Application

  4. Paste your Development-Callback URL to the Authorized Redirect URIs

  5. Your Client ID and Client Secret will be shown in the pop-up after creating the OAuth Client ID or in the right-top of the Credentials tab

  6. Paste Client ID and Client Secret to the related fields on Altogic Designer

Google Credential

You can watch video tutorial of the Configuration of Google OAuth provider:

Facebook

  1. Sign in to your Facebook Developers account

  2. Click on Create App on the right-top of the home page

  3. Select Consumer

  4. Set up Facebook Login to your app

  5. Select App Review → Permission and Features on the left sidebar

  6. Get advanced access for the public_profile

  7. Select Facebook Login → Settings on the left sidebar

  8. Paste your Development-Callback URL to the Valid OAuth Redirect URIs

  9. Select Settings → Basic on the left sidebar

  10. Copy&paste the App ID and App Secret to the related fields on Altogic Designer.

Facebook Credetials

You can watch video tutorial of the Configuration of Facebook OAuth provider:

Twitter

  1. Sign in to your Twitter Developer Portal

  2. Click on Create Project in the middle of the home page

  3. Enter the necessary information related(App name, description, etc.)

  4. After creating your project, click Edit button on Authentication Settings tab

  5. Make sure 3-legged OAuth and Request email addresses from users is selected

  6. Paste your Development-Callback URL to the Callback URLs field

  7. Type your Website, Terms of Service, Privacy Policy URLs

  8. Change tab to the Keys and Tokens and Regenerate Keys

  9. Copy&paste the API Key and Key Secret to the related fields on Altogic Designer. (Consumer key and consumer secret)

Twitter Credentials

You can watch video tutorial of the Configuration of Twitter OAuth provider:

Discord

  1. Sign in to your Discord Developers account

  2. Click on New Application on the right bottom of the home page

  3. Give your application a name and click on Create

  4. Select OAuth2 on the left sidebar

  5. Click on Add Redirect button

  6. Paste your Development-Callback URL in here

  7. Copy&paste the Client ID and Client Secret to the related fields on Altogic Designer.

Discord Credentials

You can watch video tutorial of the Configuration of Discord OAuth provider:

Github

  1. Sign in to your Github account.

  2. Open your profile dropdown on the right-top of the page and click Settings

  3. Click on Developer Settings at the bottom of the Settings page

  4. Select OAuth Apps and click on Register a new application

Github Create OAuth App

  • Homepage URL: Our frontend application’s index URL

  • Authorization callback URL: Auth redirect URL that Altogic creates for us

After creating our OAuth app on Github, there will be Client ID and Client Secrets presented in the General tab. Copy&paste them to the related fields on Altogic Designer.

Github Credentials

You can watch video tutorial of the Configuration of Github OAuth provider:

Frontend Development

Installation

Before installing the application, be sure you have already installed NodeJS in your development environment.

To install

💡 You can visit: https://nodejs.org/en/download/ to download.

To get started, open the terminal and create a new React project

// creates a react app with the name of 'altogic-oauth-provider-authentication-tutorial'
npx create-react-app altogic-oauth-provider-authentication-tutorial

The above command creates a React project in the altogic-oauth-provider-authentication-tutorialdirectory.

cd altogic-oauth-provider-authentication-tutorial
touch .env

Create a .env file in the root directory of your application, open the file in your editor and paste the following.

REACT_APP_ALTOGIC_ENV_URL = YOUR - APPLICATION - ENV - URL;
REACT_APP_ALTOGIC_CLIENT_KEY = YOUR - APPLICATION - CLIENT - KEY;

Replace YOUR-APPLICATION-ENV-URL and YOUR-APPLICATION-CLIENT-KEY with the envUrl and clientKey you copied before, then return to your terminal.

Install the Altogic client library to our project by using NPM or Yarn by running the following command:

// Installation of Altogic Client Library with NPM
npm i altogic

Next, create a file to handle the Altogic services and client.

Go back to your root directory and follow the commands below:

cd src
mkdir helpers
cd helpers
touch altogic.js

altogic.js will be created in the src/helpers directory. Open the file in your editor and paste the following.

import { createClient } from "altogic";

let envUrl = process.env.REACT_APP_ALTOGIC_ENV_URL;
let clientKey = process.env.REACT_APP_ALTOGIC_CLIENT_KEY;

const altogic = createClient(envUrl, clientKey, {
signInRedirect: "/signin",
});

export default altogic;

The third parameter of createClient function signInRedirect handles the redirection to the Sign In page automatically when you have invalid session tokens or signed out. This is a beneficial feature for managing sessions in scenarios when you sign out from your mobile phone.

User Interface Components

We will use Tailwind CSS and Headless UI library for styling the project. Run the following commands in the root directory to install the library.

npm install -D tailwindcss postcss autoprefixer
npm install @headlessui/react

Below command will create tailwind.config.js file:

npx tailwindcss init -p

Open the tailwind.config.js in editor and copy/paste following script to configure template paths:

module.exports = {
content: ["./src/**/*.{js,jsx,ts,tsx}"],
theme: {
extend: {},
},
plugins: [],
};

Open index.css file in src directory and add the following directives:

@tailwind base;
@tailwind components;
@tailwind utilities;

And we will use Font Awesome Icons in our project. You should install the Font Awesome Library to have well-looking components.

  1. Add SVG Core:

    npm i --save @fortawesome/fontawesome-svg-core

  2. Add Icon Packages:

    npm i --save @fortawesome/free-solid-svg-icons npm i --save @fortawesome/free-regular-svg-icons npm i --save @fortawesome/free-brands-svg-icons

  3. Add React Component

    npm i --save @fortawesome/[email protected]

We installed the necessary dependencies to our project for styling. Now, it is time to install react-router for routing our Single Page Application(SPA).

npm i react-router-dom

We completed the installation part of the project. We will use custom button components to style our application. Open your terminal on your root directory and type following commands one by one:

cd src
mkdir components
cd components
mkdir Buttons
cd Buttons
touch PrimaryButton.js SecondaryButton.js

Above commands will create PrimaryButton and SecondaryButton files under /src/components/Buttons directory. Now, you can copy&paste the following scripts to the related files:

import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faCircleNotch } from "@fortawesome/free-solid-svg-icons";
const PrimaryButton = ({ children, component, ...props }) => {
const fullWidth = props.fullWidth ? "w-full" : "";
return (
<button
className={`my-2 disabled:cursor-not-allowed text-sm whitespace-nowrap inline-flex items-center justify-center text-center px-4 py-2 border border-transparent rounded-md shadow-sm font-medium text-white bg-indigo-600 hover:bg-indigo-700 ${fullWidth}`}
onClick={props.customClickEvent}
disabled={props.disabled}
>
{props.loading ? (
<>
<FontAwesomeIcon icon={faCircleNotch} color="white" spin />
&nbsp;
</>
) : (
<></>
)}

{props.content}
{children}
{component}
</button>
);
};

export default PrimaryButton;
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faCircleNotch } from "@fortawesome/free-solid-svg-icons";

const SecondaryButton = (props) => {
return (
<button
className="whitespace-nowrap2 mx-1 text-indigo-600 text-sm inline-flex items-center justify-center px-4 py-2 border border-transparent rounded-md shadow-sm font-medium bg-slate-200 hover:bg-slate-300"
onClick={props.customClickEvent}
>
{props.loading ? (
<>
<FontAwesomeIcon icon={faCircleNotch} color="indigo-600" spin />
&nbsp;
</>
) : (
<></>
)}{" "}
{props.content}
</button>
);
};

export default SecondaryButton;

Implementation

Building Main Components

The next step is to create the core components that we’ll need for our application,

  1. ProviderSelector A component that can be used for redirection to the provider both sign in and sign up

  2. AuthRedirect A component that the user is redirected to after the provider’s authorization

  3. App The main application component. It renders all the views with their properties.

Switch to the root directory of your react application and run following commands:

cd src
mkdir pages
cd pages
touch AuthRedirect.js ProviderSelector.js

This creates a pages directory with the components in the src directory. Your folder structure should look similar to the screenshot:

Since the user must be redirected to the related providers authorization page, we will develop the ProviderSelector component.

Altogic Client Library has just one simple method to handle both sign up and sign in cases to simplify the authentication process. A user instance is created in the database right after the first sign in/up action with the application. Therefore; I don’t have to design multiple use cases as a developer. Thus, we will handle sign in and sign up functionalities within just one component. We will be adding two different routes such as /signin and /signup, however same component will be rendered inside them with different texts.

We will design 5 buttons for each provider: Google, Facebook, Twitter, Github, Discord. When the user clicks the related button, it triggers the signin(provider) function defined inside the component. We will call Altogic Client Library altogic.auth.signInWithProvider(provider) method to redirect the user to the related authorization page.

import { Link } from "react-router-dom";
import altogic from "../helpers/altogic";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import {
faFacebook,
faTwitter,
faGoogle,
faDiscord,
faGithub,
} from "@fortawesome/free-brands-svg-icons";
const ProviderSelector = (props) => {
const type = props.type;

// This function will be triggered after the user selects provider from the UI.
//It takes the selected provider as parameter and redirects user to the providers login&authorization page
const signin = (provider) => {
altogic.auth.signInWithProvider(provider);
};
return (
<div className="flex mt-10">
<div className="w-full max-w-md m-auto bg-white rounded-lg border border-primaryBorder shadow-lg py-1 px-16">
<div className="min-h-full flex py-5 pl-7 sm:px-6 lg:px-8 ">
<div className="max-w-md w-full space-y-8">
<div className=" text-center">
<img
className="mx-auto w-auto"
src={require("../images/logo.png")}
alt="logo"
/>
<h2 className="mt-1 font-semibold text-3xl text-gray-900 ">
{type}
</h2>
<p className="text-gray-400">Choose your account</p>
</div>
<div className="text-center text-gray-400 text-sm">
<div className="btn-group text-left">
<button
className=" my-2 text-indigo-600 text-sm pl-7 font-semibold py-2 border w-full rounded-md text-left bg-slate-200 hover:bg-slate-300"
onClick={(event) => {
event.preventDefault();
signin("google");
}}
>
<FontAwesomeIcon icon={faGoogle} size="xl" className="mr-3" />
Continue with Google
</button>
<button
className=" my-2 text-indigo-600 text-sm pl-7 font-semibold py-2 border w-full rounded-md text-left bg-slate-200 hover:bg-slate-300"
onClick={(event) => {
event.preventDefault();
signin("facebook");
}}
>
<FontAwesomeIcon
icon={faFacebook}
size="xl"
className="mr-3"
/>
Continue with Facebook
</button>
<button
className=" my-2 text-indigo-600 text-sm pl-7 font-semibold py-2 border w-full rounded-md text-left bg-slate-200 hover:bg-slate-300"
onClick={(event) => {
event.preventDefault();
signin("twitter");
}}
>
<FontAwesomeIcon
icon={faTwitter}
size="xl"
className="mr-3"
/>
Continue with Twitter
</button>
<button
className=" my-2 text-indigo-600 text-sm pl-7 font-semibold py-2 border w-full rounded-md text-left bg-slate-200 hover:bg-slate-300"
onClick={(event) => {
event.preventDefault();
signin("discord");
}}
>
<FontAwesomeIcon
icon={faDiscord}
size="xl"
className="mr-3"
/>
Continue with Discord
</button>
<button
className=" my-2 text-indigo-600 text-sm pl-7 font-semibold py-2 border w-full rounded-md text-left bg-slate-200 hover:bg-slate-300"
onClick={(event) => {
event.preventDefault();
signin("github");
}}
>
<FontAwesomeIcon icon={faGithub} size="xl" className="mr-3" />
Continue with Github
</button>
</div>
<p>
{type === "Sign In"
? "Don't have an account?"
: "Already have an account?"}
&nbsp;
{type === "Sign In" ? (
<Link to="/signup" className="text-blue-600">
Sign up
</Link>
) : (
<Link to="/signin" className="text-blue-600">
Sign in
</Link>
)}
&nbsp;
</p>
</div>
</div>
</div>
</div>
</div>
);
};

export default ProviderSelector;

As we early noticed in the article, users will be redirected to the frontend of the application with specific routing. We have to catch the return response of the redirection URL and take necessary actions on the application such as setting isAuth state in the context.

Now, we will develop a viewless AuthRedirect component to handle access_token from the provider and check whether the user is authenticated or somehow not.

import { useContext, useEffect } from "react";
import altogic from "../helpers/altogic";
import { useNavigate } from "react-router-dom";
import { AuthenticationContext } from "../context/AuthenticationContext";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faCircleNotch } from "@fortawesome/free-solid-svg-icons";

const AuthRedirect = () => {
const navigate = useNavigate();
const context = useContext(AuthenticationContext);
useEffect(() => {
// We define another function inside the useEffect hook to handle async functionalities.
const getUrl = async () => {
// Altogic client library function getAuthGrant() takes one parameter, which is access_token.It updates the session and user information on localStorage.
// If you don't pass a parameter to getAuthGrant() function, it automatically fetches the access_token from the URL. If no access token present in URL,
// it raises an error.
const resp = await altogic.auth.getAuthGrant();
if (resp.errors === null) {
navigate("/profile");
context.setIsAuth(true);
}
};
getUrl();
}, []);
return (
<>
<div className="text-center h-screen">
<FontAwesomeIcon
icon={faCircleNotch}
size="5x"
color="indigo-600"
spin
/>
</div>
</>
);
};

export default AuthRedirect;

BONUS: Extra Features/Components

To focus on the main purpose of the article, we won’t go over with the development of the below components in this article. However, you can reach the source code of the extra feature components from Github repository for full-featured app.

  1. Profile A component that the users can view, upload and remove their profile pictures

  2. Sessions A component that the users can view and sign out from their active sessions. Built with SessionTable and SessionItem sub-components.

  3. Footer ,Header ,ProfileDropdown Navigation bar and footer badge

  4. NotFound A component to catch the routes not presented in application routes

  5. Notification A component that notifies the user about requests/responses

  6. RequiresAuth A wrapper component that checks whether the user is authenticated before rendering a component; otherwise, it redirects the user to the signup/login page.

  7. RequiresNotAuth A wrapper component that checks whether the user is not authenticated before rendering a component; otherwise, it redirects the user back to the profile page(for example Sign In page).

  8. ModalContext , ModalProvider Provides context structure to communicate between components to show notifications to the user

You can see the list of related Altogic functions with above features:


// Upload file to the cloud storage using buckets
altogic.storage.bucket(<BUCKET_NAME>).upload(<FILENAME>,file,{createBucket:true})

// Update field of an instance on database
altogic.db.model(<MODEL_NAME>).object(<OBJECT_ID>).update({<OBJECT_FIELD>: <VALUE> })

// Remove files from the cloud storage
altogic.storage.bucket(<BUCKET_NAME>).deleteFiles([<LIST_OF_FILENAMES>])

// Unset instance field on database(profilePicture)
altogic.db.model(<MODEL_NAME>).object(<OBJECT_ID>).updateFields([{ field: <FIELD_NAME>, updateType: "unset" }])

// List all active sessions
altogic.auth.getAllSesssions()

// Sign out from the session
altogic.auth.signOut(token)

// Sign out from all sessions
altogic.auth.signOutAll()

Authentication Context

Here we came to the one of the core component and structure of app, AuthenticationContext created using useContext() hook. Context is used to generate shared data accessible across the component hierarchy without passing props to each component. For example, isAuth state stores the boolean value of is user authenticated or not. Almost every component must use the isAuth state to handle its internal functionalities. I could pass props to each component like parent to child. However it is not flexible and easy to use. I built an AuthenticationContext to handle and manage all the data and functionalities related with authentication.

I defined all my Altogic functions and related states in AuthenticationContext to distribute it to the child components.

import { createContext, useContext, useEffect, useState } from "react";
import { useNavigate } from "react-router-dom";
import altogic from "../helpers/altogic";
import { ModalContext } from "./ModalContext";

export const AuthenticationContext = createContext();

const AuthenticationProvider = ({ children }) => {
// Define states
const [sessions, setSessions] = useState(null);
const [isAuth, setIsAuth] = useState(false);
const context = useContext(ModalContext);

// We use useNavigate() method to switch routes
let navigate = useNavigate();

useEffect(() => {
const sendReq = async () => {
const resp = await getAllSessions();
setSessions(resp.sessions);
};

// Fetches the session data to determine if the user is authenticated and updates the context
if (altogic.auth.getSession()) {
setIsAuth(true);
sendReq();
}
}, [isAuth]);

// Signs out from the current session with Altogic Client Library signOut() function and updates isAuth state. We call this function in ProfileDropdown component
const signOutCurrentSession = async () => {
try {
const resp = await altogic.auth.signOut();

if (resp.errors === null) {
setIsAuth(false);
navigate("/signin");
}
} catch (error) {
console.error(error);
}
};

// This function will call same Altogic Client Library function with above signOutFromTheCurrentSession().
// But the difference is, we call this function in Sessions component, not in ProfileDropdown
const signOutSelectedSession = async (token) => {
const flag = token === altogic.auth.getSession().token;

try {
const resp = await altogic.auth.signOut(token);
if (resp.errors === null) {
const temp = await getAllSessions();
setSessions(temp.sessions);
if (flag) {
setIsAuth(false);
navigate("/signin");
} else {
await context.openModal(
"Signed out successfully from the selected session!",
"success"
);
}
} else {
await context.openModal(resp.errors.items[0].message, "error");
}
} catch (error) {
console.error(error);
}
};

// Sign out from all sessions with AltogicClientLibrary signOutAll() function
const signOutAllSessions = async () => {
try {
const resp = await altogic.auth.signOutAll();
if (resp.errors === null) {
setIsAuth(false);
navigate("/");
}
} catch (error) {
console.error(error);
}
};

// Get list of all active sessions with Altogic Client Library function getAllSessions()
const getAllSessions = async () => {
try {
return await altogic.auth.getAllSessions();
} catch (error) {
console.error(error);
return null;
}
};

return (
<AuthenticationContext.Provider
value={{
isAuth,
setIsAuth,
signOutCurrentSession,
signOutSelectedSession,
getAllSessions,
signOutAllSessions,
sessions,
}}
>
{children}
</AuthenticationContext.Provider>
);
};

export default AuthenticationProvider;

Finally, we have completed the implementation of the core components of our application. It only remains the development of the App component left. You can reach the source code of the additional side components from the Github repository.

We defined the routes in the App component. Routes are protected with RequiresAuth and RequiresNotAuth restriction components.

import "./App.css";
import { Routes, Route } from "react-router-dom";
import AuthRedirect from "./pages/AuthRedirect";
import Header from "./components/Header";
import ProviderSelector from "./pages/ProviderSelector";
import Home from "./pages/Home";
import Profile from "./pages/Profile";
import AuthenticationProvider from "./context/AuthenticationContext";
import Sessions from "./pages/Sessions";
import NotFound from "./pages/NotFound";
import RequiresAuth from "./components/Routes/RequiresAuth";
import RequiresNotAuth from "./components/Routes/RequiresNotAuth";
import ModalProvider from "./context/ModalContext";
import Footer from "./components/Footer";
function App() {
return (
<div className="App h-screen bg-slate-100">
<ModalProvider>
<AuthenticationProvider>
<Header />
<Routes>
<Route path="/" element={<Home />} />
{/* The difference between /signin and /signup route is only their static text. Functionality is totally same. */}
<Route
path="/signin"
element={
<RequiresNotAuth>
<ProviderSelector type="Sign In" />
</RequiresNotAuth>
}
/>
<Route
path="/signup"
element={
<RequiresNotAuth>
<ProviderSelector type="Sign Up" />
</RequiresNotAuth>
}
/>

{/* This the route the provier will redirect user after authorization */}
<Route path="/auth-redirect" element={<AuthRedirect />} />

<Route
path="/profile"
element={
<RequiresAuth>
<Profile />
</RequiresAuth>
}
/>
<Route
path="/sessions"
element={
<RequiresAuth>
<Sessions />
</RequiresAuth>
}
/>
<Route path="*" element={<NotFound />} />
</Routes>
</AuthenticationProvider>
</ModalProvider>
<Footer />
</div>
);
}

export default App;

Open the terminal in the root directory and paste the following command to run your application:

npm run start

Conclusion

In this tutorial, we walked through how to configure OAuth Provider Authentication in React app using Altogic and Tailwind CSS. We all covered detailed configuration of Google, Facebook, Twitter, Discord and Github providers in our Altogic app. If you build your backend application with coding in pure programming languages & frameworks; the configuration process will take your time and effort. However, with the significant help of the Altogic, we implement only 10 lines of code snippet inside the ProviderSelector, AuthRedirect components.

Check out the Github repository for the source code of the application for further details.