diff --git a/public/auth.js b/public/auth.js new file mode 100644 index 000000000..fdad111f8 --- /dev/null +++ b/public/auth.js @@ -0,0 +1,190 @@ +document.addEventListener("DOMContentLoaded", async function() { + await ready(); +}); + +async function ready() { + const domain = localStorage.getItem('domain'); + let accessToken = localStorage.getItem(`access_token_${domain}`); + + if (domain) document.getElementById('instance').value = domain; + + if (domain && accessToken) { + // do something + } + + const urlParams = new URLSearchParams(window.location.search); + const code = urlParams.get('code'); + + if (domain && code && !accessToken) await getToken(code, domain).then(res => accessToken = res); + if (accessToken) await setStateAndRedirect(accessToken, domain); +} + +async function auth() { + setMessage('Please wait'); + const instance = document.getElementById('instance').value; + const domain = instance.match(/(?:https?:\/\/)?(.*)/)[1]; + if (!domain) { + document.getElementById('message').textContent = 'Invalid instance'; + return; + } + + localStorage.setItem('domain', domain); + + if (!localStorage.getItem(`client_id_${domain}`) || !localStorage.getItem(`client_secret_${domain}`)) { + await registerApp(domain); + } + + authorize(domain); +} + +async function registerApp(domain) { + setMessage('Registering app'); + + const appsUrl = `https://${domain}/api/v1/apps`; + const formData = new FormData(); + formData.append('client_name', 'Masto-FE standalone'); + formData.append('redirect_uris', document.location.href); + formData.append('scopes', 'read write follow push'); + + // eslint-disable-next-line promise/catch-or-return + await fetch(appsUrl, { + method: 'POST', + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + }, + body: new URLSearchParams(formData), + }) + .then(async res => { + const app = await res.json(); + localStorage.setItem(`client_id_${domain}`, app.client_id); + localStorage.setItem(`client_secret_${domain}`, app.client_secret); + }); +} + +function authorize(domain) { + setMessage('Authorizing'); + const clientId = localStorage.getItem(`client_id_${domain}`); + document.location.href = `https://${domain}/oauth/authorize?response_type=code&client_id=${clientId}&redirect_uri=${document.location.href}&scope=read+write+follow+push`; +} + +async function getToken(code, domain) { + setMessage('Getting token'); + + const tokenUrl = `https://${domain}/oauth/token`; + const clientId = localStorage.getItem(`client_id_${domain}`); + const clientSecret = localStorage.getItem(`client_secret_${domain}`); + + const formData = new FormData(); + formData.append('grant_type', 'authorization_code'); + formData.append('code', code); + formData.append('client_id', clientId); + formData.append('client_secret', clientSecret); + formData.append('scope', 'read write follow push'); + formData.append('redirect_uri', document.location.href); + + + // eslint-disable-next-line promise/catch-or-return + return fetch(tokenUrl, { + method: 'POST', + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + }, + body: new URLSearchParams(formData), + }) + .then(async res => { + const app = await res.json(); + if (app.access_token) localStorage.setItem(`access_token_${domain}`, app.access_token); + return app.access_token; + }); +} + +async function setStateAndRedirect(access_token, domain) { + setMessage('Assembling state object'); + const apiUrl = `https://${domain}/api`; + const instance = await fetch(`${apiUrl}/v1/instance`).then(async p => await p.json()); + const options = {headers: {Authorization: `Bearer ${access_token}`}}; + const credentials = await fetch(`${apiUrl}/v1/accounts/verify_credentials`, options).then(async p => await p.json()); + const state = { + "accounts": { + "plc":{ + "accepts_direct_messages_from":"everybody", + "acct": credentials.acct, + "avatar": credentials.avatar, + "avatar_static": credentials.avatar_static, + "bot": credentials.bot, + "created_at": credentials.created_at, + "display_name": credentials.display_name, + "emojis":[], + "fields":[], + "follow_requests_count":0, + "followers_count": credentials.followers_count, + "following_count": credentials.following_count, + "fqn":`${credentials.acct}@${domain}`, + "header": credentials.header, + "header_static": credentials.header_static, + "id": credentials.id, + "last_status_at": credentials.created_at, + "locked": credentials.locked, + "note":"", + "source": credentials.source, + "statuses_count": credentials.statuses_count, + "url": credentials.url, + "username": credentials.acct + } + }, + "char_limit": instance.configuration.statuses.max_characters, + "compose": { + "allow_content_types": [ + "text/x.misskeymarkdown" + ], + "default_privacy": credentials.source.privacy, + "default_sensitive": credentials.source.sensitive, + "me": credentials.id + }, + "media_attachments": { + "accept_content_types": instance.configuration.media_attachments.supported_mime_types + }, + "meta": { + "access_token": access_token, + "admin": "0", + "advanced_layout": true, + "auto_play_gif": false, + "boost_modal": false, + "compact_reaction": false, + "delete_modal": true, + "display_sensitive_media": false, + "domain": domain, + "enable_reaction": true, + "locale": "en", + "mascot": "/images/pleroma-fox-tan-smol.png", + "max_toot_chars": instance.configuration.statuses.max_characters, + "me": credentials.id, + "reduce_motion": false, + "show_quote_button": true, + "base_url": `https://${domain}`, + "streaming_api_base_url": `wss://${domain}`, + "title": `${instance.title}`, + "unfollow_modal": true + }, + "poll_limits": { + "max_expiration": instance.configuration.polls.max_expiration, + "max_option_chars": instance.configuration.polls.max_characters_per_option, + "max_options": instance.configuration.polls.max_options, + "min_expiration": instance.configuration.polls.min_expiration + }, + "push_subscription": null, + "rights": { + "admin": false, + "delete_others_notice": false + }, + "settings": {} + }; + + localStorage.setItem('initial-state', JSON.stringify(state)); + window.location.href = '/'; +} + +function setMessage(message) { + document.getElementById('message').textContent = message; + document.getElementById('btn').enabled = false; +} \ No newline at end of file diff --git a/public/index.html b/public/index.html new file mode 100644 index 000000000..20fc30ebc --- /dev/null +++ b/public/index.html @@ -0,0 +1,33 @@ + + +
+ + +