├── img ├── gmail-logo.png └── developers-logo.png ├── LICENSE ├── manifest.json ├── readme.md └── js └── background.js /img/gmail-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abraham/chrome-extension-google-apis/HEAD/img/gmail-logo.png -------------------------------------------------------------------------------- /img/developers-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abraham/chrome-extension-google-apis/HEAD/img/developers-logo.png -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 GoogleDeveloperExperts 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "manifest_version": 2, 3 | "name": "GDE Sample: Chrome extension Google APIs", 4 | "version": "2", 5 | "icons": { 6 | "16": "img/developers-logo.png", 7 | "48": "img/developers-logo.png", 8 | "128": "img/developers-logo.png" 9 | }, 10 | "permissions": [ 11 | "background", 12 | "identity", 13 | "notifications", 14 | "storage", 15 | "alarms", 16 | "https://www.googleapis.com/*", 17 | "https://*.googleusercontent.com/*" 18 | ], 19 | "background": { 20 | "scripts": [ 21 | "js/background.js" 22 | ], 23 | "persistent": false 24 | }, 25 | "browser_action": { 26 | "default_icon": { 27 | "19": "img/gmail-logo.png", 28 | "38": "img/gmail-logo.png" 29 | }, 30 | "default_title": "Click to Sign in with Google" 31 | }, 32 | "oauth2": { 33 | "client_id": "114446495690-8ejpu2984hsodgvmn8vc9pblteupetas.apps.googleusercontent.com", 34 | "scopes": [ 35 | "profile", 36 | "https://www.googleapis.com/auth/gmail.readonly" 37 | ] 38 | }, 39 | "key": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnqFjzbt+LtejD1JhOyMUbejOHdLoemzryr+ZQHK2CEKuI0yVVYToFs7eNusH/hfxhnyF27G4BU8apsTc695EpVQGD0ANKdt6BjubRnA/4VcdkmfdD3D9nsdCc+fHkINRU5e05grfs/BETWW/JAUULduaNWGfhT7nLRqL6uc/mo45REM1PGuKnSB/f3LX97K0vX/7loX21ih8Ep7aQpcWIPfCwVsb80E4am4CJ6SlBtGD2wxdEW7VInXWMAHMJ1khsi6hJwGx9ZjbnC7UVxjhDWtERK28ylKr8mmuwYUPOhCHfy/uSW/0T9VhiKUQvUokpe1WveeON3kI5nGcM+nVKwIDAQAB" 40 | } 41 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | GDE Sample: Chrome extension Google APIs 2 | ======================================== 3 | 4 | This is a sample Google Chrome extension that demonstrates using the Chrome Identity API to authorize access to the [Gmail](https://developers.google.com/gmail/api/v1/reference/users/labels/get) and [Google+](https://developers.google.com/+/api/latest/people/get) APIs. It is a quick start to the [Chrome User Authentication documentation](https://developer.chrome.com/apps/app_identity). 5 | 6 | 7 | Getting started 8 | --------------- 9 | 10 | The quick and easy to get the code running on your computer. 11 | 12 | 1. Download the source code. 13 | 1. [Load the extension](https://developer.chrome.com/extensions/getstarted#unpacked) in developer mode. 14 | 1. Authorize extension. 15 | 16 | Diving further 17 | -------------- 18 | 19 | Getting the code working for your extension is a little more work since this sample comes preloaded with an existing Chrome extension id and Google APIs client details. 20 | 21 | 1. Have a [Chrome extension published](https://developer.chrome.com/apps/app_identity#add_permissions) with `identity` permission. It can be a private extension that does nothing. 22 | 1. Install extension from the CWS and [get the `key`](https://developer.chrome.com/apps/app_identity#copy_key). Adding this to your unpacked extension source makes the development ID match what the Google APIs app expects. 23 | 1. [Register Google APIs client](https://developer.chrome.com/apps/app_identity#client_id) and enable the Google APIs you plan on using. 24 | 1. Set the [API client details](https://developer.chrome.com/apps/app_identity#update_manifest) in the manifest. 25 | 1. Authorize extension. 26 | 27 | Notes 28 | ----- 29 | 30 | * The URLs of the Google APIs you will be using must be whitelisted in the manifest. 31 | 32 | 33 | Google product names and logos are owned by Google. 34 | -------------------------------------------------------------------------------- /js/background.js: -------------------------------------------------------------------------------- 1 | /** 2 | * GDE Sample: Chrome extension Google APIs. 3 | * 4 | * This Chrome extension is example code of how to use Chrome's identity API 5 | * to get access to Google's web APIs. 6 | */ 7 | 8 | /** 9 | * Create a basic Desktop notification. 10 | * 11 | * @param {object} options 12 | * @value {string} iconUrl - Image URL to display in notification. 13 | * @value {string} title - Notification header. 14 | * @value {string} message - Notification message. 15 | */ 16 | function createBasicNotification(options) { 17 | var notificationOptions = { 18 | 'type': 'basic', 19 | 'iconUrl': options.iconUrl, // Relative to Chrome dir or remote URL must be whitelisted in manifest. 20 | 'title': options.title, 21 | 'message': options.message, 22 | 'isClickable': true, 23 | }; 24 | chrome.notifications.create(options.id, notificationOptions, function(notificationId) {}); 25 | } 26 | 27 | /** 28 | * Show a notification that prompts the user to authenticate their Google account. 29 | */ 30 | function showAuthNotification() { 31 | var options = { 32 | 'id': 'start-auth', 33 | 'iconUrl': 'img/developers-logo.png', 34 | 'title': 'GDE Sample: Chrome extension Google APIs', 35 | 'message': 'Click here to authorize access to Gmail', 36 | }; 37 | createBasicNotification(options); 38 | } 39 | 40 | /** 41 | * Show a notification that authentication was completed successfully. 42 | * 43 | * @param {object} profile 44 | * @value {string} imageUrl - Google+ profile image URL. 45 | * @value {string} displayName - Google+ profile full name. 46 | */ 47 | function showProfileNotification(profile) { 48 | var options = { 49 | 'id': 'show-profile', 50 | 'iconUrl': profile.imageUrl, 51 | 'title': 'Welcome ' + profile.displayName, 52 | 'message': 'Gmail checker is now active', 53 | }; 54 | createBasicNotification(options); 55 | } 56 | 57 | /** 58 | * Triggered anytime user clicks on a desktop notification. 59 | */ 60 | function notificationClicked(notificationId){ 61 | // User clicked on notification to start auth flow. 62 | if (notificationId === 'start-auth') { 63 | getAuthTokenInteractive(); 64 | } 65 | clearNotification(notificationId); 66 | } 67 | 68 | /** 69 | * Clear a desktop notification. 70 | * 71 | * @param {string} notificationId - Id of notification to clear. 72 | */ 73 | function clearNotification(notificationId) { 74 | chrome.notifications.clear(notificationId, function(wasCleared) {}); 75 | } 76 | 77 | /** 78 | * Get users access_token. 79 | * 80 | * @param {object} options 81 | * @value {boolean} interactive - If user is not authorized ext, should auth UI be displayed. 82 | * @value {function} callback - Async function to receive getAuthToken result. 83 | */ 84 | function getAuthToken(options) { 85 | chrome.identity.getAuthToken({ 'interactive': options.interactive }, options.callback); 86 | } 87 | 88 | /** 89 | * Get users access_token in background with now UI prompts. 90 | */ 91 | function getAuthTokenSilent() { 92 | getAuthToken({ 93 | 'interactive': false, 94 | 'callback': getAuthTokenSilentCallback, 95 | }); 96 | } 97 | 98 | /** 99 | * Get users access_token or show authorize UI if access has not been granted. 100 | */ 101 | function getAuthTokenInteractive() { 102 | getAuthToken({ 103 | 'interactive': true, 104 | 'callback': getAuthTokenInteractiveCallback, 105 | }); 106 | } 107 | 108 | /** 109 | * If user is authorized, start getting Gmail count. 110 | * 111 | * @param {string} token - Users access_token. 112 | */ 113 | function getAuthTokenSilentCallback(token) { 114 | // Catch chrome error if user is not authorized. 115 | if (chrome.runtime.lastError) { 116 | showAuthNotification(); 117 | } else { 118 | updateLabelCount(token); 119 | } 120 | } 121 | 122 | /** 123 | * User finished authorizing, start getting Gmail count. 124 | * 125 | * @param {string} token - Current users access_token. 126 | */ 127 | function getAuthTokenInteractiveCallback(token) { 128 | // Catch chrome error if user is not authorized. 129 | if (chrome.runtime.lastError) { 130 | showAuthNotification(); 131 | } else { 132 | updateLabelCount(token); 133 | getProfile(token); 134 | } 135 | } 136 | 137 | /** 138 | * Get the current users Google+ profile to welcome them. 139 | * 140 | * https://developers.google.com/+/api/latest/people/get 141 | * 142 | * @param {string} token - Current users access_token. 143 | */ 144 | function getProfile(token) { 145 | get({ 146 | 'url': 'https://www.googleapis.com/plus/v1/people/me', 147 | 'callback': getProfileCallback, 148 | 'token': token, 149 | }); 150 | } 151 | 152 | /** 153 | * Got users Google+ profile, show welcome desktop notification. 154 | * 155 | * https://developers.google.com/+/api/latest/people/get 156 | * 157 | * @param {object} person - Google+ person resource. 158 | */ 159 | function getProfileCallback(person) { 160 | var options = { 161 | 'displayName': person.displayName, 162 | 'imageUrl': person.image.url + '0', 163 | }; 164 | showProfileNotification(options); 165 | } 166 | 167 | /** 168 | * Get details about the users Gmail inbox. 169 | * 170 | * https://developers.google.com/gmail/api/v1/reference/users/labels/get 171 | * 172 | * @param {string} token - Current users access_token. 173 | */ 174 | function updateLabelCount(token) { 175 | get({ 176 | 'url': 'https://www.googleapis.com/gmail/v1/users/me/labels/INBOX', 177 | 'callback': updateLabelCountCallback, 178 | 'token': token, 179 | }); 180 | } 181 | 182 | /** 183 | * Got users Gmail inbox details. 184 | * 185 | * https://developers.google.com/gmail/api/v1/reference/users/labels/get 186 | * 187 | * @param {object} label - Gmail users.labels resource. 188 | */ 189 | function updateLabelCountCallback(label) { 190 | setBadgeCount(label.threadsUnread); 191 | } 192 | 193 | /** 194 | * Make an authenticated HTTP GET request. 195 | * 196 | * @param {object} options 197 | * @value {string} url - URL to make the request to. Must be whitelisted in manifest.json 198 | * @value {string} token - Google access_token to authenticate request with. 199 | * @value {function} callback - Function to receive response. 200 | */ 201 | function get(options) { 202 | var xhr = new XMLHttpRequest(); 203 | xhr.onreadystatechange = function() { 204 | if (xhr.readyState === 4 && xhr.status === 200) { 205 | // JSON response assumed. Other APIs may have different responses. 206 | options.callback(JSON.parse(xhr.responseText)); 207 | } else { 208 | console.log('get', xhr.readyState, xhr.status, xhr.responseText); 209 | } 210 | }; 211 | xhr.open("GET", options.url, true); 212 | // Set standard Google APIs authentication header. 213 | xhr.setRequestHeader('Authorization', 'Bearer ' + options.token); 214 | xhr.send(); 215 | } 216 | 217 | /** 218 | * Set browserAction status. 219 | * 220 | * @param {object} options 221 | * @value {string} text - Up to four letters to be visible on browserAction. 222 | * @value {string} color - Text background color. E.g. #FF0000 223 | * @value {string} title - The hover tooltip. 224 | */ 225 | function setBadge(options) { 226 | chrome.browserAction.setBadgeText({ 'text': options.text }); 227 | chrome.browserAction.setBadgeBackgroundColor({ 'color': options.color }); 228 | chrome.browserAction.setTitle({ 'title': options.title }); 229 | } 230 | 231 | /** 232 | * Set the browserAction status for unauthenticated user. 233 | */ 234 | function setBadgeNoAuth() { 235 | setBadge({ 236 | 'text': '?', 237 | 'color': '#9E9E9E', 238 | 'title': 'Click to authorize Gmail', 239 | }); 240 | } 241 | 242 | /** 243 | * Set the browserAction status for Gmail label count. 244 | * 245 | * @param {int} count - The count of unread emails in label. 246 | */ 247 | function setBadgeCount(count) { 248 | var color = '#9E9E9E'; 249 | var title = 'No unread mail'; 250 | if (count > 0) { 251 | color = '#F44336'; 252 | title = count + ' unread mail'; 253 | } 254 | setBadge({ 255 | 'text': count + '', // Cast count int to string. 256 | 'color': color, 257 | 'title': title, 258 | }); 259 | } 260 | 261 | /** 262 | * User clicked on browserAction button. Check if user is authenticated. 263 | * 264 | * @param {object} tab - Chrome tab resource. 265 | */ 266 | function browserActionClicked(tab) { 267 | getAuthToken({ 268 | 'interactive': false, 269 | 'callback': getBrowserActionAuthTokenCallback, 270 | }); 271 | } 272 | 273 | /** 274 | * If user is authenticated open Gmail in new tab or start auth flow. 275 | * 276 | * @param {string} token - Current users access_token. 277 | */ 278 | function getBrowserActionAuthTokenCallback(token) { 279 | if (chrome.runtime.lastError) { 280 | getAuthTokenInteractive(); 281 | } else { 282 | chrome.tabs.create({ 'url': 'https://mail.google.com' }); 283 | } 284 | 285 | } 286 | 287 | /** 288 | * Chrome alarm has triggered. 289 | * 290 | * @param {object} alarm - Chrome alarm resource. 291 | */ 292 | function onAlarm(alarm) { 293 | // Check Gmail for current unread count. 294 | if (alarm.name === 'update-count') { 295 | getAuthTokenSilent(); 296 | } 297 | } 298 | 299 | /** 300 | * Wire up Chrome event listeners. 301 | */ 302 | chrome.notifications.onClicked.addListener(notificationClicked); 303 | chrome.browserAction.onClicked.addListener(browserActionClicked); 304 | chrome.alarms.onAlarm.addListener(onAlarm); 305 | 306 | /** 307 | * Perform initial auth checks and set alarm for periodic updates. 308 | */ 309 | setBadgeNoAuth(); 310 | getAuthTokenSilent(); 311 | chrome.alarms.create('update-count', { 'delayInMinutes': 15, 'periodInMinutes': 15 }); 312 | --------------------------------------------------------------------------------