Added support for new login/token endpoints.

This commit is contained in:
flash 2025-04-22 20:19:38 +00:00
parent 4acdf6090f
commit db20f087b1
Signed by: flash
GPG key ID: 2C9C2C574D47FE3E
6 changed files with 124 additions and 81 deletions

37
src/ami.js/auth.js Normal file
View file

@ -0,0 +1,37 @@
#include commitment.js
const AmiIsAuth = value => {
return typeof value === 'object'
&& value !== null
&& 'type' in value
&& (value.type === null || typeof value.type === 'string')
&& 'token' in value
&& (value.token === null || typeof value.token === 'string')
&& 'header' in value
&& (value.header === null || typeof value.header === 'string')
&& 'refresh' in value
&& typeof value.refresh === 'function';
};
const AmiFlashiiAuth = function(flashii) {
let type = null;
let token = null;
return {
get type() { return type; },
get token() { return token; },
get header() { return type ? `${type} ${token}` : null; },
refresh() {
return new Commitment(success => {
flashii.v1.chat.token({})
.success(result => {
({ token_type: type=null, access_token: token=null } = result);
success();
})
.fail(() => { flashii.v1.chat.login({}).run(); })
.run();
});
},
};
};

View file

@ -1,8 +1,8 @@
#include auth.js
#include chat.js
#include cookies.js
#include copyright.jsx
#include emotes.jsx
#include flashii.js
#include messages.jsx
#include notify.js
#include reconnect.jsx
@ -14,11 +14,11 @@
#include title.jsx
#include txtrigs.js
var AmiContext = function(title, auth, loading) {
var pub = {};
var AmiContext = function(title, auth, flashii, loading) {
if(!AmiIsAuth(auth))
throw new Error('auth param is not acceptable');
const flashii = new Flashii(`${window.FII_URL}/api`);
pub.flashii = flashii;
var pub = { auth, flashii };
var settings = new AmiSettings;
pub.settings = settings;
@ -304,10 +304,17 @@ var AmiContext = function(title, auth, loading) {
else
overlay += '<br/>' + info.baka.until.toLocaleString();
}
overlay += `<br/><br/><a href="${window.FII_URL}/_sockchat/login?legacy=1">${AmiStrings.getMenuString('back')}</a>`;
loading.hideIndicator();
loading.setTextRaw(overlay);
flashii.v1.chat.login({ assign: false })
.success(url => {
overlay += `<br/><br/><a href="${url}">${AmiStrings.getMenuString('back')}</a>`;
})
.always(() => {
loading.hideIndicator();
loading.setTextRaw(overlay);
})
.run();
});
sockChat.watch('session:term', function(info) {
console.log('session:term', info);
@ -315,11 +322,17 @@ var AmiContext = function(title, auth, loading) {
var overlay = AmiStrings.getMenuString(info.baka.type);
if(info.baka.until)
overlay += '<br/>' + info.baka.until.toLocaleString();
overlay += `<br/><br/><a href="${window.FII_URL}/_sockchat/login?legacy=1">${AmiStrings.getMenuString('back')}</a>`;
if(loading === undefined)
loading = new AmiLoadingOverlay(document.body);
loading.setTextRaw(overlay);
flashii.v1.chat.login({ assign: false })
.success(url => {
overlay += `<br/><br/><a href="${url}">${AmiStrings.getMenuString('back')}</a>`;
})
.always(() => {
if(loading === undefined)
loading = new AmiLoadingOverlay(document.body);
loading.setTextRaw(overlay);
})
.run();
});
sockChat.watch('user:add', function(info) {

View file

@ -66,6 +66,7 @@ const Flashii = function(baseUrl) {
body=null,
headers=null,
type='json',
authed=false,
}) => {
return new Commitment((success, fail) => {
const url = createUrl(path, fields);
@ -80,7 +81,7 @@ const Flashii = function(baseUrl) {
headers ??= {};
if(fresh) headers['Cache-Control'] = 'no-cache';
const options = { type, headers };
const options = { type, headers, authed };
$xhr.send(method, url, options, body)
.success(success)
@ -93,6 +94,39 @@ const Flashii = function(baseUrl) {
fii.v1 = {};
fii.v1.chat = {};
fii.v1.chat.login = function({ redirect=null, assign=true }) {
return new Commitment(success => {
redirect ??= `${location.protocol}//${location.host}`;
if(typeof redirect !== 'string')
throw new Error('redirect must a string.');
const url = createUrl('/v1/chat/login');
url.setParam('redirect', redirect);
// intentionally does not call success
if(assign)
location.assign(url);
else
success(url);
});
};
fii.v1.chat.token = function() {
return new Commitment((success, fail) => {
send({ method: 'GET', path: '/v1/chat/token', authed: true, fresh: true })
.success(({ status, body }) => {
if(status === 403)
throw new Error('You must be logged in to use chat.');
if(status > 299)
throw new Error(`Failed to fetch authorization token with error code ${status}.`);
success(body);
})
.fail(fail)
.run();
});
};
const verifyColourPresetName = name => {
if(/^([^A-Za-z0-9\-_]+)$/gu.test(name))
throw new Error('name argument is not an acceptable colour preset name.');

View file

@ -4,46 +4,38 @@
#include array.js
#include html.js
#include auth.js
#include common.js
#include ctx.js
#include flashii.js
#include loadoverlay.jsx
#include mszauth.js
#include ts_chat.jsx
(function() {
var loading = new AmiLoadingOverlay(document.body, true);
const loading = new AmiLoadingOverlay(document.body, true);
const flashii = new Flashii(`${window.FII_URL}/api`);
const auth = new AmiFlashiiAuth(flashii);
FutamiCommon.load()
.success(futami => {
window.futami = futami;
auth.refresh()
.success(() => {
setInterval(() => { auth.refresh(); }, 600000);
var auth = new AmiMisuzuAuth(`${window.FII_URL}/_sockchat/token`);
var refreshInfo = function(next) {
auth.refresh().success(token => {
if(token.ok === false) {
location.assign(`${window.FII_URL}/_sockchat/login?legacy=1`);
return;
}
FutamiCommon.load()
.success(futami => {
window.futami = futami;
if(typeof next === 'function')
next(token.ok);
}).run();
};
const ami = new AmiContext(window.TITLE, auth, flashii, loading);
window.ami = ami;
var ami = new AmiContext(window.TITLE, auth, loading);
window.ami = ami;
setInterval(refreshInfo, 600000);
refreshInfo(function() {
Chat.Main();
ami.sockChat.open();
window.addEventListener('beforeunload', () => ami.sockChat.close());
});
})
.fail(ex => {
console.log(ex);
alert('Failed to load environment settings!');
Chat.Main();
ami.sockChat.open();
window.addEventListener('beforeunload', () => ami.sockChat.close());
})
.fail(ex => {
console.log(ex);
alert('Failed to load environment settings!');
})
.run();
})
.run();
})();

View file

@ -1,36 +0,0 @@
#include commitment.js
#include xhr.js
var AmiMisuzuAuth = function(refreshUrl) {
let userId = null;
let authMethod = 'Misuzu';
let authToken = null;
return {
hasInfo: () => userId !== null && authToken !== null,
getUserId: () => userId,
getAuthToken: () => authToken,
getLine: () => `${authMethod} ${authToken}`,
getInfo: () => {
return {
method: authMethod,
token: authToken,
};
},
refresh: function() {
return new Commitment((success, fail) => {
$xhr.get(refreshUrl, { authed: true, type: 'json' })
.success(({ body }) => {
if(body.ok) {
userId = body.usr.toString();
authToken = body.tkn;
}
success(body);
})
.fail(fail)
.run();
});
},
};
};

View file

@ -1,7 +1,11 @@
#include auth.js
#include servers.js
#include watcher.js
var AmiSockChat = function(auth) {
if(!AmiIsAuth(auth))
throw new Error('auth param is not acceptable');
var pub = {},
watchers = new AmiWatcherCollection;
@ -470,8 +474,7 @@ var AmiSockChat = function(auth) {
watchers.call('msg:clear', pub);
watchers.call('chan:clear', pub);
var authInfo = auth.getInfo();
sendAuth([authInfo.method, authInfo.token]);
sendAuth([auth.type, auth.token]);
};
var onClose = function(ev) {