Skip to content

Instantly share code, notes, and snippets.

@raineorshine
Last active November 10, 2023 23:57
Show Gist options
  • Star 68 You must be signed in to star a gist
  • Fork 7 You must be signed in to fork a gist
  • Save raineorshine/970b60902c9e6e04f71d to your computer and use it in GitHub Desktop.
Save raineorshine/970b60902c9e6e04f71d to your computer and use it in GitHub Desktop.
How to set up user authentication for a Chrome Extension using the Chrome Identity API

How to set up user authentication for a Chrome Extension using the Chrome Identity API

  1. Create a private key file, from which you can create the manifest key and Application ID, as detailed here: https://stackoverflow.com/questions/23873623/obtaining-chrome-extension-id-for-development
  2. Add the manifest key to "key" in manifest.json
  3. Create a new project in Google Developer Console https://console.developers.google.com/project
  4. Go to "APIs & auth > Credentials" and create new client id for a Chrome Application using the Application ID generated in step 3.
  5. Copy the Client ID to oauth2.client_id in the manifest.json

Deprecated?

  1. Create a new app on the Chrome Web Store Developer Dashboard https://chrome.google.com/webstore/developer/dashboard
  2. Click on "more info" and copy the public key to "key" in manifest.json. (or not needed because of next step...?)

Resources

chrome.identity.getAuthToken({
interactive: true
}, function(token) {
if (chrome.runtime.lastError) {
alert(chrome.runtime.lastError.message);
return;
}
var x = new XMLHttpRequest();
x.open('GET', 'https://www.googleapis.com/oauth2/v1/userinfo?alt=json&access_token=' + token);
x.onload = function() {
alert(x.response);
};
x.send();
});
{
"key": "<Application ID>",
"name": "Identity test",
"version": "1",
"manifest_version": 2,
"background": {
"scripts": ["background.js"]
},
"permissions": [
"identity"
],
"oauth2": {
"client_id": "<Client ID>",
"scopes": [
"https://www.googleapis.com/auth/userinfo.email"
]
}
}
@herodrigues
Copy link

herodrigues commented Feb 28, 2019

I also tried that approach with identity.launchWebFlow, but it doesn't work if I use the value returned from identity.getRedirectURL as redirectURL parameter.

// const redirectURL = browser.identity.getRedirectURL()

const scopes = ['email']
let authURL = 'https://accounts.google.com/o/oauth2/auth'
authURL += `?client_id=${clientId}`
authURL += `&response_type=token`
authURL += `&redirect_uri=${encodeURIComponent('http://localhost')}`
authURL += `&scope=${encodeURIComponent(scopes.join(' '))}`

browser.identity.launchWebAuthFlow({
  url: authURL,
  interactive: true
})

I'm calling this piece of code in my background page from an action triggered in the content script. It doesn't work if I set redirect_uri to sender.tab.url (from the runtime.onMessage listener) either.
It works only if I use localhost 😕

I don't need the private key for though.
My extension is loaded in development mode. That's maybe the for that?
Who knows...

Couldn't find anything in the docs.

@kairyou
Copy link

kairyou commented Feb 6, 2020

for launchWebFlow

  • Create OAuth client ID, Application type: Web application, Authorized redirect URIs: https://<extension-id>.chromiumapp.org/
global.browser = require('webextension-polyfill'); // https://git.io/Jve8X
const redirectURL = browser.identity.getRedirectURL();
const { oauth2 } = browser.runtime.getManifest();
const clientId = oauth2.client_id;
const authParams = new URLSearchParams({
  client_id: clientId,
  response_type: 'token',
  redirect_uri: redirectURL,
  scope: ['email'].join(' '),
});
const authURL = `https://accounts.google.com/o/oauth2/auth?${authParams.toString()}`;
browser.identity.launchWebAuthFlow({ url: authURL, interactive: true }).then((responseUrl) => {
  console.warn({ responseUrl, authURL });
  const url = new URL(responseUrl);
  const urlParams = new URLSearchParams(url.hash.slice(1));
  const params = Object.fromEntries(urlParams.entries()); // access_token, expires_in
  fetch(`https://www.googleapis.com/oauth2/v1/userinfo?alt=json&access_token=${params.access_token}`, {
    method: 'GET',
    headers: { 'Content-Type': 'application/json' },
  }).then(response => response.json()).then((data) => {
    alert(JSON.stringify(data));
  });
}).catch((error) => {
  console.warn(error.message, authURL);
});
  • manifest.json
"permissions": [ "identity" ],
"oauth2": {
  "client_id": "***.apps.googleusercontent.com", // Client ID
  "scopes": ["https://www.googleapis.com/auth/userinfo.email"]
}

@jkcorrea
Copy link

For anyone reading this, make sure to include the trailing / when authorizing the https://<extension-id>.chromiumapp.org/ in Google dev console. I just spent the better part of an hour debugging my failing auth flow until I realized I was thinking too hard...

Thanks @kairyou and @raineorshine for the examples!

@LispyAriaro
Copy link

Thanks so much. This gist was EXACTLY what I needed. I spent so much time everywhere else.
Google's site here: https://developer.chrome.com/apps/app_identity ... showed how to get the token but not how to use the token to retrieve some data. Almost useless. Thank you again for this gist.

@garridio85
Copy link

This is a great explanation!

Do you know how I can refresh the access token by any chance? When I try to run the same call with interactive: false it throws an error saying user not logged in.

I know the user is logged in as I have all their info and an active session.

Any advice would be greatly appreciated.

@bartoszluczak
Copy link

for launchWebFlow

  • Create OAuth client ID, Application type: Web application, Authorized redirect URIs: https://<extension-id>.chromiumapp.org/
const redirectURL = browser.identity.getRedirectURL();
const { oauth2 } = browser.runtime.getManifest();
const clientId = oauth2.client_id;
const authParams = new URLSearchParams({
  client_id: clientId,
  response_type: 'token',
  redirect_uri: redirectURL,
  scope: ['email'].join(' '),
});
const authURL = `https://accounts.google.com/o/oauth2/auth?${authParams.toString()}`;
browser.identity.launchWebAuthFlow({ url: authURL, interactive: true }).then((responseUrl) => {
  console.warn({ responseUrl, authURL });
  const url = new URL(responseUrl);
  const urlParams = new URLSearchParams(url.hash.slice(1));
  const params = Object.fromEntries(urlParams.entries()); // access_token, expires_in
  fetch(`https://www.googleapis.com/oauth2/v1/userinfo?alt=json&access_token=${params.access_token}`, {
    method: 'GET',
    headers: { 'Content-Type': 'application/json' },
  }).then(response => response.json()).then((data) => {
    alert(JSON.stringify(data));
  });
}).catch((error) => {
  console.warn(error.message, authURL);
});

Great post by how should looks manifest file?

@mikeyyyzhao
Copy link

Has this changed in the last few weeks? It looks like launchWebAuthFlow is no longer a promise and we need to pass in a call back function instead as the second parameter. Otherwise, this errors out with an invocation error.

@p6l-richard
Copy link

@mikeyyyzhao that is specific to chrome.
If you want to continue your thenable promise, you can use a polyfill:
https://github.com/mozilla/webextension-polyfill#using-the-promise-based-apis

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment