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

Chrome Apps are deprecated, but this works on Chrome extensions too.

You can also only upload (not publish!) an extension to the Chrome Strore, then click on More info to get the private key
Thanks!

@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