35 |
--------------------------------------------------------------------------------
/lib/oauth2_finish.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2011 Google Inc. All Rights Reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | // This script serves as an intermediary between oauth2.html and oauth2.js.
18 |
19 | // Get all query string parameters from the original URL.
20 | var url = decodeURIComponent(window.location.href.match(/&from=([^&]+)/)[1]);
21 | var index = url.indexOf('?');
22 | if (index > -1) {
23 | url = url.substring(0, index);
24 | }
25 |
26 | // Derive adapter name from URI and then finish the process.
27 | var adapterName = OAuth2.lookupAdapterName(url);
28 | var finisher = new OAuth2(adapterName, OAuth2.FINISH);
--------------------------------------------------------------------------------
/samples/mixed/options.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
18 |
19 |
20 | OAuth 2.0 Options
21 |
22 |
23 |
27 |
28 |
29 |
30 |
OAuth 2.0 Permissions
31 |
34 |
37 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/lib/oauth2_inject.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2011 Google Inc. All Rights Reserved.
3 |
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 |
18 | // This script servers as an intermediary between oauth2.js and
19 | // oauth2.html
20 |
21 | // Get all ? params from this URL
22 | var url = window.location.href;
23 | var params = '?';
24 | var index = url.indexOf(params);
25 | if (index > -1) {
26 | params = url.substring(index);
27 | }
28 |
29 | // Also append the current URL to the params
30 | params += '&from=' + encodeURIComponent(url);
31 |
32 | // Redirect back to the extension itself so that we have priveledged
33 | // access again
34 | var redirect = chrome.extension.getURL('oauth2/oauth2.html');
35 | window.location = redirect + params;
36 |
--------------------------------------------------------------------------------
/lib/adapters/sample.js:
--------------------------------------------------------------------------------
1 | OAuth2.adapter('sample', {
2 | /**
3 | * @return {URL} URL to the page that returns the authorization code
4 | */
5 | authorizationCodeURL: function(config) {
6 | return '';
7 | },
8 |
9 | /**
10 | * @return {URL} URL to the page that we use to inject the content
11 | * script into
12 | */
13 | redirectURL: function(config) {
14 | return '';
15 | },
16 |
17 | /**
18 | * @return {String} Authorization code for fetching the access token
19 | */
20 | parseAuthorizationCode: function(url) {
21 | return '';
22 | },
23 |
24 | /**
25 | * @return {URL} URL to the access token providing endpoint
26 | */
27 | accessTokenURL: function() {
28 | return '';
29 | },
30 |
31 | /**
32 | * @return {String} HTTP method to use to get access tokens
33 | */
34 | accessTokenMethod: function() {
35 | return 'POST';
36 | },
37 |
38 | /**
39 | * @return {Object} The payload to use when getting the access token
40 | */
41 | accessTokenParams: function(authorizationCode, config) {
42 | return {};
43 | },
44 |
45 | /**
46 | * @return {Object} Object containing accessToken {String},
47 | * refreshToken {String} and expiresIn {Int}
48 | */
49 | parseAccessToken: function(response) {
50 | return {
51 | accessToken: '',
52 | refreshToken: '',
53 | expiresIn: Number.MAX_VALUE
54 | };
55 | }
56 | });
57 |
--------------------------------------------------------------------------------
/lib/adapters/facebook.js:
--------------------------------------------------------------------------------
1 | OAuth2.adapter('facebook', {
2 | authorizationCodeURL: function(config) {
3 | return ('https://www.facebook.com/dialog/oauth?' +
4 | 'client_id={{CLIENT_ID}}&' +
5 | 'redirect_uri={{REDIRECT_URI}}&' +
6 | 'scope={{API_SCOPE}}')
7 | .replace('{{CLIENT_ID}}', config.clientId)
8 | .replace('{{REDIRECT_URI}}', this.redirectURL(config))
9 | .replace('{{API_SCOPE}}', config.apiScope);
10 | },
11 |
12 | redirectURL: function(config) {
13 | return 'http://www.facebook.com/robots.txt';
14 | },
15 |
16 | parseAuthorizationCode: function(url) {
17 | // TODO: error handling (URL may have
18 | // ?error=asfasfasiof&error_code=43 etc)
19 | return url.match(/[&\?]code=([^&]+)/)[1];
20 | },
21 |
22 | accessTokenURL: function() {
23 | return 'https://graph.facebook.com/oauth/access_token';
24 | },
25 |
26 | accessTokenMethod: function() {
27 | return 'GET';
28 | },
29 |
30 | accessTokenParams: function(authorizationCode, config) {
31 | return {
32 | code: authorizationCode,
33 | client_id: config.clientId,
34 | client_secret: config.clientSecret,
35 | redirect_uri: this.redirectURL(config)
36 | };
37 | },
38 |
39 | parseAccessToken: function(response) {
40 | return {
41 | accessToken: response.match(/access_token=([^&]*)/)[1],
42 | expiresIn: response.match(/expires=([^&]*)/)[1]
43 | };
44 | }
45 | });
46 |
--------------------------------------------------------------------------------
/lib/adapters/bitly.js:
--------------------------------------------------------------------------------
1 | OAuth2.adapter('bitly', {
2 |
3 | authorizationCodeURL: function(config) {
4 | return ('https://bitly.com/oauth/authorize?' +
5 | 'client_id={{CLIENT_ID}}&' +
6 | 'redirect_uri={{REDIRECT_URI}}')
7 | .replace('{{CLIENT_ID}}', config.clientId)
8 | .replace('{{REDIRECT_URI}}', this.redirectURL(config));
9 | },
10 |
11 | redirectURL: function(config) {
12 | return 'http://bitly.com/robots.txt';
13 | },
14 |
15 | parseAuthorizationCode: function(url) {
16 | // TODO: Error handling (URL may have ?error=foo_bar&error_code=43 etc).
17 | return url.match(/[&\?]code=([^&]+)/)[1];
18 | },
19 |
20 | accessTokenURL: function() {
21 | return 'https://api-ssl.bitly.com/oauth/access_token';
22 | },
23 |
24 | accessTokenMethod: function() {
25 | return 'POST';
26 | },
27 |
28 | accessTokenParams: function(authorizationCode, config) {
29 | return {
30 | code: authorizationCode,
31 | client_id: config.clientId,
32 | client_secret: config.clientSecret,
33 | redirect_uri: this.redirectURL(config),
34 | grant_type: 'authorization_code'
35 | };
36 | },
37 |
38 | parseAccessToken: function(response) {
39 | return {
40 | accessToken: response.match(/access_token=([^&]*)/)[1],
41 | apiKey: response.match(/apiKey=([^&]*)/)[1],
42 | expiresIn: Number.MAX_VALUE,
43 | login: response.match(/login=([^&]*)/)[1]
44 | };
45 | }
46 |
47 | });
--------------------------------------------------------------------------------
/lib/adapters/feedly.js:
--------------------------------------------------------------------------------
1 | OAuth2.adapter('feedly', {
2 | authorizationCodeURL: function(config) {
3 | return ('https://cloud.feedly.com/v3/auth/auth?' +
4 | 'response_type=code&' +
5 | 'client_id={{CLIENT_ID}}&' +
6 | 'redirect_uri={{REDIRECT_URI}}&' +
7 | 'scope={{API_SCOPE}}')
8 | .replace('{{CLIENT_ID}}', config.clientId)
9 | .replace('{{REDIRECT_URI}}', this.redirectURL(config))
10 | .replace('{{API_SCOPE}}', config.apiScope);
11 | },
12 |
13 | redirectURL: function(config) {
14 | return 'http://www.feedly.com/robots.txt';
15 | },
16 |
17 | parseAuthorizationCode: function(url) {
18 | return url.match(/[&\?]code=([^&]+)/)[1];
19 | },
20 |
21 | accessTokenURL: function() {
22 | return 'https://cloud.feedly.com/v3/auth/token';
23 | },
24 |
25 | accessTokenMethod: function() {
26 | return 'POST';
27 | },
28 |
29 | accessTokenParams: function(authorizationCode, config) {
30 | return {
31 | code: authorizationCode,
32 | client_id: config.clientId,
33 | client_secret: config.clientSecret,
34 | grant_type: "authorization_code",
35 | redirect_uri: this.redirectURL(config)
36 | };
37 | },
38 |
39 | parseAccessToken: function(response) {
40 | var parsedResponse = JSON.parse(response);
41 | return {
42 | userId: parsedResponse.id,
43 | accessToken: parsedResponse.access_token,
44 | refreshToken: parsedResponse.refresh_token,
45 | expiresIn: parsedResponse.expires_in
46 | };
47 | }
48 | });
49 |
--------------------------------------------------------------------------------
/lib/adapters/google.js:
--------------------------------------------------------------------------------
1 | OAuth2.adapter('google', {
2 | authorizationCodeURL: function(config) {
3 | return ('https://accounts.google.com/o/oauth2/auth?' +
4 | 'approval_prompt=force&' +
5 | 'client_id={{CLIENT_ID}}&' +
6 | 'redirect_uri={{REDIRECT_URI}}&' +
7 | 'scope={{API_SCOPE}}&' +
8 | 'access_type=offline&' +
9 | 'response_type=code')
10 | .replace('{{CLIENT_ID}}', config.clientId)
11 | .replace('{{REDIRECT_URI}}', this.redirectURL(config))
12 | .replace('{{API_SCOPE}}', config.apiScope);
13 | },
14 |
15 | redirectURL: function(config) {
16 | return 'http://www.google.com/robots.txt';
17 | },
18 |
19 | parseAuthorizationCode: function(url) {
20 | var error = url.match(/[&\?]error=([^&]+)/);
21 | if (error) {
22 | throw 'Error getting authorization code: ' + error[1];
23 | }
24 | return url.match(/[&\?]code=([\w\/\-]+)/)[1];
25 | },
26 |
27 | accessTokenURL: function() {
28 | return 'https://accounts.google.com/o/oauth2/token';
29 | },
30 |
31 | accessTokenMethod: function() {
32 | return 'POST';
33 | },
34 |
35 | accessTokenParams: function(authorizationCode, config) {
36 | return {
37 | code: authorizationCode,
38 | client_id: config.clientId,
39 | client_secret: config.clientSecret,
40 | redirect_uri: this.redirectURL(config),
41 | grant_type: 'authorization_code'
42 | };
43 | },
44 |
45 | parseAccessToken: function(response) {
46 | var parsedResponse = JSON.parse(response);
47 | return {
48 | accessToken: parsedResponse.access_token,
49 | refreshToken: parsedResponse.refresh_token,
50 | expiresIn: parsedResponse.expires_in
51 | };
52 | }
53 | });
54 |
--------------------------------------------------------------------------------
/samples/fbmusic/popup.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2011 Google Inc. All Rights Reserved.
3 |
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | var facebook = new OAuth2('facebook', {
18 | client_id: '177955888930840',
19 | client_secret: 'b42a5741bd3d6de6ac591c7b0e279c9f',
20 | api_scope: 'read_stream,user_likes'
21 | });
22 | facebook.authorize(function() {
23 |
24 | //document.addEventListener('DOMContentLoaded', function() {
25 |
26 | // Make an XHR that creates the task
27 | var xhr = new XMLHttpRequest();
28 | xhr.onreadystatechange = function(event) {
29 | if (xhr.readyState == 4) {
30 | if(xhr.status == 200) {
31 | // Great success: parse response with JSON
32 | var parsed = JSON.parse(xhr.responseText);
33 | var html = '';
34 | parsed.data.forEach(function(item, index) {
35 | html += '
' + item.name + '
';
36 | });
37 | document.querySelector('#music').innerHTML = html;
38 | return;
39 |
40 | } else {
41 | // Request failure: something bad happened
42 | }
43 | }
44 | };
45 |
46 | xhr.open('GET', 'https://graph.facebook.com/me/music', true);
47 | xhr.setRequestHeader('Content-Type', 'application/json');
48 | xhr.setRequestHeader('Authorization', 'OAuth ' + facebook.getAccessToken());
49 |
50 | xhr.send();
51 |
52 | });
53 |
--------------------------------------------------------------------------------
/samples/mixed/options.js:
--------------------------------------------------------------------------------
1 | var google = new OAuth2('google', {
2 | client_id: '952993494713-h12m6utvq8g8d8et8n2i68plbrr6cr4d.apps.googleusercontent.com',
3 | client_secret: 'IZ4hBSbosuhoWAX4lyAomm-R',
4 | api_scope: 'https://www.googleapis.com/auth/tasks'
5 | });
6 |
7 | var facebook = new OAuth2('facebook', {
8 | client_id: '177955888930840',
9 | client_secret: 'b42a5741bd3d6de6ac591c7b0e279c9f',
10 | api_scope: 'read_stream,user_likes'
11 | });
12 |
13 | var github = new OAuth2('github', {
14 | client_id: '09450dfdc3ae76768b08',
15 | client_secret: '8ecfc23e0dba1ce1a295fbabc01fa71db4b80261',
16 | });
17 |
18 | function authorize(providerName) {
19 | var provider = window[providerName];
20 | provider.authorize(checkAuthorized);
21 | }
22 |
23 | function clearAuthorized() {
24 | console.log('clear');
25 | ['google', 'facebook', 'github'].forEach(function(providerName) {
26 | var provider = window[providerName];
27 | provider.clearAccessToken();
28 | });
29 | checkAuthorized();
30 | }
31 |
32 | function checkAuthorized() {
33 | console.log('checkAuthorized');
34 | ['google', 'facebook', 'github'].forEach(function(providerName) {
35 | var provider = window[providerName];
36 | var button = document.querySelector('#' + providerName);
37 | if (provider.hasAccessToken()) {
38 | button.classList.add('authorized');
39 | } else {
40 | button.classList.remove('authorized');
41 | }
42 | });
43 | }
44 |
45 | document.addEventListener('DOMContentLoaded', function () {
46 | document.querySelector('button#google').addEventListener('click', function() { authorize('google'); });
47 | document.querySelector('button#github').addEventListener('click', function() { authorize('github'); });
48 | document.querySelector('button#facebook').addEventListener('click', function() { authorize('facebook'); });
49 | document.querySelector('button#clear').addEventListener('click', function() { clearAuthorized() });
50 |
51 | checkAuthorized();
52 | });
53 |
54 |
--------------------------------------------------------------------------------
/lib/adapters/github.js:
--------------------------------------------------------------------------------
1 | OAuth2.adapter('github', {
2 | /**
3 | * @return {URL} URL to the page that returns the authorization code
4 | */
5 | authorizationCodeURL: function(config) {
6 | return ('https://github.com/login/oauth/authorize?' +
7 | 'client_id={{CLIENT_ID}}&' +
8 | 'scope={{API_SCOPE}}&' +
9 | 'redirect_uri={{REDIRECT_URI}}')
10 | .replace('{{CLIENT_ID}}', config.clientId)
11 | .replace('{{API_SCOPE}}', config.apiScope)
12 | .replace('{{REDIRECT_URI}}', this.redirectURL(config));
13 | },
14 |
15 | /**
16 | * @return {URL} URL to the page that we use to inject the content
17 | * script into
18 | */
19 | redirectURL: function(config) {
20 | return 'https://github.com/robots.txt';
21 | },
22 |
23 | /**
24 | * @return {String} Authorization code for fetching the access token
25 | */
26 | parseAuthorizationCode: function(url) {
27 | var error = url.match(/[&\?]error=([^&]+)/);
28 | if (error) {
29 | throw 'Error getting authorization code: ' + error[1];
30 | }
31 | return url.match(/[&\?]code=([\w\/\-]+)/)[1];
32 | },
33 |
34 | /**
35 | * @return {URL} URL to the access token providing endpoint
36 | */
37 | accessTokenURL: function() {
38 | return 'https://github.com/login/oauth/access_token';
39 | },
40 |
41 | /**
42 | * @return {String} HTTP method to use to get access tokens
43 | */
44 | accessTokenMethod: function() {
45 | return 'POST';
46 | },
47 |
48 | /**
49 | * @return {Object} The payload to use when getting the access token
50 | */
51 | accessTokenParams: function(authorizationCode, config) {
52 | return {
53 | code: authorizationCode,
54 | client_id: config.clientId,
55 | client_secret: config.clientSecret,
56 | redirect_uri: this.redirectURL(config),
57 | grant_type: 'authorization_code'
58 | };
59 | },
60 |
61 | /**
62 | * @return {Object} Object containing accessToken {String},
63 | * refreshToken {String} and expiresIn {Int}
64 | */
65 | parseAccessToken: function(response) {
66 | return {
67 | accessToken: response.match(/access_token=([^&]*)/)[1],
68 | expiresIn: Number.MAX_VALUE
69 | };
70 | }
71 | });
72 |
--------------------------------------------------------------------------------
/samples/tasks/popup.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2011 Google Inc. All Rights Reserved.
3 |
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | var google = new OAuth2('google', {
18 | client_id: '952993494713-h12m6utvq8g8d8et8n2i68plbrr6cr4d.apps.googleusercontent.com',
19 | client_secret: 'IZ4hBSbosuhoWAX4lyAomm-R',
20 | api_scope: 'https://www.googleapis.com/auth/tasks'
21 | });
22 |
23 | google.authorize(function() {
24 |
25 | var TASK_CREATE_URL = 'https://www.googleapis.com/tasks/v1/lists/@default/tasks';
26 |
27 | var form = document.getElementById('form');
28 | var success = document.getElementById('success');
29 |
30 | // Hook up the form to create a new task with Google Tasks
31 | form.addEventListener('submit', function(event) {
32 | event.preventDefault();
33 | var input = document.getElementById('input');
34 | createTodo(input.value);
35 | });
36 |
37 | function createTodo(task) {
38 | // Make an XHR that creates the task
39 | var xhr = new XMLHttpRequest();
40 | xhr.onreadystatechange = function(event) {
41 | if (xhr.readyState == 4) {
42 | if(xhr.status == 200) {
43 | // Great success: parse response with JSON
44 | var task = JSON.parse(xhr.responseText);
45 | document.getElementById('taskid').innerHTML = task.id;
46 | form.style.display = 'none';
47 | success.style.display = 'block';
48 |
49 | } else {
50 | // Request failure: something bad happened
51 | }
52 | }
53 | };
54 |
55 | var message = JSON.stringify({
56 | title: task
57 | });
58 |
59 | xhr.open('POST', TASK_CREATE_URL, true);
60 |
61 | xhr.setRequestHeader('Content-Type', 'application/json');
62 | xhr.setRequestHeader('Authorization', 'OAuth ' + google.getAccessToken());
63 |
64 | xhr.send(message);
65 | }
66 |
67 | });
68 |
69 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Deprecation note
2 |
3 | The [Chrome Identity API][identity] makes this drop-in library obsolete.
4 |
5 | [identity]: http://developer.chrome.com/apps/app_identity.html
6 |
7 | # Original docs
8 |
9 | This is the OAuth 2.0 library for Chrome Extensions. It's available on
10 | both [github][] and on [google code][].
11 |
12 | [Sample extensions][samples] that use this library can be found in this same
13 | distribution, but please note that you will need to run the
14 | `cp-oauth2.sh` script inside the `samples` directory to get these
15 | samples to work.
16 |
17 | # Thanks, contributors!
18 |
19 | Many thanks to [neocotic@](https://github.com/neocotic) and other
20 | contributors for their great work in keeping this library up-to-date.
21 |
22 | # How to use this library
23 |
24 | Register your application with an OAuth 2.0 endpoint that you'd like to
25 | use. If it's a Google API you're calling, go to the [Google APIs][gapi]
26 | page, create your application and note your client ID and client secret.
27 | For more info on this, check out the [Google OAuth 2.0][goauth2] docs.
28 | When you setup your application, you will be asked to provide redirect
29 | URI(s). Please provide the URI that corresponds to the service you're
30 | using.
31 |
32 | Here's a table that will come in handy:
33 |
34 |
35 |
36 |
Adapter
37 |
Redirect URI
38 |
Access Token URI
39 |
40 |
41 |
google
42 |
http://www.google.com/robots.txt
43 |
https://accounts.google.com/o/oauth2/token
44 |
45 |
46 |
facebook
47 |
http://www.facebook.com/robots.txt
48 |
https://graph.facebook.com/oauth/access_token
49 |
50 |
51 |
github
52 |
https://github.com/robots.txt
53 |
https://github.com/login/oauth/access_token
54 |
55 |
56 |
bitly
57 |
http://bitly.com/robots.txt
58 |
https://api-ssl.bitly.com/oauth/access_token
59 |
60 |
61 |
feedly
62 |
http://www.feedly.com/robots.txt
63 |
https://cloud.feedly.com/v3/auth/token
64 |
65 |
66 |
67 | #### Step 1: Copy library
68 |
69 | You will need to copy the [oauth2 library][oauth2crx] into your chrome
70 | extension root into a directory called `oauth2`.
71 |
72 | #### Step 2: Inject content script
73 |
74 | Then you need to modify your manifest.json file to include a content
75 | script at the redirect URL used by the Google adapter. The "matches"
76 | redirect URI can be looked up in the table above:
77 |
78 | "content_scripts": [
79 | {
80 | "matches": ["http://www.google.com/robots.txt*"],
81 | "js": ["oauth2/oauth2_inject.js"],
82 | "run_at": "document_start"
83 | }
84 | ],
85 |
86 | #### Step 3: Allow access token URL
87 |
88 | Also, you will need to add a permission to Google's access token
89 | granting URL, since the library will do an XHR against it. The access
90 | token URI can be looked up in the table above as well.
91 |
92 | "permissions": [
93 | "https://accounts.google.com/o/oauth2/token"
94 | ],
95 | "web_accessible_resources": [
96 | "oauth2/oauth2.html"
97 | ],
98 |
99 | #### Step 4: Include the OAuth 2.0 library
100 |
101 | Next, in your extension's code, you should include the OAuth 2.0
102 | library:
103 |
104 |
105 |
106 | #### Step 5: Configure the OAuth 2.0 endpoint
107 |
108 | And configure your OAuth 2 connection by providing clientId,
109 | clientSecret and apiScopes from the registration page. The authorize()
110 | method may create a new popup window for the user to grant your
111 | extension access to the OAuth2 endpoint.
112 |
113 | var googleAuth = new OAuth2('google', {
114 | client_id: '17755888930840',
115 | client_secret: 'b4a5741bd3d6de6ac591c7b0e279c9f',
116 | api_scope: 'https://www.googleapis.com/auth/tasks'
117 | });
118 |
119 | googleAuth.authorize(function() {
120 | // Ready for action, can now make requests with
121 | googleAuth.getAccessToken()
122 | });
123 |
124 | #### Step 6: Use the access token
125 |
126 | Now that your user has an access token via `auth.getAccessToken()`, you
127 | can request protected data by adding the accessToken as a request header
128 |
129 | xhr.setRequestHeader('Authorization', 'OAuth ' + myAuth.getAccessToken())
130 |
131 | or by passing it as part of the URL (depending on your particular impl):
132 |
133 | myUrl + '?oauth_token=' + myAuth.getAccessToken();
134 |
135 | **Note**: if you have multiple OAuth 2.0 endpoints that you would like
136 | to authorize with, you can do that too! Just inject content scripts and
137 | add permissions for all of the providers you would like to authorize
138 | with.
139 |
140 | For more information about this library, please see this [blog
141 | post][blog].
142 |
143 |
144 | [gapi]: https://code.google.com/apis/console/
145 | [goauth2]: http://code.google.com/apis/accounts/docs/OAuth2.html
146 | [oauth crx]: http://code.google.com/chrome/extensions/tut_oauth.html
147 | [oauth2crx]: https://github.com/borismus/oauth2-extensions/tree/master/lib
148 |
149 | [github]: https://github.com/borismus/oauth2-extensions/
150 | [google code]: http://code.google.com/p/oauth2-extensions/
151 | [samples]: https://github.com/borismus/oauth2-extensions/samples
152 | [blog]: http://smus.com/oauth2-chrome-extensions
153 |
--------------------------------------------------------------------------------
/COPYING:
--------------------------------------------------------------------------------
1 |
2 | Apache License
3 | Version 2.0, January 2004
4 | http://www.apache.org/licenses/
5 |
6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
7 |
8 | 1. Definitions.
9 |
10 | "License" shall mean the terms and conditions for use, reproduction,
11 | and distribution as defined by Sections 1 through 9 of this document.
12 |
13 | "Licensor" shall mean the copyright owner or entity authorized by
14 | the copyright owner that is granting the License.
15 |
16 | "Legal Entity" shall mean the union of the acting entity and all
17 | other entities that control, are controlled by, or are under common
18 | control with that entity. For the purposes of this definition,
19 | "control" means (i) the power, direct or indirect, to cause the
20 | direction or management of such entity, whether by contract or
21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
22 | outstanding shares, or (iii) beneficial ownership of such entity.
23 |
24 | "You" (or "Your") shall mean an individual or Legal Entity
25 | exercising permissions granted by this License.
26 |
27 | "Source" form shall mean the preferred form for making modifications,
28 | including but not limited to software source code, documentation
29 | source, and configuration files.
30 |
31 | "Object" form shall mean any form resulting from mechanical
32 | transformation or translation of a Source form, including but
33 | not limited to compiled object code, generated documentation,
34 | and conversions to other media types.
35 |
36 | "Work" shall mean the work of authorship, whether in Source or
37 | Object form, made available under the License, as indicated by a
38 | copyright notice that is included in or attached to the work
39 | (an example is provided in the Appendix below).
40 |
41 | "Derivative Works" shall mean any work, whether in Source or Object
42 | form, that is based on (or derived from) the Work and for which the
43 | editorial revisions, annotations, elaborations, or other modifications
44 | represent, as a whole, an original work of authorship. For the purposes
45 | of this License, Derivative Works shall not include works that remain
46 | separable from, or merely link (or bind by name) to the interfaces of,
47 | the Work and Derivative Works thereof.
48 |
49 | "Contribution" shall mean any work of authorship, including
50 | the original version of the Work and any modifications or additions
51 | to that Work or Derivative Works thereof, that is intentionally
52 | submitted to Licensor for inclusion in the Work by the copyright owner
53 | or by an individual or Legal Entity authorized to submit on behalf of
54 | the copyright owner. For the purposes of this definition, "submitted"
55 | means any form of electronic, verbal, or written communication sent
56 | to the Licensor or its representatives, including but not limited to
57 | communication on electronic mailing lists, source code control systems,
58 | and issue tracking systems that are managed by, or on behalf of, the
59 | Licensor for the purpose of discussing and improving the Work, but
60 | excluding communication that is conspicuously marked or otherwise
61 | designated in writing by the copyright owner as "Not a Contribution."
62 |
63 | "Contributor" shall mean Licensor and any individual or Legal Entity
64 | on behalf of whom a Contribution has been received by Licensor and
65 | subsequently incorporated within the Work.
66 |
67 | 2. Grant of Copyright License. Subject to the terms and conditions of
68 | this License, each Contributor hereby grants to You a perpetual,
69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
70 | copyright license to reproduce, prepare Derivative Works of,
71 | publicly display, publicly perform, sublicense, and distribute the
72 | Work and such Derivative Works in Source or Object form.
73 |
74 | 3. Grant of Patent License. Subject to the terms and conditions of
75 | this License, each Contributor hereby grants to You a perpetual,
76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
77 | (except as stated in this section) patent license to make, have made,
78 | use, offer to sell, sell, import, and otherwise transfer the Work,
79 | where such license applies only to those patent claims licensable
80 | by such Contributor that are necessarily infringed by their
81 | Contribution(s) alone or by combination of their Contribution(s)
82 | with the Work to which such Contribution(s) was submitted. If You
83 | institute patent litigation against any entity (including a
84 | cross-claim or counterclaim in a lawsuit) alleging that the Work
85 | or a Contribution incorporated within the Work constitutes direct
86 | or contributory patent infringement, then any patent licenses
87 | granted to You under this License for that Work shall terminate
88 | as of the date such litigation is filed.
89 |
90 | 4. Redistribution. You may reproduce and distribute copies of the
91 | Work or Derivative Works thereof in any medium, with or without
92 | modifications, and in Source or Object form, provided that You
93 | meet the following conditions:
94 |
95 | (a) You must give any other recipients of the Work or
96 | Derivative Works a copy of this License; and
97 |
98 | (b) You must cause any modified files to carry prominent notices
99 | stating that You changed the files; and
100 |
101 | (c) You must retain, in the Source form of any Derivative Works
102 | that You distribute, all copyright, patent, trademark, and
103 | attribution notices from the Source form of the Work,
104 | excluding those notices that do not pertain to any part of
105 | the Derivative Works; and
106 |
107 | (d) If the Work includes a "NOTICE" text file as part of its
108 | distribution, then any Derivative Works that You distribute must
109 | include a readable copy of the attribution notices contained
110 | within such NOTICE file, excluding those notices that do not
111 | pertain to any part of the Derivative Works, in at least one
112 | of the following places: within a NOTICE text file distributed
113 | as part of the Derivative Works; within the Source form or
114 | documentation, if provided along with the Derivative Works; or,
115 | within a display generated by the Derivative Works, if and
116 | wherever such third-party notices normally appear. The contents
117 | of the NOTICE file are for informational purposes only and
118 | do not modify the License. You may add Your own attribution
119 | notices within Derivative Works that You distribute, alongside
120 | or as an addendum to the NOTICE text from the Work, provided
121 | that such additional attribution notices cannot be construed
122 | as modifying the License.
123 |
124 | You may add Your own copyright statement to Your modifications and
125 | may provide additional or different license terms and conditions
126 | for use, reproduction, or distribution of Your modifications, or
127 | for any such Derivative Works as a whole, provided Your use,
128 | reproduction, and distribution of the Work otherwise complies with
129 | the conditions stated in this License.
130 |
131 | 5. Submission of Contributions. Unless You explicitly state otherwise,
132 | any Contribution intentionally submitted for inclusion in the Work
133 | by You to the Licensor shall be under the terms and conditions of
134 | this License, without any additional terms or conditions.
135 | Notwithstanding the above, nothing herein shall supersede or modify
136 | the terms of any separate license agreement you may have executed
137 | with Licensor regarding such Contributions.
138 |
139 | 6. Trademarks. This License does not grant permission to use the trade
140 | names, trademarks, service marks, or product names of the Licensor,
141 | except as required for reasonable and customary use in describing the
142 | origin of the Work and reproducing the content of the NOTICE file.
143 |
144 | 7. Disclaimer of Warranty. Unless required by applicable law or
145 | agreed to in writing, Licensor provides the Work (and each
146 | Contributor provides its Contributions) on an "AS IS" BASIS,
147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
148 | implied, including, without limitation, any warranties or conditions
149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
150 | PARTICULAR PURPOSE. You are solely responsible for determining the
151 | appropriateness of using or redistributing the Work and assume any
152 | risks associated with Your exercise of permissions under this License.
153 |
154 | 8. Limitation of Liability. In no event and under no legal theory,
155 | whether in tort (including negligence), contract, or otherwise,
156 | unless required by applicable law (such as deliberate and grossly
157 | negligent acts) or agreed to in writing, shall any Contributor be
158 | liable to You for damages, including any direct, indirect, special,
159 | incidental, or consequential damages of any character arising as a
160 | result of this License or out of the use or inability to use the
161 | Work (including but not limited to damages for loss of goodwill,
162 | work stoppage, computer failure or malfunction, or any and all
163 | other commercial damages or losses), even if such Contributor
164 | has been advised of the possibility of such damages.
165 |
166 | 9. Accepting Warranty or Additional Liability. While redistributing
167 | the Work or Derivative Works thereof, You may choose to offer,
168 | and charge a fee for, acceptance of support, warranty, indemnity,
169 | or other liability obligations and/or rights consistent with this
170 | License. However, in accepting such obligations, You may act only
171 | on Your own behalf and on Your sole responsibility, not on behalf
172 | of any other Contributor, and only if You agree to indemnify,
173 | defend, and hold each Contributor harmless for any liability
174 | incurred by, or claims asserted against, such Contributor by reason
175 | of your accepting any such warranty or additional liability.
176 |
177 | END OF TERMS AND CONDITIONS
178 |
179 | APPENDIX: How to apply the Apache License to your work.
180 |
181 | To apply the Apache License to your work, attach the following
182 | boilerplate notice, with the fields enclosed by brackets "[]"
183 | replaced with your own identifying information. (Don't include
184 | the brackets!) The text should be enclosed in the appropriate
185 | comment syntax for the file format. We also recommend that a
186 | file or class name and description of purpose be included on the
187 | same "printed page" as the copyright notice for easier
188 | identification within third-party archives.
189 |
190 | Copyright 2011 Google Inc
191 |
192 | Licensed under the Apache License, Version 2.0 (the "License");
193 | you may not use this file except in compliance with the License.
194 | You may obtain a copy of the License at
195 |
196 | http://www.apache.org/licenses/LICENSE-2.0
197 |
198 | Unless required by applicable law or agreed to in writing, software
199 | distributed under the License is distributed on an "AS IS" BASIS,
200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
201 | See the License for the specific language governing permissions and
202 | limitations under the License.
203 |
--------------------------------------------------------------------------------
/lib/oauth2.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2011 Google Inc. All Rights Reserved.
3 |
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 |
18 | /**
19 | * Constructor
20 | *
21 | * @param {String} adapterName name of the adapter to use for this OAuth 2
22 | * @param {Object} config Containing clientId, clientSecret and apiScope
23 | * @param {String} config Alternatively, OAuth2.FINISH for the finish flow
24 | */
25 | var OAuth2 = function(adapterName, config) {
26 | this.adapterName = adapterName;
27 | var that = this;
28 | OAuth2.loadAdapter(adapterName, function() {
29 | that.adapter = OAuth2.adapters[adapterName];
30 | if (config == OAuth2.FINISH) {
31 | that.finishAuth();
32 | } else if (config) {
33 | that.updateLocalStorage();
34 |
35 | var data = that.get();
36 | data.clientId = config.client_id;
37 | data.clientSecret = config.client_secret;
38 | data.apiScope = config.api_scope;
39 | that.setSource(data);
40 | }
41 | });
42 | };
43 |
44 | /**
45 | * Pass instead of config to specify the finishing OAuth flow.
46 | */
47 | OAuth2.FINISH = 'finish';
48 |
49 | /**
50 | * OAuth 2.0 endpoint adapters known to the library
51 | */
52 | OAuth2.adapters = {};
53 | OAuth2.adapterReverse = localStorage.oauth2_adapterReverse &&
54 | JSON.parse(localStorage.oauth2_adapterReverse) || {};
55 | // Update the persisted adapterReverse in localStorage.
56 | if (localStorage.adapterReverse) {
57 | OAuth2.adapterReverse = JSON.parse(localStorage.adapterReverse);
58 | delete localStorage.adapterReverse;
59 | }
60 |
61 | /**
62 | * Consolidates the data stored in localStorage on the current adapter in to
63 | * a single JSON object.
64 | * The update should only ever happen once per adapter and will delete the old
65 | * obsolete entries in localStorage after copying their values.
66 | */
67 | OAuth2.prototype.updateLocalStorage = function() {
68 | // Check if update is even required.
69 | if (this.getSource()) {
70 | return;
71 | }
72 | var data = {};
73 | var variables = [
74 | 'accessToken', 'accessTokenDate', 'apiScope', 'clientId', 'clientSecret',
75 | 'expiresIn', 'refreshToken'
76 | ];
77 | // Check if a variable has already been persisted and then copy them.
78 | var key;
79 | for (var i = 0; i < variables.length; i++) {
80 | key = this.adapterName + '_' + variables[i];
81 | if (localStorage.hasOwnProperty(key)) {
82 | data[variables[i]] = localStorage[key];
83 | delete localStorage[key];
84 | }
85 | }
86 | // Persist the new JSON object in localStorage.
87 | this.setSource(data);
88 | };
89 |
90 | /**
91 | * Opens up an authorization popup window. This starts the OAuth 2.0 flow.
92 | *
93 | * @param {Function} callback Method to call when the user finished auth.
94 | */
95 | OAuth2.prototype.openAuthorizationCodePopup = function(callback) {
96 | // Store a reference to the callback so that the newly opened window can call
97 | // it later.
98 | window['oauth-callback'] = callback;
99 |
100 | // Create a new tab with the OAuth 2.0 prompt
101 | chrome.tabs.create({url: this.adapter.authorizationCodeURL(this.getConfig())},
102 | function(tab) {
103 | // 1. user grants permission for the application to access the OAuth 2.0
104 | // endpoint
105 | // 2. the endpoint redirects to the redirect URL.
106 | // 3. the extension injects a script into that redirect URL
107 | // 4. the injected script redirects back to oauth2.html, also passing
108 | // the redirect URL
109 | // 5. oauth2.html uses redirect URL to know what OAuth 2.0 flow to finish
110 | // (if there are multiple OAuth 2.0 adapters)
111 | // 6. Finally, the flow is finished and client code can call
112 | // myAuth.getAccessToken() to get a valid access token.
113 | });
114 | };
115 |
116 | /**
117 | * Gets access and refresh (if provided by endpoint) tokens
118 | *
119 | * @param {String} authorizationCode Retrieved from the first step in the process
120 | * @param {Function} callback Called back with 3 params:
121 | * access token, refresh token and expiry time
122 | */
123 | OAuth2.prototype.getAccessAndRefreshTokens = function(authorizationCode, callback) {
124 | var that = this;
125 | // Make an XHR to get the token
126 | var xhr = new XMLHttpRequest();
127 | xhr.addEventListener('readystatechange', function(event) {
128 | if (xhr.readyState == 4) {
129 | if (xhr.status == 200) {
130 | // Callback with the data (incl. tokens).
131 | callback(that.adapter.parseAccessToken(xhr.responseText));
132 | }
133 | }
134 | });
135 |
136 | var method = that.adapter.accessTokenMethod();
137 | var items = that.adapter.accessTokenParams(authorizationCode, that.getConfig());
138 | var key = null;
139 | if (method == 'POST') {
140 | var formData = new FormData();
141 | for (key in items) {
142 | formData.append(key, items[key]);
143 | }
144 | xhr.open(method, that.adapter.accessTokenURL(), true);
145 | xhr.send(formData);
146 | } else if (method == 'GET') {
147 | var url = that.adapter.accessTokenURL();
148 | var params = '?';
149 | for (key in items) {
150 | params += encodeURIComponent(key) + '=' +
151 | encodeURIComponent(items[key]) + '&';
152 | }
153 | xhr.open(method, url + params, true);
154 | xhr.send();
155 | } else {
156 | throw method + ' is an unknown method';
157 | }
158 | };
159 |
160 | /**
161 | * Refreshes the access token using the currently stored refresh token
162 | * Note: this only happens for the Google adapter since all other OAuth 2.0
163 | * endpoints don't implement refresh tokens.
164 | *
165 | * @param {String} refreshToken A valid refresh token
166 | * @param {Function} callback On success, called with access token and expiry time and refresh token
167 | */
168 | OAuth2.prototype.refreshAccessToken = function(refreshToken, callback) {
169 | var xhr = new XMLHttpRequest();
170 | xhr.onreadystatechange = function(event) {
171 | if (xhr.readyState == 4) {
172 | if(xhr.status == 200) {
173 | console.log(xhr.responseText);
174 | // Parse response with JSON
175 | var obj = JSON.parse(xhr.responseText);
176 | // Callback with the tokens
177 | callback(obj.access_token, obj.expires_in, obj.refresh_token);
178 | }
179 | }
180 | };
181 |
182 | var data = this.get();
183 | var formData = new FormData();
184 | formData.append('Content-Type', 'application/x-www-form-urlencoded; charset=utf-8')
185 | formData.append('client_id', data.clientId);
186 | formData.append('client_secret', data.clientSecret);
187 | formData.append('refresh_token', refreshToken);
188 | formData.append('grant_type', 'refresh_token');
189 | xhr.open('POST', this.adapter.accessTokenURL(), true);
190 | xhr.send(formData);
191 | };
192 |
193 | /**
194 | * Extracts authorizationCode from the URL and makes a request to the last
195 | * leg of the OAuth 2.0 process.
196 | */
197 | OAuth2.prototype.finishAuth = function() {
198 | var authorizationCode = null;
199 | var that = this;
200 |
201 | // Loop through existing extension views and excute any stored callbacks.
202 | function callback(error) {
203 | var views = chrome.extension.getViews();
204 | for (var i = 0, view; view = views[i]; i++) {
205 | if (view['oauth-callback']) {
206 | view['oauth-callback'](error);
207 | // TODO: Decide whether it's worth it to scope the callback or not.
208 | // Currently, every provider will share the same callback address but
209 | // that's not such a big deal assuming that they check to see whether
210 | // the token exists instead of blindly trusting that it does.
211 | }
212 | }
213 |
214 | // Once we get here, close the current tab and we're good to go.
215 | // The following works around bug: crbug.com/84201
216 | window.open('', '_self', '');
217 | window.close();
218 | }
219 |
220 | try {
221 | authorizationCode = that.adapter.parseAuthorizationCode(window.location.href);
222 | console.log(authorizationCode);
223 | } catch (e) {
224 | console.error(e);
225 | callback(e);
226 | }
227 |
228 | that.getAccessAndRefreshTokens(authorizationCode, function(response) {
229 | var data = that.get();
230 | data.accessTokenDate = new Date().valueOf();
231 |
232 | // Set all data returned by the OAuth 2.0 provider.
233 | for (var name in response) {
234 | if (response.hasOwnProperty(name) && response[name]) {
235 | data[name] = response[name];
236 | }
237 | }
238 |
239 | that.setSource(data);
240 | callback();
241 | });
242 | };
243 |
244 | /**
245 | * @return True iff the current access token has expired
246 | */
247 | OAuth2.prototype.isAccessTokenExpired = function() {
248 | var data = this.get();
249 | return (new Date().valueOf() - data.accessTokenDate) > data.expiresIn * 1000;
250 | };
251 |
252 | /**
253 | * Get the persisted adapter data in localStorage. Optionally, provide a
254 | * property name to only retrieve its value.
255 | *
256 | * @param {String} [name] The name of the property to be retrieved.
257 | * @return The data object or property value if name was specified.
258 | */
259 | OAuth2.prototype.get = function(name) {
260 | var src = this.getSource();
261 | var obj = src ? JSON.parse(src) : {};
262 | return name ? obj[name] : obj;
263 | };
264 |
265 | /**
266 | * Set the value of a named property on the persisted adapter data in
267 | * localStorage.
268 | *
269 | * @param {String} name The name of the property to change.
270 | * @param value The value to be set.
271 | */
272 | OAuth2.prototype.set = function(name, value) {
273 | var obj = this.get();
274 | obj[name] = value;
275 | this.setSource(obj);
276 | };
277 |
278 | /**
279 | * Clear all persisted adapter data in localStorage. Optionally, provide a
280 | * property name to only clear its value.
281 | *
282 | * @param {String} [name] The name of the property to clear.
283 | */
284 | OAuth2.prototype.clear = function(name) {
285 | if (name) {
286 | var obj = this.get();
287 | delete obj[name];
288 | this.setSource(obj);
289 | } else {
290 | delete localStorage['oauth2_' + this.adapterName];
291 | }
292 | };
293 |
294 | /**
295 | * Get the JSON string for the object stored in localStorage.
296 | *
297 | * @return {String} The source JSON string.
298 | */
299 | OAuth2.prototype.getSource = function() {
300 | return localStorage['oauth2_' + this.adapterName];
301 | };
302 |
303 | /**
304 | * Set the JSON string for the object stored in localStorage.
305 | *
306 | * @param {Object|String} source The new JSON string/object to be set.
307 | */
308 | OAuth2.prototype.setSource = function(source) {
309 | if (!source) {
310 | return;
311 | }
312 | if (typeof source !== 'string') {
313 | source = JSON.stringify(source);
314 | }
315 | localStorage['oauth2_' + this.adapterName] = source;
316 | };
317 |
318 | /**
319 | * Get the configuration parameters to be passed to the adapter.
320 | *
321 | * @returns {Object} Contains clientId, clientSecret and apiScope.
322 | */
323 | OAuth2.prototype.getConfig = function() {
324 | var data = this.get();
325 | return {
326 | clientId: data.clientId,
327 | clientSecret: data.clientSecret,
328 | apiScope: data.apiScope
329 | };
330 | };
331 |
332 | /***********************************
333 | *
334 | * STATIC ADAPTER RELATED METHODS
335 | *
336 | ***********************************/
337 |
338 | /**
339 | * Loads an OAuth 2.0 adapter and calls back when it's loaded
340 | *
341 | * @param adapterName {String} The name of the JS file
342 | * @param callback {Function} Called as soon as the adapter has been loaded
343 | */
344 | OAuth2.loadAdapter = function(adapterName, callback) {
345 | // If it's already loaded, don't load it again
346 | if (OAuth2.adapters[adapterName]) {
347 | callback();
348 | return;
349 | }
350 | var head = document.querySelector('head');
351 | var script = document.createElement('script');
352 | script.type = 'text/javascript';
353 | script.src = '/oauth2/adapters/' + adapterName + '.js';
354 | script.addEventListener('load', function() {
355 | callback();
356 | });
357 | head.appendChild(script);
358 | };
359 |
360 | /**
361 | * Registers an adapter with the library. This call is used by each adapter
362 | *
363 | * @param {String} name The adapter name
364 | * @param {Object} impl The adapter implementation
365 | *
366 | * @throws {String} If the specified adapter is invalid
367 | */
368 | OAuth2.adapter = function(name, impl) {
369 | var implementing = 'authorizationCodeURL redirectURL accessTokenURL ' +
370 | 'accessTokenMethod accessTokenParams accessToken';
371 |
372 | // Check for missing methods
373 | implementing.split(' ').forEach(function(method, index) {
374 | if (!method in impl) {
375 | throw 'Invalid adapter! Missing method: ' + method;
376 | }
377 | });
378 |
379 | // Save the adapter in the adapter registry
380 | OAuth2.adapters[name] = impl;
381 | // Make an entry in the adapter lookup table
382 | OAuth2.adapterReverse[impl.redirectURL()] = name;
383 | // Store the the adapter lookup table in localStorage
384 | localStorage.oauth2_adapterReverse = JSON.stringify(OAuth2.adapterReverse);
385 | };
386 |
387 | /**
388 | * Looks up the adapter name based on the redirect URL. Used by oauth2.html
389 | * in the second part of the OAuth 2.0 flow.
390 | *
391 | * @param {String} url The url that called oauth2.html
392 | * @return The adapter for the current page
393 | */
394 | OAuth2.lookupAdapterName = function(url) {
395 | var adapterReverse = JSON.parse(localStorage.oauth2_adapterReverse);
396 | return adapterReverse[url];
397 | };
398 |
399 | /***********************************
400 | *
401 | * PUBLIC API
402 | *
403 | ***********************************/
404 |
405 | /**
406 | * Authorizes the OAuth authenticator instance.
407 | *
408 | * @param {Function} callback Tries to callback when auth is successful
409 | * Note: does not callback if grant popup required
410 | */
411 | OAuth2.prototype.authorize = function(callback) {
412 | var that = this;
413 | OAuth2.loadAdapter(that.adapterName, function() {
414 | that.adapter = OAuth2.adapters[that.adapterName];
415 | var data = that.get();
416 | if (!data.accessToken) {
417 | // There's no access token yet. Start the authorizationCode flow
418 | that.openAuthorizationCodePopup(callback);
419 | } else if (that.isAccessTokenExpired()) {
420 | // There's an existing access token but it's expired
421 | if (data.refreshToken) {
422 | that.refreshAccessToken(data.refreshToken, function(at, exp, re) {
423 | var newData = that.get();
424 | newData.accessTokenDate = new Date().valueOf();
425 | newData.accessToken = at;
426 | newData.expiresIn = exp;
427 | newData.refreshToken = re;
428 | that.setSource(newData);
429 | // Callback when we finish refreshing
430 | if (callback) {
431 | callback();
432 | }
433 | });
434 | } else {
435 | // No refresh token... just do the popup thing again
436 | that.openAuthorizationCodePopup(callback);
437 | }
438 | } else {
439 | // We have an access token, and it's not expired yet
440 | if (callback) {
441 | callback();
442 | }
443 | }
444 | });
445 | };
446 |
447 | /**
448 | * @returns A valid access token.
449 | */
450 | OAuth2.prototype.getAccessToken = function() {
451 | return this.get('accessToken');
452 | };
453 |
454 | /**
455 | * Indicate whether or not a valid access token exists.
456 | *
457 | * @returns {Boolean} True if an access token exists; otherwise false.
458 | */
459 | OAuth2.prototype.hasAccessToken = function() {
460 | return !!this.get('accessToken');
461 | };
462 |
463 | /**
464 | * Clears an access token, effectively "logging out" of the service.
465 | */
466 | OAuth2.prototype.clearAccessToken = function() {
467 | this.clear('accessToken');
468 | };
469 |
--------------------------------------------------------------------------------