Simplifying Google Authentication with MV3 and Firebase for Chrome Extensions (React/Typescript)
I know Google Auth is a pain right now with Chrome extensions on MV3 and Firebase, as the example shown on the Firebase docs won't work on Chrome extensions on MV3.
Tho google claims MV3 to be stable a lot of Google APIs aren't compatible with it an example being Firebase GoogleAuthProvider. I had this issue as well and have found a workaround for it.
Luckily, there is an API by Google that helps in setting up for us. Chrome Identity.
Once you get the access token from chrome identity you can use it authorized with Google for simple auth, or with other apis like google calendar and Gmail.
Before jumping into code there are some settings to be done google cloud level, firebase, and in your extension in manfiest.json
Go to - https://console.cloud.google.com/apis/credentials
Create a Client ID there.
After the client id is created use the type chrome extensions and put the item id as your chrome extension id.
Now once that is done you also have to enable Chrome identity API from Google apis/services.
You can get it here - https://console.cloud.google.com/apis/api/identitytoolkit.googleapis.com/
Once that is done we will also have to add the Chrome extension id in the Firebase auth
Safelist your client id (chrome extension id here just the id part)
Now we will have to make changes in the manifest.json to provide all the scopes, permission and others.
https://cloud.google.com/identity-platform/docs/web/chrome-extension (docs to refer by Google)
"permissions": [
"tabs",
"http://localhost/*",
"identity",
"indetity.profile",
"identity.email",
"https://www.googleapis.com/*",
"https://www.googleapis.com/auth/calendar.readonly",
"script-src 'self' https://apis.google.com 'unsafe-eval'; object-src 'self'"
],
"oauth2": {
"client_id": "YOUR_CLIENT_ID",
"scopes": [
"openid",
"email",
"profile",
"https://www.googleapis.com/auth/userinfo.email"
]
},
"key": "YOUR_EXTENSION_KEY"
YOUR_CLIENT_ID
- When you create a Google Client ID above. Click on the client id In the Additional Information section copy client id
from there and paste it in place of this tag.
YOUR_EXTENSION_KEY
- steps to retrieve this are below.
Open a file explorer on your computer.
Navigate to the appropriate location for your operating system:
Windows: Go to the following path:
%LOCALAPPDATA%\Google\Chrome\User Data\Default\Extensions
Mac: Go to the following path:
~/Library/Application Support/Google/Chrome/Default/Extensions
Linux: Go to the following path:
~/.config/google-chrome/Default/Extensions
In the Extensions folder, you will find subfolders named after the installed extensions. Look for the folder that corresponds to your extension.
Inside the extension folder, locate the
manifest.json
file.Open the
manifest.json
file in a text editor or an IDE.In the manifest.json file, find the
"key"
field. The value assigned to this field is the extension key generated by Chrome.jsonCopy code"manifest_version": 2, "key": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
- Note down the value of the
"key"
field.
Now finally after all this setup is done you can use chrome.identity
to retrieve the access token and use it with auth, or any Google apis.
Here is an example of Google auth done in react
with typescript
Function to retrieve auth token with identity
import { GoogleAuthProvider, signInWithCredential, signInWithPopup, User } from 'firebase/auth';
export const getGoogleAuthCredential = (scopes: string[] = ['profile', 'email', 'openid']) => {
return new Promise<ReturnType<typeof GoogleAuthProvider.credential>>((resolve, reject) => {
if (typeof chrome !== 'undefined' && chrome.identity) {
chrome.identity.getAuthToken({ interactive: true, scopes: scopes }, (token: any) => {
if (chrome.runtime.lastError) {
reject(chrome.runtime.lastError);
}
const credential = GoogleAuthProvider.credential(null, token);
resolve(credential);
});
} else {
// Handle the case when the `chrome` object is not available
reject(new Error('Chrome extension environment is required.'));
}
});
};
export const googleAuthHandler = async () => {
try {
let credential;
// Check if running in Chrome extension environment
if (window.location.origin.includes("chrome-extension")) {
credential = await getGoogleAuthCredential();
} else {
// Use regular GoogleAuthProvider for non-extension environments
const provider = new GoogleAuthProvider();
await signInWithPopup(auth, provider);
return;
}
delete credential.idToken;
const result = await signInWithCredential(auth, credential);
await addSettingsForNewGoogleUser(result.user);
triggerMessage("Successfully signed in with Google.", "success")
} catch (error) {
console.error(error);
triggerMessage("Error signing in with Google. Please try again.", "error")
return null;
}
};