React Chrome Extension with Microsoft OAuth2 Authentication (Vite & TypeScript)
Today’s topic is creating an extension using React.js, Vite, and TypeScript, and integrating it with Microsoft OAuth2 login.
Step 1: Setting Up the Chrome Extension with React and Vite
To begin, we’ll create a new React project specifically for your Chrome extension using Vite. Open your terminal and run the following command:
npm create vite@latest chrome-extension -- --template react-swc-ts
This command creates a project named chrome-extension
with a React template using SWC for compilation and TypeScript for type safety. After running the command, you should see a success message.
Step 2: Installing Dependencies and Project Setup
Navigate to your newly created project directory using cd chrome-extension
. Here, we'll install the necessary dependencies for our extension:
npm install
This command will install all the dependencies listed in your project’s package.json
file. Once the installation is complete, you're ready to configure the Chrome extension.
Step 3: Configuring the Manifest File
Every Chrome extension requires a manifest file named manifest.json
. This file defines essential information about your extension, such as its name, description, permissions, and functionalities.
To streamline the manifest file creation process, we’ll install a helpful library:
npm install @crxjs/vite-plugin@beta
This library provides a Vite plugin that simplifies integrating and managing the manifest file within your development workflow.
Next Steps:
In the following steps, we’ll explore how to utilize the @crxjs/vite-plugin
to configure the manifest file, write your extension's logic, and build it for deployment to Chrome.
npm i @crxjs/vite-plugin@beta -D
After installing lets configure our vite.config.ts
file.
Create manifest.json
file in main directory.
{
"manifest_version": 3,
"name": "My extension",
"version": "1.0.0",
"action": { "default_popup": "index.html" },
"icons": {
"16": "icon_logo_16px.png",
"32": "icon_logo_32px.png",
"48": "icon_logo_48px.png",
"128": "icon_logo_128px.png"
}
}
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react-swc";
import { crx } from "@crxjs/vite-plugin";
import manifest from "./manifest.json";
// https://vitejs.dev/config/
export default defineConfig({
plugins: [react(), crx({ manifest })],
server: {
port: 3000,
},
});
And that is it just build it using npm run build
and your dist folder ready for uploading extension to chrome. For uploading it go to chrome://extensions/
url in your chrome and activate developer mode:
Then you will see Load unpacked button. Click it and select your dist file that is all your extension is ready.
Step 2: Integrating Microsoft OAuth2 Login
To enable login functionality using Microsoft OAuth2, we need to configure our extension and prepare the React project .
Preparing the React Project:
Before we begin configuration, let’s streamline our project for this new feature.
- Cleaning Up Unnecessary Code:
We can remove any unused CSS code from your App.css
file. This helps maintain a clean codebase.
2. Clearing Unneeded Code (Optional):
If the App.tsx
file doesn't contain any relevant code for the extension's functionality, you can consider clearing it. However, if it has some initial structure or imports you want to keep, proceed to the next step without clearing it entirely.
import './App.css'
function App() {
return (
<div>
My Extension
</div>
)
}
export default App
After doing that again build it and in chrome://extensions/
tab just click reload button for reloading out extension.
You will see our extension is changed.
To facilitate login functionality and access user data, our extension will require specific permissions from Chrome. We’ll update the manifest.json
file to grant these permissions.
Permissions Needed:
chrome.storage
: This allows our extension to store and retrieve data locally within the browser. We'll likely use this to store authentication tokens.chrome.identity
: This permission enables the extension to interact with the user's signed-in Google account (if applicable). While not directly related to Microsoft login, it might be useful depending on your extension's functionality.chrome.tabs
: This permission grants the extension access to information about open tabs. This might be necessary if your login process involves interacting with specific tabs during authentication.
And lets start doing our registration.
First we need to learn redirection uri for redirection back after complating login process. For learning that we need to use:
chrome.identity.getRedirectURL()
In app js lets define it and write it to the scrreen like that:
import './App.css'
function App() {
const redirectUri = chrome.identity.getRedirectURL();
return (
<div>
My Redirect URI - {redirectUri}
</div>
)
}
export default App
Note: when we write chrome.something it start returning error like:
For solving it we need to install chrome types with using;
npm i @types/chrome
then error disappear.
When we build and reload our extension in chrome we will see our redirect uri.
Great we get our redirectUri. Now we need to register it. Lets go to the https://portal.azure.com/#home ->Microsoft Entra ID
And Click to Add->App registration:
We will see Register an application form. In Name
field we can write anything we want. And paste our extension url
and select Single page application
at the end click register.
In opened page you will see our credentials.
Copy and paste somewhere our creddentials for registration process.
Let’s switch to the our react app and update App.ts file.
import './App.css'
import { useState } from 'react';
function App() {
const redirectUri = chrome.identity.getRedirectURL();
const [isLoggedIn, setIsLoggedIn] = useState(false);
const [authToken, setAuthToken] = useState(null)
const [accessToken, setAccessToken] = useState(null)
const handleLogin = () => {}
const handleLogout = () => {}
return (
<div>
{!isLoggedIn ? (
<button onClick={handleLogin}>Login with Microsoft</button>
) : (
<div>
<p>You are logged in to Microsoft.</p>
<button onClick={handleLogout}>Logout</button>
</div>
)}
</div>
)
}
export default App
I will not explain detailed because you already know React js enought if you are looking for this tutorial. In there we have 2 button Login and logout. We will try to log in and store access and auth tokens from MS.
For login process we need to set up url for redirecting to login page of microsoft.
const clientId = 'your client id'
const redirectUri = chrome.identity.getRedirectURL()
const tenantId = 'Your tenant id'
const challenge = is a object which hold code challenge and code verifier. Details is in below
const baseUrl = "baseurl is url of Microsoft 365 enviroment url which you want or which scope you want to accessto access"
const authUrl2 = `https://login.microsoftonline.com/${tenantId}/oauth2/v2.0/authorize?client_id=${clientId}&response_type=code&redirect_uri=${redirectUri}&response_mode=query&scope=${baseUrl}/.default&state=SrwhVCiM7ELA&code_challenge=${challenge.code_challenge}&code_challenge_method=S256&prompt=login`
As you see in here we need challenge elements for that we can use pkce-challenge library for getting it. Lets complate our App.ts file and analyzze it.
We update our handleLogin function like this:
const [authToken, setAuthToken] = useState<string | null>(null)
useEffect(() => {
chrome.storage.sync.get(['authToken','challenge'], (result) => {
if (result.authToken && result.challenge) {
setIsLoggedIn(true);
setAuthToken(result.authToken)
setChallenge(result.challenge)
}
});
}, [])
const handleLogin = async () => {
const challengeResponse = await pkceChallenge()
const authUrl = `https://login.microsoftonline.com/${tenantID}/oauth2/v2.0/authorize?client_id=${clientId}&response_type=code&redirect_uri=${redirectUri}&response_mode=query&scope=${baseUrl}/.default&state=SrwhVCiM7ELA&code_challenge=${challengeResponse.code_challenge}&code_challenge_method=S256&prompt=login`
chrome.identity.launchWebAuthFlow({
url: authUrl,
interactive: true
}, async (redirectUrl) => {
if(redirectUrl){
const params = new URLSearchParams(new URL(redirectUrl).search);
const code = params.get('code');
chrome.storage.sync.set({ authToken: code, challenge:challengeResponse });
}
});
}
in there we create authUrl and challenge. using launchWebAuthFlow
.
What is launchWebAuthFlow
? launchWebAuthFlow
is a Chrome extension API function that initiates a web authentication flow, which can involve prompting the user to sign in and grant permissions.
Easily when we use it it create popup tab for registration with our url and after complating it it redirect back to our extension.
at the end as you see there have chrome.storage.sync.set
. This is for cacheing our authToken and challenge for future in chrome storage.
<div>
{!isLoggedIn ? (
<button onClick={handleLogin}>Login with Microsoft</button>
) : (
<div>
<p>You are logged in to Microsoft.</p>
<button onClick={handleLogout}>Logout</button>
</div>
)}
<p>Auth Token - {authToken}</p>
</div>
As you see when we click Login with Microsoft it create login tab for us.
When we log in we will see the result our Auth token is ready.
At the end its time to get access token with using this auth token.
For that lets create function called getAccessToken
const getAccessToken = async (token: string | null) => {
if(authToken){
fetch(`https://login.microsoftonline.com/${tenantID}/oauth2/v2.0/token`, {
method: 'POST',
headers: {
'Content-type': 'application/x-www-form-urlencoded',
},
credentials: 'omit',
body: `client_id=${clientId}&scope=${baseUrl}/.default&grant_type=authorization_code&code=${token}&redirect_uri=${redirectUri}&code_verifier=${challenge?.code_verifier}`,
})
.then((response) => {
return response.json()
}).then(data => {
console.log(data,'accessToken')
})
}else{
throw new Error("Something went wrong")
}
}
and put it in new button inside our App ts:
<div>
{!isLoggedIn ? (
<button onClick={handleLogin}>Login with Microsoft</button>
) : (
<div>
<p>You are logged in to Microsoft.</p>
<button onClick={handleLogout}>Logout</button>
<button onClick={()=>{getAccessToken(authToken)}}>Get access token</button>
</div>
)}
<p>Auth Token - {authToken}</p>
</div>
let's check this out.
as you see that is our access token.
In Plain English 🚀
Thank you for being a part of the In Plain English community! Before you go:
- Be sure to clap and follow the writer ️👏️️
- Follow us: X | LinkedIn | YouTube | Discord | Newsletter
- Visit our other platforms: Stackademic | CoFeed | Venture | Cubed
- More content at PlainEnglish.io