Added custom InMemory SAML2 Authentication Request Repository
Added attributes mapping in application*.yml config Added attributes default values in application*.yml config Corrected MOCK-IDP to return InResponseTo and complex attribute names
This commit is contained in:
parent
f548a0b31e
commit
eb3a621b17
7 changed files with 458 additions and 75 deletions
|
@ -132,6 +132,91 @@ function createFakeSamlResponse(nameId, email, orgCode, securityGroupCode, roles
|
|||
console.log('SAML Destination :', destination);
|
||||
console.log('SAML Issuer :', issuer);
|
||||
|
||||
const assertion = `
|
||||
<saml:Assertion
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:xs="http://www.w3.org/2001/XMLSchema"
|
||||
xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion"
|
||||
ID="${assertionId}"
|
||||
IssueInstant="${now}"
|
||||
Version="2.0">
|
||||
<saml:Issuer>${issuer}</saml:Issuer>
|
||||
<saml:Subject>
|
||||
<saml:NameID>${nameId}</saml:NameID>
|
||||
<saml:SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer">
|
||||
<saml:SubjectConfirmationData NotOnOrAfter="${fiveMinutesLater}" Recipient="${recipient}" />
|
||||
</saml:SubjectConfirmation>
|
||||
</saml:Subject>
|
||||
<saml:Conditions NotBefore="${now}" NotOnOrAfter="${fiveMinutesLater}">
|
||||
<saml:AudienceRestriction>
|
||||
<saml:Audience>${audience}</saml:Audience>
|
||||
</saml:AudienceRestriction>
|
||||
</saml:Conditions>
|
||||
<saml:AuthnStatement AuthnInstant="${now}">
|
||||
<saml:AuthnContext>
|
||||
<saml:AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport</saml:AuthnContextClassRef>
|
||||
</saml:AuthnContext>
|
||||
</saml:AuthnStatement>
|
||||
<saml:AttributeStatement>
|
||||
<saml:Attribute Name="email">
|
||||
<saml:AttributeValue>${email}</saml:AttributeValue>
|
||||
</saml:Attribute>
|
||||
<saml:Attribute Name="organization_code">
|
||||
<saml:AttributeValue>${orgCode}</saml:AttributeValue>
|
||||
</saml:Attribute>
|
||||
<saml:Attribute Name="security_group">
|
||||
<saml:AttributeValue>${securityGroupCode}</saml:AttributeValue>
|
||||
</saml:Attribute>
|
||||
<saml:Attribute Name="roles">
|
||||
<saml:AttributeValue>${roles}</saml:AttributeValue>
|
||||
</saml:Attribute>
|
||||
<saml:Attribute Name="urn:mace:dir:attribute-def:displayName" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri">
|
||||
<saml:AttributeValue xsi:type="xs:string">Pedro Reis</saml:AttributeValue>
|
||||
</saml:Attribute>
|
||||
<saml:Attribute Name="urn:oid:2.16.840.1.113730.3.1.241" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri">
|
||||
<saml:AttributeValue xsi:type="xs:string">Pedro Reis</saml:AttributeValue>
|
||||
</saml:Attribute>
|
||||
<saml:Attribute Name="urn:mace:dir:attribute-def:mail" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri">
|
||||
<saml:AttributeValue xsi:type="xs:string">pedro.reis@unl.pt</saml:AttributeValue>
|
||||
</saml:Attribute>
|
||||
<saml:Attribute Name="urn:oid:0.9.2342.19200300.100.1.3" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri">
|
||||
<saml:AttributeValue xsi:type="xs:string">pedro.reis@unl.pt</saml:AttributeValue>
|
||||
</saml:Attribute>
|
||||
</saml:AttributeStatement>
|
||||
</saml:Assertion>`;
|
||||
|
||||
const privateKey = fs.readFileSync(path.join(__dirname, 'certs', 'idp-private.key'), 'utf8');
|
||||
const certificate = fs.readFileSync(path.join(__dirname, 'certs', 'idp-public.cert'), 'utf8');
|
||||
const certBase64 = certificate
|
||||
.replace(/-----BEGIN CERTIFICATE-----/, '')
|
||||
.replace(/-----END CERTIFICATE-----/, '')
|
||||
.replace(/\r?\n|\r/g, '');
|
||||
|
||||
const doc = new DOMParser().parseFromString(assertion);
|
||||
const sig = new SignedXml();
|
||||
sig.prefix = 'ds';
|
||||
sig.addReference("//*[local-name(.)='Assertion']", [
|
||||
"http://www.w3.org/2000/09/xmldsig#enveloped-signature",
|
||||
"http://www.w3.org/2001/10/xml-exc-c14n#",
|
||||
], "http://www.w3.org/2000/09/xmldsig#sha1");
|
||||
sig.signingKey = privateKey;
|
||||
sig.keyInfoProvider = {
|
||||
getKeyInfo: () => `<ds:X509Data><ds:X509Certificate>${certBase64}</ds:X509Certificate></ds:X509Data>`
|
||||
};
|
||||
sig.computeSignature(assertion, {
|
||||
prefix: 'ds',
|
||||
location: { reference: "//*[local-name(.)='Issuer']", action: 'after' }
|
||||
});
|
||||
|
||||
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}" 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');
|
||||
}
|
||||
|
||||
// OLD assertion string. This contains also some extra attributes
|
||||
function assertionOLD() {
|
||||
const assertion = `
|
||||
<saml:Assertion xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" ID="${assertionId}" IssueInstant="${now}" Version="2.0">
|
||||
<saml:Issuer>${issuer}</saml:Issuer>
|
||||
|
@ -166,35 +251,6 @@ function createFakeSamlResponse(nameId, email, orgCode, securityGroupCode, roles
|
|||
</saml:Attribute>
|
||||
</saml:AttributeStatement>
|
||||
</saml:Assertion>`;
|
||||
|
||||
const privateKey = fs.readFileSync(path.join(__dirname, 'certs', 'idp-private.key'), 'utf8');
|
||||
const certificate = fs.readFileSync(path.join(__dirname, 'certs', 'idp-public.cert'), 'utf8');
|
||||
const certBase64 = certificate
|
||||
.replace(/-----BEGIN CERTIFICATE-----/, '')
|
||||
.replace(/-----END CERTIFICATE-----/, '')
|
||||
.replace(/\r?\n|\r/g, '');
|
||||
|
||||
const doc = new DOMParser().parseFromString(assertion);
|
||||
const sig = new SignedXml();
|
||||
sig.prefix = 'ds';
|
||||
sig.addReference("//*[local-name(.)='Assertion']", [
|
||||
"http://www.w3.org/2000/09/xmldsig#enveloped-signature",
|
||||
"http://www.w3.org/2001/10/xml-exc-c14n#",
|
||||
], "http://www.w3.org/2000/09/xmldsig#sha1");
|
||||
sig.signingKey = privateKey;
|
||||
sig.keyInfoProvider = {
|
||||
getKeyInfo: () => `<ds:X509Data><ds:X509Certificate>${certBase64}</ds:X509Certificate></ds:X509Data>`
|
||||
};
|
||||
sig.computeSignature(assertion, {
|
||||
prefix: 'ds',
|
||||
location: { reference: "//*[local-name(.)='Issuer']", action: 'after' }
|
||||
});
|
||||
|
||||
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}" 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');
|
||||
}
|
||||
|
||||
function createLoginFormOLD() {
|
||||
|
@ -350,5 +406,11 @@ function extractIssuerFromSAMLRequest(samlRequestBase64) {
|
|||
}
|
||||
|
||||
app.listen(port, () => {
|
||||
const now = new Date();
|
||||
const localISOTime = now.toLocaleString('pt-PT'); // ISO-like, local time
|
||||
|
||||
const nowUTC = new Date().toISOString();
|
||||
|
||||
console.log(`Mock IDP running at http://localhost:${port}`);
|
||||
console.log(`Started at ${localISOTime} (UTC is ${now})`);
|
||||
});
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue