Files
masto-fe-archos/public/auth.js
vyxen 370a666d27 [feature] Nicer login.html page, with cute styling + link to source code (#19)
Fixes #9

- Cute styling, combination of Mastodon and GTS
- Short description of the project
- Error and status messages (temporarily) appear in disabled button with correct ARIA attributes
- Sufficient contrast (WCAG AAA)

Let me know if using `login.scss` both as an index file and for adding custom styling is okay. I figured this might be preferred over creating an extra folder and file.

Reviewed-on: https://codeberg.org/superseriousbusiness/masto-fe-standalone/pulls/19
Co-authored-by: vyxen <vyxen@tutamail.com>
Co-committed-by: vyxen <vyxen@tutamail.com>
2025-04-03 11:07:34 +00:00

123 lines
4.1 KiB
JavaScript

document.addEventListener("DOMContentLoaded", async function() {
await ready();
});
async function ready() {
const domain = localStorage.getItem('domain');
let accessToken = localStorage.getItem(`access_token`);
if (domain) document.getElementById('instance').value = domain;
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) {
window.location.href = '/prepare.html';
}
}
async function auth() {
setMessage('Please wait');
const instance = document.getElementById('instance').value;
const matches = instance.match(/((?:http|https):\/\/)?(.*)/);
const protocol = matches[1];
if (protocol) {
localStorage.setItem('protocol', protocol);
}
const domain = matches[2];
if (!domain) {
setMessage('Invalid instance', true);
await new Promise(r => setTimeout(r, 2000));
setMessage('Authorize', false, false);
return;
}
localStorage.setItem('domain', domain);
// We need to run this every time in cases like Iceshrimp,
// where the client id/secret aren't reusable (yet) because
// they contain use-once session information.
await registerApp(domain);
authorize(domain);
}
async function registerApp(domain) {
setMessage('Registering app');
const protocol = localStorage.getItem(`protocol`) ?? `https://`;
const appsUrl = `${protocol}${domain}/api/v1/apps`;
const formData = new FormData();
formData.append('client_name', 'Masto-FE (🦥 flavour)');
formData.append('website', 'https://codeberg.org/superseriousbusiness/masto-fe-standalone');
formData.append('redirect_uris', document.location.origin + document.location.pathname);
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`, app.client_id);
localStorage.setItem(`client_secret`, app.client_secret);
});
}
function authorize(domain) {
setMessage('Authorizing');
const clientId = localStorage.getItem(`client_id`);
const protocol = localStorage.getItem(`protocol`) ?? `https://`;
document.location.href = `${protocol}${domain}/oauth/authorize?response_type=code&client_id=${clientId}&redirect_uri=${document.location.origin + document.location.pathname}&scope=read+write+follow+push`;
}
async function getToken(code, domain) {
setMessage('Getting token');
const protocol = localStorage.getItem(`protocol`) ?? `https://`;
const tokenUrl = `${protocol}${domain}/oauth/token`;
const clientId = localStorage.getItem(`client_id`);
const clientSecret = localStorage.getItem(`client_secret`);
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.origin + document.location.pathname);
// 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`, app.access_token);
return app.access_token;
});
}
function setMessage(message, error = false, disabled = true) {
document.getElementById('message').setAttribute('role', 'status');
document.getElementById('message').textContent = message;
document.getElementById('btn').disabled = disabled;
if (!error) return;
const instance = document.getElementById('instance');
instance.setAttribute('aria-invalid', true);
instance.setAttribute('aria-describedby', 'message');
}