refactor(all): prefer token-based authentication

This commit is contained in:
vulet 2021-01-17 17:58:02 +08:00
parent 9e17440abc
commit f69b52261a
24 changed files with 166 additions and 117 deletions

52
auth.js Normal file
View file

@ -0,0 +1,52 @@
const sdk = require('matrix-js-sdk');
const axios = require('axios');
const fs = require('fs');
const registrar = require('./registrar.js');
module.exports.getMatrixToken = async () => {
matrixClient = sdk.createClient(registrar.config.matrix.domain);
matrixClient.loginWithPassword(registrar.config.matrix.user, registrar.config.matrix.password)
.then((response) => {
fs.writeFileSync('matrix_auth.json', JSON.stringify(response, null, 2));
matrixClient.startClient();
});
};
module.exports.matrixTokenLogin = async () => {
matrixClient = sdk.createClient({
baseUrl: registrar.config.matrix.domain,
accessToken: registrar.matrix_auth.access_token,
userId: registrar.matrix_auth.user_id,
timelineSupport: true,
});
matrixClient.startClient();
};
module.exports.registerFediverseApp = async () => {
axios.post(`${registrar.config.fediverse.domain}/api/v1/apps`,
{
client_name: registrar.config.fediverse.client_name,
redirect_uris: 'urn:ietf:wg:oauth:2.0:oob',
scopes: 'read write follow push',
})
.then((response) => {
axios.post(`${registrar.config.fediverse.domain}/oauth/token`,
{
username: registrar.config.fediverse.username,
password: registrar.config.fediverse.password,
client_id: response.data.client_id,
client_secret: response.data.client_secret,
scope: 'read write follow push',
grant_type: 'password',
redirect_uri: 'urn:ietf:wg:oauth:2.0:oob',
})
.then((tokens) => {
fs.writeFileSync('fediverse_auth.json', JSON.stringify(tokens.data, null, 2));
})
.catch((e) => {
console.log(e);
});
}).catch((e) => {
console.log(e);
});
};

View file

@ -4,7 +4,7 @@ exports.runQuery = function (matrixClient, room, registrar) {
axios({ axios({
method: 'POST', method: 'POST',
url: `${registrar.config.fediverse.domain}/api/v1/statuses`, url: `${registrar.config.fediverse.domain}/api/v1/statuses`,
headers: { Authorization: `Bearer ${registrar.config.fediverse.token}` }, headers: { Authorization: `Bearer ${registrar.fediverse_auth.access_token}` },
data: { status: `@10grans@fedi.cc beg` }, data: { status: `@10grans@fedi.cc beg` },
}).then((response) => { }).then((response) => {
matrixClient.sendHtmlNotice(room.roomId, matrixClient.sendHtmlNotice(room.roomId,
@ -18,4 +18,4 @@ exports.runQuery = function (matrixClient, room, registrar) {
matrixClient.sendHtmlNotice(room.roomId, matrixClient.sendHtmlNotice(room.roomId,
'', `${e}`); '', `${e}`);
}); });
}; };

View file

@ -4,7 +4,7 @@ exports.runQuery = function (matrixClient, room, userInput, registrar) {
axios({ axios({
method: 'POST', method: 'POST',
url: `${registrar.config.fediverse.domain}/api/v1/statuses/${userInput}/unfavourite`, url: `${registrar.config.fediverse.domain}/api/v1/statuses/${userInput}/unfavourite`,
headers: { Authorization: `Bearer ${registrar.config.fediverse.token}` }, headers: { Authorization: `Bearer ${registrar.fediverse_auth.access_token}` },
}).then((response) => { }).then((response) => {
matrixClient.sendHtmlNotice(room.roomId, matrixClient.sendHtmlNotice(room.roomId,
'', '',

View file

@ -4,7 +4,7 @@ exports.runQuery = function (matrixClient, room, userInput, registrar) {
axios({ axios({
method: 'POST', method: 'POST',
url: `${registrar.config.fediverse.domain}/api/v1/statuses/${userInput}/favourite`, url: `${registrar.config.fediverse.domain}/api/v1/statuses/${userInput}/favourite`,
headers: { Authorization: `Bearer ${registrar.config.fediverse.token}` }, headers: { Authorization: `Bearer ${registrar.fediverse_auth.access_token}` },
}).then((response) => { }).then((response) => {
matrixClient.sendHtmlNotice(room.roomId, matrixClient.sendHtmlNotice(room.roomId,
'', '',

View file

@ -4,7 +4,7 @@ exports.runQuery = function (matrixClient, room, userInput, registrar) {
axios({ axios({
method: 'POST', method: 'POST',
url: `${registrar.config.fediverse.domain}/api/v1/statuses/${userInput}/reblog`, url: `${registrar.config.fediverse.domain}/api/v1/statuses/${userInput}/reblog`,
headers: { Authorization: `Bearer ${registrar.config.fediverse.token}` }, headers: { Authorization: `Bearer ${registrar.fediverse_auth.access_token}` },
}).then((response) => { }).then((response) => {
matrixClient.sendHtmlNotice(room.roomId, matrixClient.sendHtmlNotice(room.roomId,
'', '',

View file

@ -6,7 +6,7 @@ exports.runQuery = function (matrixClient, room, registrar) {
axios({ axios({
method: 'GET', method: 'GET',
url: `${registrar.config.fediverse.domain}/api/v1/timelines/home`, url: `${registrar.config.fediverse.domain}/api/v1/timelines/home`,
headers: { Authorization: `Bearer ${registrar.config.fediverse.token}` }, headers: { Authorization: `Bearer ${registrar.fediverse_auth.access_token}` },
}).then((events) => { }).then((events) => {
const event = fs.readFileSync('timeline.json', 'utf8'); const event = fs.readFileSync('timeline.json', 'utf8');
fs.writeFileSync('timeline.json', events.data[0].created_at, 'utf8'); fs.writeFileSync('timeline.json', events.data[0].created_at, 'utf8');

View file

@ -5,7 +5,7 @@ exports.runQuery = function (matrixClient, room, userInput, registrar) {
axios({ axios({
method: 'POST', method: 'POST',
url: `${registrar.config.fediverse.domain}/api/v1/accounts/${findUID.data.id}/follow`, url: `${registrar.config.fediverse.domain}/api/v1/accounts/${findUID.data.id}/follow`,
headers: { Authorization: `Bearer ${registrar.config.fediverse.token}` }, headers: { Authorization: `Bearer ${registrar.fediverse_auth.access_token}` },
}) })
.then((response) => { .then((response) => {
matrixClient.sendHtmlNotice(room.roomId, matrixClient.sendHtmlNotice(room.roomId,

View file

@ -26,7 +26,7 @@ const mediaDownload = async (url, { whitelist, blacklist }) => {
}; };
}; };
const mediaUpload = async ({ domain, token }, { data, filename, mimetype }) => { const mediaUpload = async ({ domain }, { data, filename, mimetype }, registrar) => {
const form = new FormData(); const form = new FormData();
form.append('file', data, { form.append('file', data, {
filename: filename || 'upload', filename: filename || 'upload',
@ -35,7 +35,7 @@ const mediaUpload = async ({ domain, token }, { data, filename, mimetype }) => {
const upload = await axios({ const upload = await axios({
method: 'POST', method: 'POST',
url: `${domain}/api/v1/media`, url: `${domain}/api/v1/media`,
headers: form.getHeaders({ Authorization: `Bearer ${token}` }), headers: form.getHeaders({ Authorization: `Bearer ${registrar.fediverse_auth.access_token}` }),
data: form, data: form,
}); });
if(upload.statusText !== 'OK') throw upload; if(upload.statusText !== 'OK') throw upload;
@ -47,12 +47,12 @@ const run = async (matrixClient, { roomId }, content, replyId, mediaURL, subject
const fediverse = registrar.config.fediverse; const fediverse = registrar.config.fediverse;
if(mediaURL) { if(mediaURL) {
const media = await mediaDownload(mediaURL, registrar.config.fediverse.mimetypes); const media = await mediaDownload(mediaURL, registrar.config.fediverse.mimetypes);
mediaId = await mediaUpload(fediverse, media); mediaId = await mediaUpload(fediverse, media, registrar);
} }
const response = await axios({ const response = await axios({
method: 'POST', method: 'POST',
url: `${fediverse.domain}/api/v1/statuses`, url: `${fediverse.domain}/api/v1/statuses`,
headers: { Authorization: `Bearer ${fediverse.token}`, 'Content-Type': 'application/x-www-form-urlencoded' }, headers: { Authorization: `Bearer ${registrar.fediverse_auth.access_token}`, 'Content-Type': 'application/x-www-form-urlencoded' },
data : qs.stringify({ data : qs.stringify({
status: content, status: content,
content_type: `text/markdown`, content_type: `text/markdown`,

View file

@ -4,9 +4,9 @@ exports.runQuery = function (matrixClient, room, userInput, registrar) {
axios({ axios({
method: 'POST', method: 'POST',
url: `${registrar.config.fediverse.domain}/api/v1/statuses`, url: `${registrar.config.fediverse.domain}/api/v1/statuses`,
headers: { Authorization: `Bearer ${registrar.config.fediverse.token}` }, headers: { Authorization: `Bearer ${registrar.fediverse_auth.access_token}` },
data: { data: {
status: `@mordekai ${userInput}`, status: `@mordekai ${userInput}`,
content_type: `text/markdown`, content_type: `text/markdown`,
visibility: 'unlisted', visibility: 'unlisted',
expires_in: '7200' expires_in: '7200'

View file

@ -6,7 +6,7 @@ exports.runQuery = function (matrixClient, room, registrar) {
axios({ axios({
method: 'GET', method: 'GET',
url: `${registrar.config.fediverse.domain}/api/v1/notifications`, url: `${registrar.config.fediverse.domain}/api/v1/notifications`,
headers: { Authorization: `Bearer ${registrar.config.fediverse.token}` }, headers: { Authorization: `Bearer ${registrar.fediverse_auth.access_token}` },
}).then((notifications) => { }).then((notifications) => {
const event = fs.readFileSync('notification.json', 'utf8'); const event = fs.readFileSync('notification.json', 'utf8');
fs.writeFileSync('notification.json', notifications.data[0].created_at, 'utf8'); fs.writeFileSync('notification.json', notifications.data[0].created_at, 'utf8');

View file

@ -4,7 +4,7 @@ exports.runQuery = function (matrixClient, room, userInput, registrar) {
axios({ axios({
method: 'POST', method: 'POST',
url: `${registrar.config.fediverse.domain}/api/v1/statuses/${userInput}/pin`, url: `${registrar.config.fediverse.domain}/api/v1/statuses/${userInput}/pin`,
headers: { Authorization: `Bearer ${registrar.config.fediverse.token}` }, headers: { Authorization: `Bearer ${registrar.fediverse_auth.access_token}` },
}).then((response) => { }).then((response) => {
matrixClient.sendHtmlNotice(room.roomId, matrixClient.sendHtmlNotice(room.roomId,
'', '',

View file

@ -4,7 +4,7 @@ exports.runQuery = function (matrixClient, room, userInput, registrar) {
axios({ axios({
method: 'POST', method: 'POST',
url: `${registrar.config.fediverse.domain}/api/v1/statuses`, url: `${registrar.config.fediverse.domain}/api/v1/statuses`,
headers: { Authorization: `Bearer ${registrar.config.fediverse.token}` }, headers: { Authorization: `Bearer ${registrar.fediverse_auth.access_token}` },
data: { status: userInput, content_type: `text/markdown` }, data: { status: userInput, content_type: `text/markdown` },
}).then((response) => { }).then((response) => {
matrixClient.sendHtmlNotice(room.roomId, matrixClient.sendHtmlNotice(room.roomId,

View file

@ -4,7 +4,7 @@ exports.runQuery = function (matrixClient, room, userInput, registrar) {
axios({ axios({
method: 'DELETE', method: 'DELETE',
url: `${registrar.config.fediverse.domain}/api/v1/statuses/${userInput}`, url: `${registrar.config.fediverse.domain}/api/v1/statuses/${userInput}`,
headers: { Authorization: `Bearer ${registrar.config.fediverse.token}` }, headers: { Authorization: `Bearer ${registrar.fediverse_auth.access_token}` },
}).then((response) => { }).then((response) => {
matrixClient.sendHtmlNotice(room.roomId, matrixClient.sendHtmlNotice(room.roomId,
'', '',

View file

@ -4,7 +4,7 @@ exports.runQuery = function (matrixClient, room, address, flaggedInput, registra
axios({ axios({
method: 'POST', method: 'POST',
url: `${registrar.config.fediverse.domain}/api/v1/statuses`, url: `${registrar.config.fediverse.domain}/api/v1/statuses`,
headers: { Authorization: `Bearer ${registrar.config.fediverse.token}` }, headers: { Authorization: `Bearer ${registrar.fediverse_auth.access_token}` },
data: { status: flaggedInput, in_reply_to_id: address, content_type: `text/markdown` }, data: { status: flaggedInput, in_reply_to_id: address, content_type: `text/markdown` },
}).then((response) => { }).then((response) => {
matrixClient.sendHtmlNotice(room.roomId, matrixClient.sendHtmlNotice(room.roomId,

View file

@ -4,7 +4,7 @@ exports.runQuery = function (matrixClient, room, userInput, registrar) {
axios({ axios({
method: 'GET', method: 'GET',
url: `${registrar.config.fediverse.domain}/api/v1/statuses/${userInput}`, url: `${registrar.config.fediverse.domain}/api/v1/statuses/${userInput}`,
headers: { Authorization: `Bearer ${registrar.config.fediverse.token}` }, headers: { Authorization: `Bearer ${registrar.fediverse_auth.access_token}` },
}).then((response) => { }).then((response) => {
matrixClient.sendHtmlNotice(room.roomId, matrixClient.sendHtmlNotice(room.roomId,
'', '',

View file

@ -4,7 +4,7 @@ exports.runQuery = function (matrixClient, room, address, flaggedInput, registra
axios({ axios({
method: 'POST', method: 'POST',
url: `${registrar.config.fediverse.domain}/api/v1/statuses`, url: `${registrar.config.fediverse.domain}/api/v1/statuses`,
headers: { Authorization: `Bearer ${registrar.config.fediverse.token}` }, headers: { Authorization: `Bearer ${registrar.fediverse_auth.access_token}` },
data: { status: `@10grans@fedi.cc tip `+ flaggedInput + ` to `+address }, data: { status: `@10grans@fedi.cc tip `+ flaggedInput + ` to `+address },
}).then((response) => { }).then((response) => {
matrixClient.sendHtmlNotice(room.roomId, matrixClient.sendHtmlNotice(room.roomId,
@ -18,4 +18,4 @@ exports.runQuery = function (matrixClient, room, address, flaggedInput, registra
matrixClient.sendHtmlNotice(room.roomId, matrixClient.sendHtmlNotice(room.roomId,
'', `${e}`); '', `${e}`);
}); });
}; };

View file

@ -5,7 +5,7 @@ exports.runQuery = function (matrixClient, room, userInput, registrar) {
axios({ axios({
method: 'POST', method: 'POST',
url: `${registrar.config.fediverse.domain}/api/v1/accounts/${findUID.data.id}/unfollow`, url: `${registrar.config.fediverse.domain}/api/v1/accounts/${findUID.data.id}/unfollow`,
headers: { Authorization: `Bearer ${registrar.config.fediverse.token}` }, headers: { Authorization: `Bearer ${registrar.fediverse_auth.access_token}` },
}) })
.then((response) => { .then((response) => {
matrixClient.sendHtmlNotice(room.roomId, matrixClient.sendHtmlNotice(room.roomId,

View file

@ -4,7 +4,7 @@ exports.runQuery = function (matrixClient, room, userInput, registrar) {
axios({ axios({
method: 'POST', method: 'POST',
url: `${registrar.config.fediverse.domain}/api/v1/statuses/${userInput}/unpin`, url: `${registrar.config.fediverse.domain}/api/v1/statuses/${userInput}/unpin`,
headers: { Authorization: `Bearer ${registrar.config.fediverse.token}` }, headers: { Authorization: `Bearer ${registrar.fediverse_auth.access_token}` },
}).then((response) => { }).then((response) => {
matrixClient.sendHtmlNotice(room.roomId, matrixClient.sendHtmlNotice(room.roomId,
'', '',

View file

@ -7,7 +7,9 @@ module.exports = {
}, },
fediverse: { fediverse: {
domain: 'https://your_federation.com', domain: 'https://your_federation.com',
token: 'your_federation_token', username: '',
password: '',
client_name: '',
subject: '', subject: '',
mimetypes: { mimetypes: {
whitelist: [], whitelist: [],

9
fediverse_auth.json Normal file
View file

@ -0,0 +1,9 @@
{
"access_token": "",
"created_at": 0,
"expires_in": 0,
"me": "",
"refresh_token": "",
"scope": "",
"token_type": ""
}

152
main.js
View file

@ -1,95 +1,73 @@
const sdk = require('matrix-js-sdk');
const axios = require('axios');
const registrar = require('./registrar.js'); const registrar = require('./registrar.js');
const auth = require('./auth.js');
const auth = { registrar.matrix_auth.access_token ? auth.matrixTokenLogin() : auth.getMatrixToken();
type: 'm.login.password', if (!registrar.fediverse_auth.access_token && registrar.config.fediverse.username) auth.registerFediverseApp();
user: registrar.config.matrix.user,
password: registrar.config.matrix.password,
};
axios.post(`${registrar.config.matrix.domain}/_matrix/client/r0/login`, auth).then((response) => { matrixClient.on('RoomMember.membership', (event, member) => {
CreateClient(response.data.access_token, response.data.user_id); if (member.membership === 'invite' && member.userId === matrixClient.credentials.userId) {
}).catch((e) => { matrixClient.joinRoom(member.roomId).done(() => {
console.log(e); console.log('Auto-joined %s', member.roomId);
});
}
if (member.membership === 'leave' && member.userId === matrixClient.credentials.userId) {
matrixClient.forget(member.roomId).then(() => {
console.log('Kicked %s', member.roomId);
});
}
}); });
let CreateClient = (token, user_id) => { matrixClient.on('Room.timeline', (event, room, toStartOfTimeline) => {
const matrixClient = sdk.createClient({ if (toStartOfTimeline) return;
baseUrl: registrar.config.matrix.domain, if (event.getType() !== 'm.room.message') return;
accessToken: token, if (event.getSender() === matrixClient.credentials.userId) return;
userId: user_id, if (event.event.unsigned.age > 10000) return;
timelineSupport: true, if (event.event.content.body.charAt(0) === '+') {
}); console.log(`Logs: ${event.event.sender} - ${event.event.content.body}`);
let args = event.event.content.body.slice(1).trim().split(/ +/g);
let command = args.shift().toLowerCase();
const userInput = args.join(' ');
const flaggedInput = userInput.substr(userInput.indexOf(' ') + 1);
const address = args.slice(0, 1).join(' ').replace(/"/g, '');
matrixClient.on('RoomMember.membership', (event, member) => { args = [];
if (member.membership === 'invite' && member.userId === matrixClient.credentials.userId) {
matrixClient.joinRoom(member.roomId).done(() => { switch(command) {
console.log('Auto-joined %s', member.roomId); case 'config':
}); return;
case 'help': case 'beg': case 'flood': case 'notify':
args.push(matrixClient, room, registrar);
break;
case 'tip':
args.push(matrixClient, room, address, flaggedInput, registrar);
break;
case 'archive': case 'rearchive':
args.push(matrixClient, room, userInput, !!~command.indexOf('re'), registrar);
command = 'archive';
break;
case 'post': case 'reply': case 'media': case 'mediareply':
case 'random': case 'randomreply': case 'randommedia': case 'randommediareply':
args.push(matrixClient, room, userInput, registrar, {
isReply: !!~command.indexOf('reply'),
hasMedia: !!~command.indexOf('media'),
hasSubject: !!~command.indexOf('random'),
});
command = 'media';
break;
case 'proxy':
try {
const url = new URL(userInput);
command = registrar.config.invidious.domains.includes(url.hostname)
? 'invidious'
: registrar.config.nitter.domains.includes(url.hostname)
? 'nitter'
: 'proxy';
} catch(e) {}
//fallthrough
default:
args.push(matrixClient, room, userInput, registrar);
} }
if (member.membership === 'leave' && member.userId === matrixClient.credentials.userId) {
matrixClient.forget(member.roomId).then(() => {
console.log('Kicked %s', member.roomId);
});
}
});
matrixClient.on('Room.timeline', (event, room, toStartOfTimeline) => { registrar[command] && registrar[command].runQuery.apply(null, args);
if (toStartOfTimeline) return; }
if (event.getType() !== 'm.room.message') return; });
if (event.getSender() === matrixClient.credentials.userId) return;
if (event.event.unsigned.age > 10000) return;
if (event.event.content.body.charAt(0) === '+') {
console.log(`Logs: ${event.event.sender} - ${event.event.content.body}`);
let args = event.event.content.body.slice(1).trim().split(/ +/g);
let command = args.shift().toLowerCase();
const userInput = args.join(' ');
const flaggedInput = userInput.substr(userInput.indexOf(' ') + 1);
const address = args.slice(0, 1).join(' ').replace(/"/g, '');
args = [];
switch(command) {
case 'config':
return;
case 'help': case 'beg': case 'flood': case 'notify':
args.push(matrixClient, room, registrar);
break;
case 'tip':
args.push(matrixClient, room, address, flaggedInput, registrar);
break;
case 'archive': case 'rearchive':
args.push(matrixClient, room, userInput, !!~command.indexOf('re'), registrar);
command = 'archive';
break;
case 'plemara': case 'reply': case 'media': case 'mediareply':
case 'random': case 'randomreply': case 'randommedia': case 'randommediareply':
args.push(matrixClient, room, userInput, registrar, {
isReply: !!~command.indexOf('reply'),
hasMedia: !!~command.indexOf('media'),
hasSubject: !!~command.indexOf('random'),
});
command = 'media';
break;
case 'proxy':
try {
const url = new URL(userInput);
command = registrar.config.invidious.domains.includes(url.hostname)
? 'invidious'
: registrar.config.nitter.domains.includes(url.hostname)
? 'nitter'
: 'proxy';
} catch(e) {}
//fallthrough
default:
args.push(matrixClient, room, userInput, registrar);
}
registrar[command] && registrar[command].runQuery.apply(null, args);
}
});
matrixClient.startClient();
module.exports = matrixClient;
};

6
matrix_auth.json Normal file
View file

@ -0,0 +1,6 @@
{
"user_id": "",
"access_token": "",
"home_server": "",
"device_id": ""
}

View file

@ -1,6 +1,6 @@
{ {
"name": "plemara", "name": "ligh7hau5",
"version": "0.3.0", "version": "0.4.0",
"description": "A Matrix to Fediverse client", "description": "A Matrix to Fediverse client",
"main": "main.js", "main": "main.js",
"scripts": { "scripts": {
@ -9,14 +9,14 @@
}, },
"repository": { "repository": {
"type": "git", "type": "git",
"url": "git+https://github.com/vulet/plemara.git" "url": "git+https://github.com/vulet/ligh7hau5.git"
}, },
"author": "vul", "author": "vul",
"license": "AGPL-3.0-only", "license": "AGPL-3.0-only",
"bugs": { "bugs": {
"url": "https://github.com/vulet/plemara/issues" "url": "https://github.com/vulet/lighthau5/issues"
}, },
"homepage": "https://github.com/vulet/plemara#readme", "homepage": "https://github.com/vulet/lighthau5#readme",
"dependencies": { "dependencies": {
"axios": "^0.19.2", "axios": "^0.19.2",
"file-system": "^2.2.2", "file-system": "^2.2.2",

View file

@ -1,5 +1,7 @@
module.exports = { module.exports = {
config: require('./config.js'), config: require('./config.js'),
fediverse_auth: require('./fediverse_auth.json'),
matrix_auth: require('./matrix_auth.json'),
archive: require('./commands/archive.js'), archive: require('./commands/archive.js'),
invidious: require('./commands/invidious.js'), invidious: require('./commands/invidious.js'),
nitter: require('./commands/nitter.js'), nitter: require('./commands/nitter.js'),