forked from cybrespace/pinafore
finish whole oauth logic
This commit is contained in:
parent
965826a360
commit
9753b3d1c6
|
@ -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",
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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(/=+$/, '');
|
||||
});
|
||||
}
|
|
@ -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 }
|
|
@ -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
|
||||
})
|
||||
})
|
||||
}
|
|
@ -16,7 +16,14 @@
|
|||
<p>Don't have an instance? <a href="https://joinmastodon.org">Join Mastodon!</a></p>
|
||||
</Layout>
|
||||
<style>
|
||||
@media (max-width: 767px) {
|
||||
input {
|
||||
width: 90%;
|
||||
}
|
||||
}
|
||||
|
||||
input {
|
||||
width: 250px;
|
||||
}
|
||||
|
||||
form {
|
||||
|
@ -30,11 +37,39 @@
|
|||
display: block;
|
||||
margin: 20px 5px;
|
||||
}
|
||||
|
||||
|
||||
|
||||
</style>
|
||||
<script>
|
||||
import Layout from '../_components/Layout.html';
|
||||
import { registerApplication, generateAuthLink, getAccessTokenFromAuthCode } from '../_utils/mastodon'
|
||||
import { databasePromise } from '../_utils/database'
|
||||
|
||||
export default {
|
||||
oncreate: function () {
|
||||
if (process.browser) {
|
||||
(async () => {
|
||||
let params = new URLSearchParams(location.search)
|
||||
if (params.has('code')) {
|
||||
let db = await databasePromise
|
||||
let instanceData = await db.get('instance')
|
||||
this.set({instanceName: instanceData.instanceName})
|
||||
let code = params.get('code')
|
||||
instanceData.code = code
|
||||
let response = await (await getAccessTokenFromAuthCode(
|
||||
instanceData.instanceName,
|
||||
instanceData.client_id,
|
||||
instanceData.client_secret,
|
||||
instanceData.code
|
||||
)).json()
|
||||
instanceData = Object.assign(instanceData, response)
|
||||
await db.set(`instance`, instanceData)
|
||||
console.log('response', response)
|
||||
}
|
||||
})()
|
||||
}
|
||||
},
|
||||
components: {
|
||||
Layout
|
||||
},
|
||||
|
@ -42,11 +77,17 @@
|
|||
instanceName: ''
|
||||
}),
|
||||
methods: {
|
||||
handleSubmit(event) {
|
||||
handleSubmit: async function(event) {
|
||||
event.preventDefault()
|
||||
let instanceName = this.get('instanceName')
|
||||
alert(instanceName)
|
||||
}
|
||||
instanceName = instanceName.replace(/^https?:\/\//, '').replace('/$', '')
|
||||
let data = await (await registerApplication(instanceName)).json()
|
||||
let db = await databasePromise
|
||||
data.instanceName = instanceName
|
||||
await db.set(`instance`, data)
|
||||
let oauthUrl = generateAuthLink(instanceName, data.client_id)
|
||||
document.location.href = oauthUrl
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
|
@ -10,7 +10,9 @@
|
|||
<link rel='icon' type='image/png' href='/favicon.png'>
|
||||
|
||||
<script>
|
||||
if (!location.origin.match('localhost') && 'serviceWorker' in navigator) {
|
||||
if (!location.origin.match('localhost') &&
|
||||
!location.origin.match('127.0.0.1') &&
|
||||
'serviceWorker' in navigator) {
|
||||
navigator.serviceWorker.register('/service-worker.js');
|
||||
}
|
||||
</script>
|
||||
|
|
Loading…
Reference in New Issue