Added an example using the identity API (#180)

* Added an example using the identity API

* Updated with review comment fixes
This commit is contained in:
wbamberg
2017-02-16 12:02:10 -08:00
committed by GitHub
parent 39cd28eb2b
commit 4474a6bd4b
10 changed files with 214 additions and 0 deletions

33
google-userinfo/README.md Normal file
View File

@@ -0,0 +1,33 @@
# google-userinfo
This add-on fetches the user's info from their Google account and displays their name in a notification.
In detail, it adds a browser action. When the user clicks the browser action, the add-on:
* uses `identity.launchWebAuthFlow()` to get an access token from Google. This asks the user to sign into Google, if they are not already signed in (authentication), then asks the user if they grant the WebExtension permission to get their user info, if the user has not already granted this permission (authorization).
* validates the access token
* passes the access token into a Google API that returns the user's info
* displays a notification containing the user's name.
This is following essentially the process documented here: https://developers.google.com/identity/protocols/OAuth2UserAgent.
## Setup ##
There's some basic setup you must do before you can use this example.
* **getting the redirect URL**: this represents the end point of the flow, where the access token is delivered to the WebExtension. The redirect URL is derived from the WebExtension's ID. To get the redirect URL for this example, install it, visit about:addons, and open its "Preferences" page. It will look something like "https://dc6ae45f54e3d55036b819b93a1876228e5f5f7b.extensions.allizom.org/".
* **registering your add-on with Google as an OAuth2 client**.
* Visit https://console.developers.google.com/apis/credentials
* Click "Create credentials", and select "OAuth client ID"
* Select "Web application", and give it a name. The name is shown to the user to help them understand whether to authorize the add-on.
* Paste the redirect URL into the " Authorized redirect URIs" box.
* Click "Create"
* You'll see a popup containing a Client ID and a secret. Copy the client ID (you can ignore the secret).
* Paste this value into authorize.js in place of YOUR-CLIENT-ID.
* Reload the add-on.
Note that because you have to edit authorize.js, we can't provide a prebuilt, presigned version of this add-on in the "builds" directory of this repo, as we can for other examples. So to run this example in Firefox you'll need to use the ["Load Temporary Add-on"](https://developer.mozilla.org/en-US/Add-ons/WebExtensions/Temporary_Installation_in_Firefox) feature, or use the [web-ext](https://developer.mozilla.org/en-US/Add-ons/WebExtensions/Getting_started_with_web-ext) tool.

View File

@@ -0,0 +1,74 @@
const REDIRECT_URL = browser.identity.getRedirectURL();
const CLIENT_ID = "YOUR-CLIENT-ID";
const SCOPES = ["openid", "email", "profile"];
const AUTH_URL =
`https://accounts.google.com/o/oauth2/auth
?client_id=${CLIENT_ID}
&response_type=token
&redirect_uri=${encodeURIComponent(REDIRECT_URL)}
&scope=${encodeURIComponent(SCOPES.join(' '))}`;
const VALIDATION_BASE_URL="https://www.googleapis.com/oauth2/v3/tokeninfo";
function extractAccessToken(redirectUri) {
let m = redirectUri.match(/[#\?](.*)/);
if (!m || m.length < 1)
return null;
let params = new URLSearchParams(m[1].split("#")[0]);
return params.get("access_token");
}
/**
Validate the token contained in redirectURL.
This follows essentially the process here:
https://developers.google.com/identity/protocols/OAuth2UserAgent#tokeninfo-validation
- make a GET request to the validation URL, including the access token
- if the response is 200, and contains an "aud" property, and that property
matches the clientID, then the response is valid
- otherwise it is not valid
Note that the Google page talks about an "audience" property, but in fact
it seems to be "aud".
*/
function validate(redirectURL) {
const accessToken = extractAccessToken(redirectURL);
if (!accessToken) {
throw "Authorization failure";
}
const validationURL = `${VALIDATION_BASE_URL}?access_token=${accessToken}`;
const validationRequest = new Request(validationURL, {
method: "GET"
});
function checkResponse(response) {
return new Promise((resolve, reject) => {
if (response.status != 200) {
reject("Token validation error");
}
response.json().then((json) => {
if (json.aud && (json.aud === CLIENT_ID)) {
resolve(accessToken);
} else {
reject("Token validation error");
}
});
});
}
return fetch(validationRequest).then(checkResponse);
}
/**
Authenticate and authorize using browser.identity.launchWebAuthFlow().
If successful, this resolves with a redirectURL string that contains
an access token.
*/
function authorize() {
return browser.identity.launchWebAuthFlow({
interactive: true,
url: AUTH_URL
});
}
function getAccessToken() {
return authorize().then(validate);
}

View File

@@ -0,0 +1,23 @@
function notifyUser(user) {
browser.notifications.create({
"type": "basic",
"title": "Google info",
"message": `Hi ${user.name}`
});}
function logError(error) {
console.error(`Error: ${error}`);
}
/**
When the button's clicked:
- get an access token using the identity API
- use it to get the user's info
- show a notification containing some of it
*/
browser.browserAction.onClicked.addListener(() => {
getAccessToken()
.then(getUserInfo)
.then(notifyUser)
.catch(logError);
});

View File

@@ -0,0 +1,22 @@
/**
Fetch the user's info, passing in the access token in the Authorization
HTTP request header.
*/
function getUserInfo(accessToken) {
const requestURL = "https://www.googleapis.com/oauth2/v1/userinfo?alt=json";
const requestHeaders = new Headers();
requestHeaders.append('Authorization', 'Bearer ' + accessToken);
const driveRequest = new Request(requestURL, {
method: "GET",
headers: requestHeaders
});
return fetch(driveRequest).then((response) => {
if (response.status === 200) {
return response.json();
} else {
throw response.status;
}
});
}

View File

@@ -0,0 +1 @@
The "person-32.png" "person-48.png" icons are taken from the Ionicons iconset (http://ionicons.com/), and are used here under the MIT license: http://opensource.org/licenses/MIT.

Binary file not shown.

After

Width:  |  Height:  |  Size: 393 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 573 B

View File

@@ -0,0 +1,41 @@
{
"name": "Google User Info",
"version": "1",
"manifest_version": 2,
"applications": {
"gecko": {
"id": "google-user-info@mozilla.org",
"strict_min_version": "53a1"
}
},
"icons": {
"48": "icons/person-48.png"
},
"browser_action": {
"browser_style": true,
"default_icon": "icons/person-32.png"
},
"permissions": [
"identity",
"notifications",
"*://www.googleapis.com/*",
"*://accounts.google.com/*"
],
"background": {
"scripts": [
"background/authorize.js",
"background/userinfo.js",
"background/main.js"
]
},
"options_ui": {
"page": "options/options.html"
}
}

View File

@@ -0,0 +1,16 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
</head>
<body>
<div>Redirect URL: <span id="redirect-url"></span></div>
<script src="options.js"></script>
</body>
</html>

View File

@@ -0,0 +1,4 @@
/**
Display the redirect URL.
*/
document.querySelector("#redirect-url").textContent = browser.identity.getRedirectURL();