From 9753b3d1c687b13fd7bd171bae616d92658d6e3e Mon Sep 17 00:00:00 2001 From: Nolan Lawson Date: Sun, 7 Jan 2018 22:00:16 -0800 Subject: [PATCH] finish whole oauth logic --- package-lock.json | 5 +++ package.json | 1 + routes/_utils/binary.js | 27 ++++++++++++++++ routes/_utils/database.js | 21 +++++++++++++ routes/_utils/mastodon.js | 51 +++++++++++++++++++++++++++++++ routes/settings/add-instance.html | 47 ++++++++++++++++++++++++++-- templates/2xx.html | 4 ++- 7 files changed, 152 insertions(+), 4 deletions(-) create mode 100644 routes/_utils/binary.js create mode 100644 routes/_utils/database.js create mode 100644 routes/_utils/mastodon.js diff --git a/package-lock.json b/package-lock.json index 9ecb381d..b4e3de4e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2487,6 +2487,11 @@ } } }, + "idb-keyval": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/idb-keyval/-/idb-keyval-2.3.0.tgz", + "integrity": "sha1-TURLgMP4b8vNUTIbTcvJJHxZSMA=" + }, "ieee754": { "version": "1.1.8", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.8.tgz", diff --git a/package.json b/package.json index 0ca9639c..3cb36e47 100644 --- a/package.json +++ b/package.json @@ -18,6 +18,7 @@ "extract-text-webpack-plugin": "^3.0.2", "font-awesome-svg-png": "^1.2.2", "glob": "^7.1.2", + "idb-keyval": "^2.3.0", "node-fetch": "^1.7.3", "npm-run-all": "^4.1.2", "sapper": "^0.3.1", diff --git a/routes/_utils/binary.js b/routes/_utils/binary.js new file mode 100644 index 00000000..a14f023d --- /dev/null +++ b/routes/_utils/binary.js @@ -0,0 +1,27 @@ +// from blob-util +function blobToBinaryString(blob) { + return new Promise(function (resolve, reject) { + var reader = new FileReader(); + var hasBinaryString = typeof reader.readAsBinaryString === 'function'; + reader.onloadend = function (e) { + var result = e.target.result || ''; + if (hasBinaryString) { + return resolve(result); + } + resolve(arrayBufferToBinaryString(result)); + }; + reader.onerror = reject; + if (hasBinaryString) { + reader.readAsBinaryString(blob); + } else { + reader.readAsArrayBuffer(blob); + } + }); +} + +export function blobToBase64(blob) { + return blobToBinaryString(blob).then(function (binary) { + // web-safe variant + return btoa(binary).replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, ''); + }); +} \ No newline at end of file diff --git a/routes/_utils/database.js b/routes/_utils/database.js new file mode 100644 index 00000000..83429494 --- /dev/null +++ b/routes/_utils/database.js @@ -0,0 +1,21 @@ +import idbKeyVal from 'idb-keyval' +import { blobToBase64 } from '../_utils/binary' + +let databasePromise + +if (process.browser) { + databasePromise = Promise.resolve().then(async () => { + let token = await idbKeyVal.get('secure_token') + if (!token) { + let array = new Uint32Array(1028) + crypto.getRandomValues(array); + let token = await blobToBase64(new Blob([array])) + await idbKeyVal.set('secure_token', token) + } + return idbKeyVal + }) +} else { + databasePromise = Promise.resolve() +} + +export { databasePromise } diff --git a/routes/_utils/mastodon.js b/routes/_utils/mastodon.js new file mode 100644 index 00000000..e70a82b8 --- /dev/null +++ b/routes/_utils/mastodon.js @@ -0,0 +1,51 @@ +const WEBSITE = 'https://pinafore.social' +const REDIRECT_URI = (typeof location !== 'undefined' ? location.origin : 'https://pinafore.social') + '/settings/add-instance' +const SCOPES = 'read write follow' +const CLIENT_NAME = 'Pinafore' + +export function registerApplication(instanceName) { + const url = `https://${instanceName}/api/v1/apps` + return fetch(url, { + method: 'POST', + headers: { + 'Accept': 'application/json', + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + client_name: CLIENT_NAME, + redirect_uris: REDIRECT_URI, + scopes: SCOPES, + website: WEBSITE + }) + }) +} + +export function generateAuthLink(instanceName, clientId) { + let url = `https://${instanceName}/oauth/authorize` + + let params = new URLSearchParams() + params.set('client_id', clientId) + params.set('redirect_uri', REDIRECT_URI) + params.set('response_type', 'code') + params.set('scope', SCOPES) + url += '?' + params.toString() + return url +} + +export function getAccessTokenFromAuthCode(instanceName, clientId, clientSecret, code) { + let url = `https://${instanceName}/oauth/token` + return fetch(url, { + method: 'POST', + headers: { + 'Accept': 'application/json', + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + client_id: clientId, + client_secret: clientSecret, + redirect_uri: REDIRECT_URI, + grant_type: 'authorization_code', + code: code + }) + }) +} \ No newline at end of file diff --git a/routes/settings/add-instance.html b/routes/settings/add-instance.html index 505eed94..2088c148 100644 --- a/routes/settings/add-instance.html +++ b/routes/settings/add-instance.html @@ -16,7 +16,14 @@

Don't have an instance? Join Mastodon!

\ No newline at end of file diff --git a/templates/2xx.html b/templates/2xx.html index a92fd3ec..42d21e8e 100644 --- a/templates/2xx.html +++ b/templates/2xx.html @@ -10,7 +10,9 @@