stylus/background/token-manager.js
2019-10-09 23:47:42 +08:00

145 lines
4.3 KiB
JavaScript

/* global chromeLocal promisify */
/* exported tokenManager */
'use strict';
const tokenManager = (() => {
const launchWebAuthFlow = promisify(chrome.identity.launchWebAuthFlow.bind(chrome.identity));
const AUTH = {
dropbox: {
flow: 'token',
clientId: 'zg52vphuapvpng9',
authURL: 'https://www.dropbox.com/oauth2/authorize',
tokenURL: 'https://api.dropboxapi.com/oauth2/token'
},
google: {
flow: 'token',
clientId: '283762574871-v3fq18bmocd1fvo4co7pfad0rcb4bti8.apps.googleusercontent.com',
authURL: 'https://accounts.google.com/o/oauth2/v2/auth',
scopes: ['https://www.googleapis.com/auth/drive.appdata']
},
onedrive: {
flow: 'token',
clientId: '3864ce03-867c-4ad8-9856-371a097d47b1',
authURL: 'https://login.microsoftonline.com/common/oauth2/v2.0/authorize',
tokenURL: 'https://login.microsoftonline.com/common/oauth2/v2.0/token',
redirect_uri: 'https://clngdbkpkpeebahjckkjfobafhncgmne.chromiumapp.org/',
scopes: ['Files.ReadWrite.AppFolder']
}
};
return {getToken, revokeToken, getClientId};
function getClientId(name) {
return AUTH[name].clientId;
}
function buildKeys(name) {
const k = {
TOKEN: `secure/token/${name}/token`,
EXPIRE: `secure/token/${name}/expire`,
REFRESH: `secure/token/${name}/refresh`,
};
k.LIST = Object.values(k);
return k;
}
function getToken(name) {
const k = buildKeys(name);
return chromeLocal.get(k.LIST)
.then(obj => {
console.log(obj, k, Date.now() > obj[k.EXPIRE]);
if (!obj[k.TOKEN] || Date.now() > obj[k.EXPIRE]) {
return refreshToken(name, k, obj)
.catch(() => authUser(name, k));
}
return obj[k.TOKEN];
});
}
function revokeToken(name) {
const k = buildKeys(name);
return chromeLocal.remove(k.LIST);
}
function refreshToken(name, k, obj) {
if (!obj[k.REFRESH]) {
return Promise.reject(new Error('no refresh token'));
}
return Promise.reject(new Error('not implemented yet'));
}
function stringifyQuery(obj) {
const search = new URLSearchParams();
for (const key of Object.keys(obj)) {
search.set(key, obj[key]);
}
return search.toString();
}
function authUser(name, k) {
const provider = AUTH[name];
const state = Math.random().toFixed(8).slice(2);
const query = {
response_type: provider.flow,
client_id: provider.clientId,
redirect_uri: provider.redirect_uri || chrome.identity.getRedirectURL(),
state
};
if (provider.scopes) {
query.scope = provider.scopes.join(' ');
}
const url = `${provider.authURL}?${stringifyQuery(query)}`;
return launchWebAuthFlow({
url,
interactive: true
})
.then(url => {
const params = new URLSearchParams(
provider.flow === 'token' ?
new URL(url).hash.slice(1) :
new URL(url).search.slice(1)
);
if (params.get('state') !== state) {
throw new Error(`unexpected state: ${params.get('state')}, expected: ${state}`);
}
if (provider.flow === 'token') {
const obj = {};
for (const [key, value] of params.entries()) {
obj[key] = value;
}
return obj;
}
const code = params.get('code');
return fetch(provider.tokenURL, {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
body: stringifyQuery({
code,
grant_type: 'authorization_code',
client_id: provider.clientId,
redirect_uri: provider.redirect_uri || chrome.identity.getRedirectURL()
})
})
.then(r => {
if (r.ok) {
return r.json();
}
return r.text()
.then(body => {
throw new Error(`failed to fetch (${r.status}): ${body}`);
});
});
})
.then(result =>
console.log(result) && 0 || chromeLocal.set({
[k.TOKEN]: result.access_token,
[k.EXPIRE]: result.expires_in ? Date.now() + result.expires_in * 1000 : undefined,
[k.REFRESH]: result.refresh_token
})
.then(() => result.access_token)
);
}
})();