@m-ld/gateway
Version:
m-ld gateway for services
86 lines • 9.12 kB
JavaScript
import { Server as SocketIoServer } from 'socket.io';
import { IoRemotesService } from '@m-ld/m-ld/ext/socket.io-server';
import { JwtAuthorization, KeyAuthorization } from '../server/Authorization.js';
import { AccountOwnedId, as, asUuid, validate } from '../lib/index.js';
import { BadRequestError, ForbiddenError, InternalServerError, NotFoundError, toHttpError } from '../http/errors.js';
import LOG from 'loglevel';
import { Account } from '../server/index.js';
const asHandshakeAuth = as.object({
user: AccountOwnedId.asComponentId.optional(),
key: as.string(),
jwt: as.string()
});
const asHandshakeQuery = as.object({
'@domain': as.string().domain().required()
}).unknown();
export class IoService extends IoRemotesService {
constructor(gateway, server) {
const io = new SocketIoServer(server, {
cors: { origin: '*', methods: ['GET', 'POST'] }
});
super(io.sockets);
this.resolveLive = new Map;
// Attach authorisation
io.use(async (socket, next) => {
try {
const { user, key, jwt } = validate(socket.handshake.auth, asHandshakeAuth);
const { '@domain': domainName } = validate(socket.handshake.query, asHandshakeQuery);
if (!domainName.endsWith(gateway.domainName))
return next(new BadRequestError('Bad gateway'));
// The gateway is connecting to a domain: its own, or a subdomain
if (key === gateway.me.authKey.toString())
return next();
const sdId = AccountOwnedId.fromDomain(domainName).validate();
const access = { id: sdId, forWrite: 'Subdomain' };
const remotesAuth = await Account.getDetails(gateway.domain, sdId.account, 'remotesAuth');
if (!user && !key && !jwt) {
validate(sdId.name, asUuid); // Anonymous messaging requires UUID subdomain
if (!remotesAuth.includes('anon'))
return next(new ForbiddenError('Anonymous messaging unavailable'));
}
else {
if (user && key) {
// key authentication is the default if nothing else is specified
if (remotesAuth.length && !remotesAuth.includes('key'))
return next(new ForbiddenError('Key-authenticated messaging unavailable'));
await new KeyAuthorization(user, key).verifyUser(gateway, access);
}
else if (jwt) {
if (!remotesAuth.includes('jwt'))
return next(new ForbiddenError('JWT-authenticated messaging unavailable'));
await new JwtAuthorization(jwt).verifyUser(gateway, access);
}
else {
return next(new ForbiddenError('Unrecognised authentication'));
}
// Ensure named subdomain is in the cache and live
if (!gateway.hasClonedSubdomain(sdId)) {
const [sdc] = await Promise.all([
gateway.getSubdomain(sdId),
new Promise(resolve => this.resolveLive.set(sdId.toDomain(), resolve))
]);
if (sdc == null)
return next(new NotFoundError);
}
}
LOG.debug('IO authorised for', user || 'anonymous', 'in', domainName);
return next();
}
catch (e) {
const httpError = toHttpError(e);
if (httpError instanceof InternalServerError)
LOG.error('IO authorisation failed for', socket.handshake, e);
return next(httpError);
}
});
}
async getPresent(domain) {
const present = await super.getPresent(domain);
if (present.length > 0) {
this.resolveLive.get(domain)?.();
this.resolveLive.delete(domain);
}
return present;
}
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiSW9TZXJ2aWNlLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL3NvY2tldC5pby9Jb1NlcnZpY2UudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLE1BQU0sSUFBSSxjQUFjLEVBQUUsTUFBTSxXQUFXLENBQUM7QUFDckQsT0FBTyxFQUFFLGdCQUFnQixFQUFFLE1BQU0saUNBQWlDLENBQUM7QUFDbkUsT0FBTyxFQUFFLGdCQUFnQixFQUFFLGdCQUFnQixFQUFFLE1BQU0sNEJBQTRCLENBQUM7QUFDaEYsT0FBTyxFQUFFLGNBQWMsRUFBRSxFQUFFLEVBQUUsTUFBTSxFQUFFLFFBQVEsRUFBRSxNQUFNLGlCQUFpQixDQUFDO0FBQ3ZFLE9BQU8sRUFDTCxlQUFlLEVBQUUsY0FBYyxFQUFFLG1CQUFtQixFQUFFLGFBQWEsRUFBRSxXQUFXLEVBQ2pGLE1BQU0sbUJBQW1CLENBQUM7QUFDM0IsT0FBTyxHQUFHLE1BQU0sVUFBVSxDQUFDO0FBQzNCLE9BQU8sRUFBRSxPQUFPLEVBQVcsTUFBTSxvQkFBb0IsQ0FBQztBQUd0RCxNQUFNLGVBQWUsR0FBRyxFQUFFLENBQUMsTUFBTSxDQUFDO0lBQ2hDLElBQUksRUFBRSxjQUFjLENBQUMsYUFBYSxDQUFDLFFBQVEsRUFBRTtJQUM3QyxHQUFHLEVBQUUsRUFBRSxDQUFDLE1BQU0sRUFBRTtJQUNoQixHQUFHLEVBQUUsRUFBRSxDQUFDLE1BQU0sRUFBRTtDQUNqQixDQUFDLENBQUM7QUFDSCxNQUFNLGdCQUFnQixHQUFHLEVBQUUsQ0FBQyxNQUFNLENBQUM7SUFDakMsU0FBUyxFQUFFLEVBQUUsQ0FBQyxNQUFNLEVBQUUsQ0FBQyxNQUFNLEVBQUUsQ0FBQyxRQUFRLEVBQUU7Q0FDM0MsQ0FBQyxDQUFDLE9BQU8sRUFBRSxDQUFDO0FBRWIsTUFBTSxPQUFPLFNBQVUsU0FBUSxnQkFBZ0I7SUFHN0MsWUFBWSxPQUFnQixFQUFFLE1BQWM7UUFDMUMsTUFBTSxFQUFFLEdBQUcs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