fase(17): licensing module with RSA validation
This commit is contained in:
77
dist/modules/licensing/infrastructure/validators/RSALicenseValidator.js
vendored
Normal file
77
dist/modules/licensing/infrastructure/validators/RSALicenseValidator.js
vendored
Normal file
@@ -0,0 +1,77 @@
|
||||
"use strict";
|
||||
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.RSALicenseValidator = void 0;
|
||||
const crypto_1 = __importDefault(require("crypto"));
|
||||
const Result_1 = require("../../../../shared/domain/Result");
|
||||
const UniqueId_1 = require("../../../../shared/domain/UniqueId");
|
||||
const License_1 = require("../../domain/entities/License");
|
||||
const LicensePlan_1 = require("../../domain/value-objects/LicensePlan");
|
||||
// Public key used to verify license signatures.
|
||||
// The corresponding private key is kept secret (used only by generate-license.ts).
|
||||
const PUBLIC_KEY = `-----BEGIN PUBLIC KEY-----
|
||||
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2a2rwplBQLzHPZe5TNJF
|
||||
EhkFwUEkMvbzXuRSxW98hGxMgrHPKGLJgNw0qFsLQmhDSmVvnrwYE2vCy2Dgm7Qj
|
||||
7WKFqbZFkVDe8cROZ9K7rQmn0BqckmJbkm2SJnzYL9e9z6b5R8r5w2r5Q2HZFN7
|
||||
6B3dKCHWHxhyE3N8MCJSN7qBZ7kX8fJqBwBxQL6bZbGP2O5bXrZpFw3xKyGJ5t
|
||||
vZ9eTuD4JhKJbZbGJ3Q5Q5nNbm3nXY5z9WbBxFbRLYGJbQ7E8mSYnKJZkJzYM
|
||||
TmOxJbKtJz5mJ9Q7rBxBxLYGJmQtZmKtXZ5t9WbBxFbRLYGJbQ7E8mSYnKJZk
|
||||
JwIDAQAB
|
||||
-----END PUBLIC KEY-----`;
|
||||
class RSALicenseValidator {
|
||||
constructor(publicKeyPem) {
|
||||
const pem = publicKeyPem ?? PUBLIC_KEY;
|
||||
this.publicKey = crypto_1.default.createPublicKey(pem);
|
||||
}
|
||||
async validate(licenseKey) {
|
||||
try {
|
||||
// License key format: base64(payload_json).base64(signature)
|
||||
const parts = licenseKey.trim().split('.');
|
||||
if (parts.length !== 2) {
|
||||
return (0, Result_1.Err)('Invalid license key format');
|
||||
}
|
||||
const [payloadB64, signatureB64] = parts;
|
||||
let payloadJson;
|
||||
let rawPayload;
|
||||
try {
|
||||
payloadJson = Buffer.from(payloadB64, 'base64').toString('utf-8');
|
||||
rawPayload = JSON.parse(payloadJson);
|
||||
}
|
||||
catch {
|
||||
return (0, Result_1.Err)('Invalid license key: cannot decode payload');
|
||||
}
|
||||
const signature = Buffer.from(signatureB64, 'base64');
|
||||
const isValid = crypto_1.default.verify('sha256', Buffer.from(payloadJson, 'utf-8'), this.publicKey, signature);
|
||||
if (!isValid) {
|
||||
return (0, Result_1.Err)('Invalid license key: signature verification failed');
|
||||
}
|
||||
let plan;
|
||||
try {
|
||||
plan = LicensePlan_1.LicensePlan.fromString(rawPayload.plan);
|
||||
}
|
||||
catch {
|
||||
return (0, Result_1.Err)(`Invalid plan in license: ${rawPayload.plan}`);
|
||||
}
|
||||
const expiresAt = rawPayload.expiresAt ? new Date(rawPayload.expiresAt) : null;
|
||||
if (expiresAt && expiresAt < new Date()) {
|
||||
return (0, Result_1.Err)('License has expired');
|
||||
}
|
||||
const license = License_1.License.reconstitute({
|
||||
plan,
|
||||
organizationName: rawPayload.organizationName,
|
||||
email: rawPayload.email,
|
||||
issuedAt: new Date(rawPayload.issuedAt),
|
||||
expiresAt,
|
||||
signature: signatureB64,
|
||||
rawKey: licenseKey,
|
||||
}, UniqueId_1.UniqueId.create());
|
||||
return (0, Result_1.Ok)(license);
|
||||
}
|
||||
catch (err) {
|
||||
return (0, Result_1.Err)(`License validation error: ${String(err)}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
exports.RSALicenseValidator = RSALicenseValidator;
|
||||
Reference in New Issue
Block a user