dev: allow app tokens to identify user sessions

This commit is contained in:
KernelDeimos 2024-10-24 19:51:54 -04:00
parent bc51d4bd52
commit 6b8fbda14c
3 changed files with 79 additions and 0 deletions

View File

@ -22,6 +22,7 @@ const { get_user, get_app } = require("../../helpers");
const { Context } = require("../../util/context");
const APIError = require("../../api/APIError");
const { DB_WRITE } = require("../database/consts");
const { UUIDFPE } = require("../../util/uuidfpe");
const APP_ORIGIN_UUID_NAMESPACE = '33de3768-8ee0-43e9-9e73-db192b97a5d8';
@ -30,6 +31,7 @@ const LegacyTokenError = class extends Error {};
class AuthService extends BaseService {
static MODULES = {
jwt: require('jsonwebtoken'),
crypto: require('crypto'),
uuidv5: require('uuid').v5,
uuidv4: require('uuid').v4,
}
@ -38,6 +40,11 @@ class AuthService extends BaseService {
this.db = await this.services.get('database').get(DB_WRITE, 'auth');
this.svc_session = await this.services.get('session');
const uuid_fpe_key = this.config.uuid_fpe_key
? UUIDFPE.uuidToBuffer(this.config.uuid_fpe_key)
: this.modules.crypto.randomBytes(16);
this.uuid_fpe = new UUIDFPE(uuid_fpe_key);
this.sessions = {};
const svc_token = await this.services.get('token');
@ -78,6 +85,12 @@ class AuthService extends BaseService {
}
if ( decoded.type === 'app-under-user' ) {
const session_uuid = this.uuid_fpe.decrypt(decoded.session);
const session = await this.get_session_(session_uuid);
if ( ! session ) {
throw APIError.create('token_auth_failed');
}
const user = await get_user({ uuid: decoded.user_uid });
if ( ! user ) {
throw APIError.create('token_auth_failed');
@ -164,6 +177,7 @@ class AuthService extends BaseService {
version: '0.0.0',
user_uid: actor_type.user.uuid,
app_uid,
session: this.uuid_fpe.encrypt(actor_type.session),
},
this.global_config.jwt_secret,
);

View File

@ -81,6 +81,10 @@ const compression = {
short: 'u',
...uuid_compression(),
},
session: {
short: 's',
...uuid_compression(),
},
version: 'v',
type: {
short: 't',

View File

@ -0,0 +1,61 @@
const crypto = require('crypto');
class UUIDFPE {
static ALGORITHM = 'aes-128-ecb';
constructor(key) {
if ( !key || key.length !== 16 ) {
throw new Error('Key must be a 16-byte Buffer.');
}
this.key = key;
}
static uuidToBuffer (uuidStr) {
const hexStr = uuidStr.replace(/-/g, '');
return Buffer.from(hexStr, 'hex');
}
static bufferToUuid (buffer) {
const hexStr = buffer.toString('hex');
return [
hexStr.substring(0, 8),
hexStr.substring(8, 12),
hexStr.substring(12, 16),
hexStr.substring(16, 20),
hexStr.substring(20)
].join('-');
}
encrypt(uuidStr) {
const plaintext = this.constructor.uuidToBuffer(uuidStr);
const cipher = crypto.createCipheriv(
this.constructor.ALGORITHM,
this.key,
null,
);
cipher.setAutoPadding(false);
const encrypted = Buffer.concat([
cipher.update(plaintext),
cipher.final(),
]);
return this.constructor.bufferToUuid(encrypted);
}
decrypt(encryptedUuidStr) {
const encrypted = this.constructor.uuidToBuffer(encryptedUuidStr);
const decipher = crypto.createDecipheriv(
this.constructor.ALGORITHM,
this.key,
null,
);
decipher.setAutoPadding(false);
const decrypted = Buffer.concat([decipher.update(encrypted), decipher.final()]);
return this.constructor.bufferToUuid(decrypted);
}
}
module.exports = {
UUIDFPE,
};