Improve the mock-idp to include in respnse : InResponseTo

References #4
This commit is contained in:
Orlando M Guerreiro 2025-05-27 11:11:39 +01:00
parent f17ae330d3
commit f548a0b31e

View file

@ -7,6 +7,7 @@ const path = require('path');
const bodyParser = require('body-parser');
const zlib = require('zlib');
const xml2js = require('xml2js');
const xpath = require('xpath');
const app = express();
const port = 3000;
@ -34,12 +35,15 @@ app.get('/saml/sso', async (req, res) => {
storedSamlRequest = samlRequest;
storedRelayState = relayState;
const authnRequestId = await extractAuthnRequestId(storedSamlRequest);
console.log('SAML2 AuthnRequestId:', authnRequestId);
// Show simple login form
res.send(createLoginForm());
});
// Step 2: Handle login form and send SAMLResponse
app.post('/saml/login', (req, res) => {
app.post('/saml/login', async (req, res) => {
const { username, email, orgCode, securityGroup, roles } = req.body;
if (!storedSamlRequest) {
return res.status(400).send('No SAMLRequest stored');
@ -60,7 +64,8 @@ app.post('/saml/login', (req, res) => {
console.log('ServiceProvider URL :', serviceProviderUrl);
console.log('ServiceProvider ACS :', acsUrl);
const samlResponse = createFakeSamlResponse(username, email, orgCode, securityGroup, roles, serviceProviderUrl, issuerUrl);
const inResponseTo = await extractAuthnRequestId(storedSamlRequest);
const samlResponse = createFakeSamlResponse(username, email, orgCode, securityGroup, roles, serviceProviderUrl, issuerUrl, inResponseTo);
const relayState = storedRelayState;
res.send(`
@ -75,7 +80,41 @@ app.post('/saml/login', (req, res) => {
`);
});
function createFakeSamlResponse(nameId, email, orgCode, securityGroupCode, roles, serviceProviderUrl, issuerUrl) {
/**
* Extracts the AuthnRequest ID from a base64-encoded SAMLRequest.
* @param {string} samlRequest - Base64-encoded SAMLRequest parameter from the URL.
* @returns {Promise<string|null>} - Promise resolving to the AuthnRequest ID, or null if not found.
*/
function extractAuthnRequestId(samlRequest) {
return new Promise((resolve, reject) => {
try {
const decoded = Buffer.from(samlRequest, 'base64');
zlib.inflateRaw(decoded, (err, inflated) => {
if (err) return reject(new Error('Failed to inflate SAMLRequest: ' + err.message));
const xml = inflated.toString('utf8');
const doc = new DOMParser().parseFromString(xml, 'text/xml');
// Define namespace mappings for XPath
const select = xpath.useNamespaces({
saml2p: 'urn:oasis:names:tc:SAML:2.0:protocol',
});
const node = select('//saml2p:AuthnRequest', doc)[0];
if (node) {
resolve(node.getAttribute('ID'));
} else {
resolve(null);
}
});
} catch (error) {
reject(error);
}
});
}
function createFakeSamlResponse(nameId, email, orgCode, securityGroupCode, roles, serviceProviderUrl, issuerUrl, inResponseTo) {
const issuer = issuerUrl;
// const audience = "https://localhost:8443/saml2/service-provider-metadata/mock-idp";
const audience = serviceProviderUrl + "/saml2/service-provider-metadata/mock-idp";
@ -152,7 +191,7 @@ function createFakeSamlResponse(nameId, email, orgCode, securityGroupCode, roles
});
const signedAssertion = sig.getSignedXml();
const samlResponse = `<?xml version="1.0" encoding="UTF-8"?><samlp:Response xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" ID="${responseId}" Version="2.0" IssueInstant="${now}" Destination="${destination}"><saml:Issuer>${issuer}</saml:Issuer><samlp:Status><samlp:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Success"/></samlp:Status>${signedAssertion}</samlp:Response>`;
const samlResponse = `<?xml version="1.0" encoding="UTF-8"?><samlp:Response xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" ID="${responseId}" Version="2.0" IssueInstant="${now}" Destination="${destination}" InResponseTo="${inResponseTo}"><saml:Issuer>${issuer}</saml:Issuer><samlp:Status><samlp:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Success"/></samlp:Status>${signedAssertion}</samlp:Response>`;
const samlResponseToUse = samlResponse.trim().replace(/^\uFEFF/, '');
return Buffer.from(samlResponseToUse, 'utf-8').toString('base64');