Skip to content

Commit

Permalink
Enable parsing saml requests.
Browse files Browse the repository at this point in the history
  • Loading branch information
wparad committed Mar 12, 2024
1 parent de40189 commit 56a5779
Show file tree
Hide file tree
Showing 7 changed files with 67 additions and 4 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ jobs:
- name: Set up Node.js
uses: actions/setup-node@v1
with:
node-version: 12
node-version: 16
registry-url: 'https://registry.npmjs.org'

- name: Install packages
Expand Down
1 change: 1 addition & 0 deletions .nvmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
16
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,6 @@
"typescript": "^4.2.4"
},
"engines": {
"node": ">= 12"
"node": ">= 16"
}
}
18 changes: 17 additions & 1 deletion src/saml.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ import {
AuthenticationOptions,
DelegationOptions,
ValidationOptions,
AuthenticationResponseMetadata
AuthenticationResponseMetadata,
SamlRequestMetadata
} from "./types";
import { assertRequired, signXmlResponse } from "./utility";
import {
Expand All @@ -29,6 +30,7 @@ import { certToPEM, generateUniqueId, keyToPEM } from "./crypto";
import { dateStringToTimestamp } from "./datetime";

const deflateRaw = util.promisify(zlib.deflateRaw);
const inflateRaw = util.promisify(zlib.inflateRaw);

// async function processValidlySignedSamlLogout(
// doc: XMLOutput,
Expand Down Expand Up @@ -227,6 +229,20 @@ class SamlLogin {
});
}

public async parseSamlRequestMetadata(samlEncodedBody: string): Promise<SamlRequestMetadata> {
const container = new URLSearchParams(samlEncodedBody);
const buffer = Buffer.from(container.get('SAMLRequest') as string, "base64");
const xmlBuffer = await inflateRaw(buffer);
// eslint-disable-next-line no-console
const parsedResult = await parseXml2JsFromString(xmlBuffer.toString());
return {
requestedIssuerEntityId: parsedResult.AuthnRequest.$.Destination,
applicationAssertionConsumerServiceUrl: parsedResult.AuthnRequest.$.AssertionConsumerServiceURL,
requestTimestap: new Date(parsedResult.AuthnRequest.$.IssueInstant),
applicationEntityId: parsedResult.AuthnRequest.Issuer[0]._
};
}

public async getSamlAssertionMetadata(samlEncodedBody: string) : Promise<AuthenticationResponseMetadata> {
const container = querystring.decode(samlEncodedBody);
const xml = Buffer.from(container.SAMLResponse as string, "base64").toString("utf8");
Expand Down
7 changes: 7 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,13 @@ export interface AuthenticationResponseMetadata {
authenticationRequestId: string;
}

export interface SamlRequestMetadata {
requestedIssuerEntityId: string;
applicationAssertionConsumerServiceUrl: string;
requestTimestap?: Date;
applicationEntityId: string;
}

export interface DelegationOptions {
/** Your platforms IdP Entity ID or URL */
issuerEntityId: string;
Expand Down
37 changes: 37 additions & 0 deletions test/saml.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { expect } from 'chai';
import SamlLogin from '../src/saml';

describe('saml.ts', () => {
it('parseSamlRequest', async () => {
const date = new Date();
const identityProviderIssuerUrl = "https://Account-Id.login.authress.io/api/authentication/saml?appId=applicationId";
const requestingApplicationUrl = "https://api.staging.application.com/api/saml/authress/callback";
const applicationEntityId = 'https://applicationIdentityId';

const samlRequestUrl = await new SamlLogin().generateAuthenticationUrl({
providerSingleSignOnUrl: identityProviderIssuerUrl,
requestTimestamp: date,
applicationEntityId: applicationEntityId,
applicationCallbackAssertionConsumerServiceUrl: requestingApplicationUrl,
allowCreate: true
});

const samlRequest = new URL(samlRequestUrl).search.toString();
const result = await new SamlLogin().parseSamlRequestMetadata(samlRequest);
const expectedResult = {
requestedIssuerEntityId: identityProviderIssuerUrl,
applicationEntityId: applicationEntityId,
applicationAssertionConsumerServiceUrl: requestingApplicationUrl,
requestTimestap: date
};
expect(result).to.eql(expectedResult);
});

it('correctly parses valid saml request', async () => {
const samlRequestString = 'fVNNb+IwEL33V6DcyRcJtBZEYmE/kChEkO5hLyvjDMTaxM56nJb++7UTaOmqii9Wxm/evHkzmSKtyprMG12IHfxtAPXdwJxzVQok7ePMaZQgkiJHImgFSDQj+/njmoSuT2oltWSydP5L68+iiKA0l6JLWy1nznbzdb39vtr8fphEcRwf/VEwio8smozvgzwMfD9gEDP/cJ+PJ34IftSl/gSFhmfmGNoukir5zHNQG1N15jwCFsWlCmIDK4GaCm3wfhgN/dEwCLMgIFFIgvGvDrc0JnBBdUtbaF0j8TzK2DCUnLHzqICHc87dUp64cKlxTgGiy6VHa+7ZbxCaszbfs1a8yWqN+sJFzsWp359DB0LyI8vSYbrdZx3J/OrbQgpsKlB7UM+cwdNufSO15q5p0qg7uZVt32WyasVZNd5VscdoWR4o++MkLffUvpLWJJX0c33gmXq3ie9UNbETWC1TWXL22sbt+SZVRXV/+zbC8+GxhZLajhi1cdV5Y5mXpXxZKKDazFirBpyB96H2ZZkhb1fb2KXhrAcLWdVUcbSThTNl+tL7e/+38EVp9nQHx6R3lRlhFmfCqblepMrtqIGZ2pmiAmup9MWjT8k71V6P7OTu+nz7nyb/AA==';

const samlRequest = new URLSearchParams({ SAMLRequest: samlRequestString });
const result = await new SamlLogin().parseSamlRequestMetadata(samlRequest.toString());
expect(result).not.to.eql(null);
})
});
4 changes: 3 additions & 1 deletion test/xml.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { expect } from 'chai';
import { parseDomFromString, parseXml2JsFromString, xpath } from "../src/xml";

it('issuer validation test', async () => {
describe("xml.ts", () => {
it('issuer validation test', async () => {
const xml = `<samlp:Response xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" ID="_8e8dc5f69a98cc4c1ff3427e5ce34606fd672f91e6" Version="2.0" IssueInstant="2014-07-17T01:01:48Z" Destination="http://sp.example.com/demo1/index.php?acs" InResponseTo="AUTHRESS_4fee3b046395c4e751011e97f8900b5273d56685">
<saml:Issuer>http://idp.example.com/metadata.php</saml:Issuer>
<samlp:Status>
Expand Down Expand Up @@ -48,4 +49,5 @@ it('issuer validation test', async () => {
const issuersXml = xpath.selectElements(doc, "/*[local-name()='Response']/*[local-name()='Issuer']");
const issuerResult = await parseXml2JsFromString(issuersXml.toString());
expect(issuerResult.Issuer._).to.eql('http://idp.example.com/metadata.php');
});
});

0 comments on commit 56a5779

Please sign in to comment.