@openinc/parse-server-opendash
Version:
Parse Server Cloud Code for open.INC Stack.
102 lines (101 loc) • 4.66 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.init = init;
const crypto_1 = require("crypto");
const jsonwebtoken_1 = __importDefault(require("jsonwebtoken"));
const jwks_rsa_1 = __importDefault(require("jwks-rsa"));
const config_1 = require("../features/config");
const types_1 = require("../types");
const tenantId = config_1.ConfigInstance.getInstance().get("MICROSOFT_TENANT_ID") ||
process.env.MICROSOFT_TENANT_ID ||
process.env.OI_MICROSOFT_TENANT_ID;
const appId = config_1.ConfigInstance.getInstance().get("MICROSOFT_APP_ID") ||
process.env.MICROSOFT_APP_ID ||
process.env.OI_MICROSOFT_APP_ID;
// Simple, standard Microsoft v2.0 endpoint with app-specific parameter
const client = (0, jwks_rsa_1.default)({
jwksUri: `https://login.microsoftonline.com/${tenantId}/discovery/keys?appid=${appId}`,
cache: true,
cacheMaxAge: 12 * 60 * 60 * 1000, // 12 hours
timeout: 30000,
});
function getKey(header, callback) {
if (!header.kid) {
return callback(new Error("No kid found in token header"));
}
client.getSigningKey(header.kid, (err, key) => {
if (err)
return callback(err);
if (!key)
return callback(new Error("Key not found"));
callback(null, key.getPublicKey());
});
}
async function init(name) {
Parse.Cloud.define(name, async (request) => {
const token = request.params.token;
const account = request.params.account;
console.log(JSON.stringify(request.params));
console.log("Account: ", JSON.stringify(account));
if (!token) {
throw new Parse.Error(Parse.Error.INVALID_JSON, "Token missing");
}
if (!tenantId || !appId) {
throw new Parse.Error(Parse.Error.INVALID_JSON, "Microsoft authentication not properly configured");
}
const verifiedPayload = await new Promise((resolve, reject) => {
jsonwebtoken_1.default.verify(token, getKey, {
audience: appId,
issuer: `https://login.microsoftonline.com/${tenantId}/v2.0`,
algorithms: ["RS256"],
}, (err, decoded) => {
if (err) {
console.error("JWT verification failed:", err.message);
return reject(err);
}
resolve(decoded);
});
});
console.log("Payload: ", JSON.stringify(verifiedPayload));
const defaultTenant = await new Parse.Query(types_1.Tenant)
.ascending("createdAt")
.first({ useMasterKey: true });
let user = (await new Parse.Query(Parse.User)
.equalTo("microsoftId", verifiedPayload.oid)
.first({ useMasterKey: true }));
// Legacy fallback: some older accounts might have been created using the oid as username.
let oldUser = (await new Parse.Query(Parse.User)
.equalTo("username", verifiedPayload.oid)
.first({ useMasterKey: true }));
if (!user && !oldUser) {
user = new Parse.User();
user.set("password", (0, crypto_1.randomBytes)(16).toString("hex"));
user.set("microsoftId", verifiedPayload.oid);
user.set("name", verifiedPayload.name || verifiedPayload.preferred_username);
user.set("tenant", defaultTenant);
user = await user.signUp(null, { useMasterKey: true });
return user.getSessionToken();
}
else if (!user && oldUser) {
// Migrate legacy account that used the oid as username to a modern record keyed by microsoftId.
user = oldUser;
user.set("microsoftId", verifiedPayload.oid);
user = await user.save(null, { useMasterKey: true });
}
// Update user info on each login
user.set("username", verifiedPayload.name ??
verifiedPayload.preferred_username ??
account.username);
user.set("email", verifiedPayload.email ?? verifiedPayload.preferred_username);
const sessionToken = "r:" + (0, crypto_1.randomBytes)(16).toString("hex");
const session = new Parse.Object("_Session");
session.set("user", user);
session.set("sessionToken", sessionToken);
session.set("expiresAt", new Date(Date.now() + 24 * 60 * 60 * 1000 * 7));
const savedSession = await session.save(null, { useMasterKey: true });
return savedSession.get("sessionToken");
});
}