From f1a436fa243080f8cfcf06fd0f40ce3af396c8b6 Mon Sep 17 00:00:00 2001 From: Andrii Kostenko Date: Fri, 19 Feb 2021 19:40:46 +0200 Subject: [PATCH] Tests use typescript (#534) Co-authored-by: Chris Barth --- .eslintrc => .eslintrc.json | 2 +- .gitignore | 13 +- .mocharc.json | 9 + .prettierignore | 7 +- package-lock.json | 112 ++ package.json | 6 +- src/passport-saml/algorithms.ts | 9 +- src/passport-saml/multiSamlStrategy.ts | 7 +- src/passport-saml/saml-post-signing.ts | 8 +- src/passport-saml/saml.ts | 18 +- src/passport-saml/strategy.ts | 6 +- src/passport-saml/types.ts | 41 +- test/.eslintrc.json | 6 - test/mocha.opts | 2 - ...lStrategy.js => multiSamlStrategy.spec.ts} | 157 +- ...sts.js => saml-post-signing-tests.spec.ts} | 63 +- test/{samlTests.js => samlTests.spec.ts} | 88 +- test/{strategy.js => strategy.spec.ts} | 22 +- ...-signatures.js => test-signatures.spec.ts} | 42 +- test/{tests.js => tests.spec.ts} | 1267 +++++++++-------- tsconfig.json | 5 +- 21 files changed, 1058 insertions(+), 832 deletions(-) rename .eslintrc => .eslintrc.json (95%) create mode 100644 .mocharc.json delete mode 100644 test/.eslintrc.json delete mode 100644 test/mocha.opts rename test/{multiSamlStrategy.js => multiSamlStrategy.spec.ts} (55%) rename test/{saml-post-signing-tests.js => saml-post-signing-tests.spec.ts} (66%) rename test/{samlTests.js => samlTests.spec.ts} (69%) rename test/{strategy.js => strategy.spec.ts} (50%) rename test/{test-signatures.js => test-signatures.spec.ts} (85%) rename test/{tests.js => tests.spec.ts} (89%) diff --git a/.eslintrc b/.eslintrc.json similarity index 95% rename from .eslintrc rename to .eslintrc.json index 7dc76aad..4f44a518 100644 --- a/.eslintrc +++ b/.eslintrc.json @@ -1,6 +1,7 @@ { "env": { "node": true, + "mocha": true, "es6": false }, "root": true, @@ -9,7 +10,6 @@ "parserOptions": { "ecmaVersion": 6 }, - "extends": [ "eslint:recommended", "plugin:@typescript-eslint/eslint-recommended", diff --git a/.gitignore b/.gitignore index ba1702c5..27396352 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ -lib -node_modules/ -.tern-port -.idea -yarn-error.log -.DS_Store +lib +node_modules/ +.tern-port +.idea +yarn-error.log +.DS_Store +.eslintcache diff --git a/.mocharc.json b/.mocharc.json new file mode 100644 index 00000000..9755384b --- /dev/null +++ b/.mocharc.json @@ -0,0 +1,9 @@ +{ + "diff": true, + "extension": "spec.ts", + "package": "./package.json", + "reporter": "spec", + "require": ["choma", "ts-node/register"], + "files": "test/**/*.spec.ts", + "watch-files": "test/**/*.spec.ts" +} diff --git a/.prettierignore b/.prettierignore index 1ebe3a08..01c96ead 100644 --- a/.prettierignore +++ b/.prettierignore @@ -1,3 +1,4 @@ -# Ignore artifacts: -node_modules -lib +# Ignore artifacts: +node_modules +lib +package-lock.json diff --git a/package-lock.json b/package-lock.json index 2e5e22fa..4faa795e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -318,6 +318,12 @@ "@types/responselike": "*" } }, + "@types/caseless": { + "version": "0.12.2", + "resolved": "https://registry.npmjs.org/@types/caseless/-/caseless-0.12.2.tgz", + "integrity": "sha512-6ckxMjBBD8URvjB6J3NcnuAn5Pkl7t3TizAg+xdlzzQGSPSmBcXf8KoIH0ua/i+tio+ZRUHEXp0HEmvaR4kt0w==", + "dev": true + }, "@types/connect": { "version": "3.4.33", "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.33.tgz", @@ -399,6 +405,12 @@ "integrity": "sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA==", "dev": true }, + "@types/mocha": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-8.2.0.tgz", + "integrity": "sha512-/Sge3BymXo4lKc31C8OINJgXLaw+7vL1/L1pGiBNpGrBiT8FQiaFpSYV0uhTaG4y78vcMBTMFsWaHDvuD+xGzQ==", + "dev": true + }, "@types/node": { "version": "14.14.13", "resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.13.tgz", @@ -442,6 +454,31 @@ "integrity": "sha512-ewFXqrQHlFsgc09MK5jP5iR7vumV/BYayNC6PgJO2LPe8vrnNFyjQjSppfEngITi0qvfKtzFvgKymGheFM9UOA==", "dev": true }, + "@types/request": { + "version": "2.48.5", + "resolved": "https://registry.npmjs.org/@types/request/-/request-2.48.5.tgz", + "integrity": "sha512-/LO7xRVnL3DxJ1WkPGDQrp4VTV1reX9RkC85mJ+Qzykj2Bdw+mG15aAfDahc76HtknjzE16SX/Yddn6MxVbmGQ==", + "dev": true, + "requires": { + "@types/caseless": "*", + "@types/node": "*", + "@types/tough-cookie": "*", + "form-data": "^2.5.0" + }, + "dependencies": { + "form-data": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.1.tgz", + "integrity": "sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA==", + "dev": true, + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + } + } + } + }, "@types/responselike": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.0.tgz", @@ -461,6 +498,27 @@ "@types/node": "*" } }, + "@types/sinon": { + "version": "9.0.10", + "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-9.0.10.tgz", + "integrity": "sha512-/faDC0erR06wMdybwI/uR8wEKV/E83T0k4sepIpB7gXuy2gzx2xiOjmztq6a2Y6rIGJ04D+6UU0VBmWy+4HEMA==", + "dev": true, + "requires": { + "@types/sinonjs__fake-timers": "*" + } + }, + "@types/sinonjs__fake-timers": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-6.0.2.tgz", + "integrity": "sha512-dIPoZ3g5gcx9zZEszaxLSVTvMReD3xxyyDnQUjA6IYDG9Ba2AV0otMPs+77sG9ojB4Qr2N2Vk5RnKeuA0X/0bg==", + "dev": true + }, + "@types/tough-cookie": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.0.tgz", + "integrity": "sha512-I99sngh224D0M7XgW1s120zxCt3VYQ3IQsuw3P3jbq5GG4yc79+ZjyKznyOGIQrflfylLgcfekeZW/vk0yng6A==", + "dev": true + }, "@types/xml-crypto": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/@types/xml-crypto/-/xml-crypto-1.4.1.tgz", @@ -967,6 +1025,12 @@ "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", "dev": true }, + "buffer-from": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", + "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", + "dev": true + }, "bytes": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", @@ -1310,6 +1374,12 @@ } } }, + "create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "dev": true + }, "cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -3239,6 +3309,12 @@ } } }, + "make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true + }, "media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", @@ -8488,6 +8564,22 @@ } } }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "source-map-support": { + "version": "0.5.19", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz", + "integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==", + "dev": true, + "requires": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, "spawn-command": { "version": "0.0.2-1", "resolved": "https://registry.npmjs.org/spawn-command/-/spawn-command-0.0.2-1.tgz", @@ -8680,6 +8772,20 @@ "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", "dev": true }, + "ts-node": { + "version": "9.1.1", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-9.1.1.tgz", + "integrity": "sha512-hPlt7ZACERQGf03M253ytLY3dHbGNGrAq9qIHWUY9XHYl1z7wYngSr3OQ5xmui8o2AaxsONxIzjafLUiWBo1Fg==", + "dev": true, + "requires": { + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "source-map-support": "^0.5.17", + "yn": "3.1.1" + } + }, "tslib": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", @@ -9277,6 +9383,12 @@ } } }, + "yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true + }, "yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", diff --git a/package.json b/package.json index 18f19da3..3f57b57c 100644 --- a/package.json +++ b/package.json @@ -37,7 +37,7 @@ "scripts": { "build": "tsc", "changelog": "gren changelog --override --generate", - "lint": "eslint --ext .ts src", + "lint": "eslint --ext .ts **/*.ts --cache", "lint-watch": "onchange -k -p 100 \"**/*.ts\" -- eslint {{file}}", "lint:fix": "eslint --ext .ts --fix src", "prepare": "tsc", @@ -60,8 +60,11 @@ }, "devDependencies": { "@types/debug": "^4.1.5", + "@types/mocha": "^8.2.0", "@types/node": "^14.14.13", "@types/passport-strategy": "^0.2.35", + "@types/request": "^2.48.5", + "@types/sinon": "^9.0.10", "@types/xml-crypto": "^1.4.1", "@types/xml-encryption": "^1.2.0", "@types/xml2js": "^0.4.7", @@ -85,6 +88,7 @@ "request": "^2.83.0", "should": "*", "sinon": "^9.2.2", + "ts-node": "^9.1.1", "typescript": "^4.1.3" }, "engines": { diff --git a/src/passport-saml/algorithms.ts b/src/passport-saml/algorithms.ts index 9907cc8a..e8b28969 100644 --- a/src/passport-saml/algorithms.ts +++ b/src/passport-saml/algorithms.ts @@ -1,33 +1,36 @@ import * as crypto from "crypto"; -export function getSigningAlgorithm(shortName: string): string { +export function getSigningAlgorithm(shortName?: string): string { switch (shortName) { case "sha256": return "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"; case "sha512": return "http://www.w3.org/2001/04/xmldsig-more#rsa-sha512"; + case "sha1": default: return "http://www.w3.org/2000/09/xmldsig#rsa-sha1"; } } -export function getDigestAlgorithm(shortName: string): string { +export function getDigestAlgorithm(shortName?: string): string { switch (shortName) { case "sha256": return "http://www.w3.org/2001/04/xmlenc#sha256"; case "sha512": return "http://www.w3.org/2001/04/xmlenc#sha512"; + case "sha1": default: return "http://www.w3.org/2000/09/xmldsig#sha1"; } } -export function getSigner(shortName: string): crypto.Signer { +export function getSigner(shortName?: string): crypto.Signer { switch (shortName) { case "sha256": return crypto.createSign("RSA-SHA256"); case "sha512": return crypto.createSign("RSA-SHA512"); + case "sha1": default: return crypto.createSign("RSA-SHA1"); } diff --git a/src/passport-saml/multiSamlStrategy.ts b/src/passport-saml/multiSamlStrategy.ts index 92f9d3a6..a4151368 100644 --- a/src/passport-saml/multiSamlStrategy.ts +++ b/src/passport-saml/multiSamlStrategy.ts @@ -14,7 +14,10 @@ import { class MultiSamlStrategy extends SamlStrategy { _options: MultiSamlConfig; - constructor(options: MultiSamlConfig, verify: VerifyWithRequest | VerifyWithoutRequest) { + + constructor(options: MultiSamlConfig, verify: VerifyWithRequest); + constructor(options: MultiSamlConfig, verify: VerifyWithoutRequest); + constructor(options: MultiSamlConfig, verify: never) { if (!options || typeof options.getSamlOptions != "function") { throw new Error("Please provide a getSamlOptions function"); } @@ -33,7 +36,7 @@ class MultiSamlStrategy extends SamlStrategy { this._options = options; } - authenticate(req: RequestWithUser, options: AuthenticateOptions & AuthorizeOptions) { + authenticate(req: RequestWithUser, options: AuthenticateOptions) { this._options.getSamlOptions(req, (err, samlOptions) => { if (err) { return this.error(err); diff --git a/src/passport-saml/saml-post-signing.ts b/src/passport-saml/saml-post-signing.ts index 020be2ef..53ac67bf 100644 --- a/src/passport-saml/saml-post-signing.ts +++ b/src/passport-saml/saml-post-signing.ts @@ -1,6 +1,6 @@ import { SignedXml } from "xml-crypto"; import * as algorithms from "./algorithms"; -import { SAMLOptions } from "./types"; +import { SamlOptions, SamlSigningOptions } from "./types"; const authnRequestXPath = '/*[local-name(.)="AuthnRequest" and namespace-uri(.)="urn:oasis:names:tc:SAML:2.0:protocol"]'; @@ -11,11 +11,11 @@ const defaultTransforms = [ "http://www.w3.org/2001/10/xml-exc-c14n#", ]; -export function signSamlPost(samlMessage: string, xpath: string, options: SAMLOptions) { +export function signSamlPost(samlMessage: string, xpath: string, options: SamlSigningOptions) { if (!samlMessage) throw new Error("samlMessage is required"); if (!xpath) throw new Error("xpath is required"); if (!options) { - options = {} as SAMLOptions; + options = {} as SamlSigningOptions; } if (options.privateCert) { @@ -41,6 +41,6 @@ export function signSamlPost(samlMessage: string, xpath: string, options: SAMLOp return sig.getSignedXml(); } -export function signAuthnRequestPost(authnRequest: string, options: SAMLOptions) { +export function signAuthnRequestPost(authnRequest: string, options: SamlSigningOptions) { return signSamlPost(authnRequest, authnRequestXPath, options); } diff --git a/src/passport-saml/saml.ts b/src/passport-saml/saml.ts index 988ec80a..e2a3e206 100644 --- a/src/passport-saml/saml.ts +++ b/src/passport-saml/saml.ts @@ -24,7 +24,7 @@ import { LogoutRequestXML, Profile, RequestWithUser, - SAMLOptions, + SamlOptions, SamlIDPListConfig, SamlIDPEntryConfig, SamlScopingConfig, @@ -107,14 +107,14 @@ async function promiseWithNameID(nameid: Node): Promise { } class SAML { - options: SAMLOptions; + options: SamlOptions; cacheProvider: InMemoryCacheProvider; - constructor(options: Partial) { + constructor(options: Partial) { this.options = this.initialize(options); this.cacheProvider = this.options.cacheProvider; } - initialize(options: Partial): SAMLOptions { + initialize(options: Partial): SamlOptions { if (!options) { options = {}; } @@ -200,7 +200,7 @@ class SAML { options.authnRequestBinding = options.authnRequestBinding || "HTTP-Redirect"; - return options as SAMLOptions; + return options as SamlOptions; } getProtocol(req: Request | { headers?: undefined; protocol?: undefined }) { @@ -575,13 +575,13 @@ class SAML { */ getAuthorizeUrl( req: Request, - options: AuthenticateOptions & AuthorizeOptions, - callback: (err: Error | null, url?: string | null) => void - ) { + options: AuthorizeOptions, + callback: (err: Error | null, url: string) => void + ): void { util.callbackify(() => this.getAuthorizeUrlAsync(req, options))(callback); } - async getAuthorizeUrlAsync(req: Request, options: AuthenticateOptions & AuthorizeOptions) { + async getAuthorizeUrlAsync(req: Request, options: AuthorizeOptions): Promise { const request = await this.generateAuthorizeRequestAsync(req, this.options.passive, false); const operation = "authorize"; const overrideParams = options ? options.additionalParams || {} : {}; diff --git a/src/passport-saml/strategy.ts b/src/passport-saml/strategy.ts index 8e42287b..365a3ed0 100644 --- a/src/passport-saml/strategy.ts +++ b/src/passport-saml/strategy.ts @@ -17,7 +17,9 @@ class Strategy extends PassportStrategy { _saml: saml.SAML; _passReqToCallback?: boolean; - constructor(options: SamlConfig, verify: VerifyWithRequest | VerifyWithoutRequest) { + constructor(options: SamlConfig, verify: VerifyWithRequest); + constructor(options: SamlConfig, verify: VerifyWithoutRequest); + constructor(options: SamlConfig, verify: never) { super(); if (typeof options == "function") { verify = options; @@ -41,7 +43,7 @@ class Strategy extends PassportStrategy { this._passReqToCallback = !!options.passReqToCallback; } - authenticate(req: RequestWithUser, options: AuthenticateOptions & AuthorizeOptions): void { + authenticate(req: RequestWithUser, options: AuthenticateOptions): void { options.samlFallback = options.samlFallback || "login-request"; const validateCallback = (err: Error | null, profile?: Profile | null, loggedOut?: boolean) => { diff --git a/src/passport-saml/types.ts b/src/passport-saml/types.ts index f9e0ceb0..5cba6b79 100644 --- a/src/passport-saml/types.ts +++ b/src/passport-saml/types.ts @@ -5,8 +5,11 @@ import type { CacheProvider } from "./inmemory-cache-provider"; export type CertCallback = ( callback: (err: Error | null, cert?: string | string[]) => void ) => void; +export type RACComparision = "exact" | "minimum" | "maximum" | "better"; +export type SignatureAlgorithm = "sha1" | "sha256" | "sha512"; export interface AuthenticateOptions extends passport.AuthenticateOptions { + samlFallback?: "login-request" | "logout-request"; additionalParams?: Record; } @@ -14,7 +17,16 @@ export interface AuthorizeOptions extends AuthenticateOptions { samlFallback?: "login-request" | "logout-request"; } -export interface SAMLOptions { +export interface SamlSigningOptions { + /** @deprecated use privateKey field instead */ + privateCert?: string | Buffer; + privateKey: string | Buffer; + signatureAlgorithm?: SignatureAlgorithm; + xmlSignatureTransforms?: string[]; + digestAlgorithm?: string; +} + +export interface SamlOptions extends SamlSigningOptions { // Core callbackUrl: string; path: string; @@ -22,17 +34,13 @@ export interface SAMLOptions { host: string; entryPoint: string; issuer: string; - /** @deprecated use privateKey field instead */ - privateCert?: string; - privateKey: string; cert: string | string[] | CertCallback; - decryptionPvk: string; - signatureAlgorithm: "sha1" | "sha256" | "sha512"; + decryptionPvk: string | Buffer; // Additional SAML behaviors additionalParams: Record; additionalAuthorizeParams: Record; - identifierFormat: string; + identifierFormat: string | null; acceptedClockSkewMs: number; attributeConsumingServiceIndex: string | null; disableRequestedAuthnContext: boolean; @@ -40,7 +48,7 @@ export interface SAMLOptions { forceAuthn: boolean; skipRequestCompression: boolean; authnRequestBinding?: string; - RACComparison: "exact" | "minimum" | "maximum" | "better"; + RACComparison: RACComparision; providerName: string; passive: boolean; idpIssuer: string; @@ -58,12 +66,10 @@ export interface SAMLOptions { logoutCallbackUrl: string; // extras - xmlSignatureTransforms: string[]; - digestAlgorithm: string; disableRequestACSUrl: boolean; } -export type SamlConfig = Partial & StrategyOptions; +export type SamlConfig = Partial & StrategyOptions; interface StrategyOptions { name?: string; @@ -73,7 +79,7 @@ interface StrategyOptions { export interface SamlScopingConfig { idpList?: SamlIDPListConfig[]; proxyCount?: number; - requesterId?: string[]; + requesterId?: string[] | string; } export type XMLValue = string | number | boolean | null | XMLObject | XMLValue[]; @@ -119,7 +125,7 @@ export interface SamlIDPEntryConfig { loc?: string; } -export type Profile = { +export interface Profile { issuer?: string; sessionIndex?: string; nameID?: string; @@ -130,12 +136,11 @@ export type Profile = { mail?: string; // InCommon Attribute urn:oid:0.9.2342.19200300.100.1.3 email?: string; // `mail` if not present in the assertion ["urn:oid:0.9.2342.19200300.100.1.3"]?: string; - getAssertionXml(): string; // get the raw assertion XML - getAssertion(): Record; // get the assertion XML parsed as a JavaScript object - getSamlResponseXml(): string; // get the raw SAML response XML -} & { + getAssertionXml?(): string; // get the raw assertion XML + getAssertion?(): Record; // get the assertion XML parsed as a JavaScript object + getSamlResponseXml?(): string; // get the raw SAML response XML [attributeName: string]: unknown; // arbitrary `AttributeValue`s -}; +} export interface RequestWithUser extends express.Request { samlLogoutRequest: any; diff --git a/test/.eslintrc.json b/test/.eslintrc.json deleted file mode 100644 index 8a30ebae..00000000 --- a/test/.eslintrc.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "env": { - "node": true, - "mocha": true - } -} diff --git a/test/mocha.opts b/test/mocha.opts deleted file mode 100644 index 36c76021..00000000 --- a/test/mocha.opts +++ /dev/null @@ -1,2 +0,0 @@ ---reporter spec ---require choma \ No newline at end of file diff --git a/test/multiSamlStrategy.js b/test/multiSamlStrategy.spec.ts similarity index 55% rename from test/multiSamlStrategy.js rename to test/multiSamlStrategy.spec.ts index b1e7a235..d8939609 100644 --- a/test/multiSamlStrategy.js +++ b/test/multiSamlStrategy.spec.ts @@ -1,25 +1,30 @@ "use strict"; - -var sinon = require("sinon"); -var should = require("should"); -var saml = require("../lib/passport-saml/saml.js"); -var SamlStrategy = require("../lib/passport-saml/index.js").Strategy; -var MultiSamlStrategy = require("../lib/passport-saml/multiSamlStrategy.js"); - -function verify() {} +import * as express from "express"; +import * as sinon from "sinon"; +import * as should from "should"; +import { Strategy as SamlStrategy, MultiSamlStrategy, SAML } from "../src/passport-saml"; +import { + MultiSamlConfig, + SamlOptionsCallback, + RequestWithUser, + SamlConfig, + SamlOptions, +} from "../src/passport-saml/types"; + +const noop = () => undefined; describe("MultiSamlStrategy()", function () { it("extends passport Strategy", function () { function getSamlOptions() { return {}; } - var strategy = new MultiSamlStrategy({ getSamlOptions: getSamlOptions }, verify); + const strategy = new MultiSamlStrategy({ getSamlOptions: getSamlOptions }, noop); strategy.should.be.an.instanceOf(SamlStrategy); }); it("throws if wrong finder is provided", function () { function createStrategy() { - return new MultiSamlStrategy({}, verify); + return new MultiSamlStrategy({} as MultiSamlConfig, noop); } should.throws(createStrategy); }); @@ -35,10 +40,10 @@ describe("MultiSamlStrategy#authenticate", function () { }); it("calls super with request and auth options", function (done) { - var superAuthenticateStub = this.superAuthenticateStub; - function getSamlOptions(req, fn) { + const superAuthenticateStub = this.superAuthenticateStub; + function getSamlOptions(req: express.Request, fn: SamlOptionsCallback) { try { - fn(); + fn(null, {}); sinon.assert.calledOnce(superAuthenticateStub); done(); } catch (err2) { @@ -46,22 +51,22 @@ describe("MultiSamlStrategy#authenticate", function () { } } - var strategy = new MultiSamlStrategy( + const strategy = new MultiSamlStrategy( { getSamlOptions: getSamlOptions, }, - verify + noop ); - strategy.authenticate(); + strategy.authenticate("random" as any, "random" as any); }); it("passes options on to saml strategy", function (done) { - var passportOptions = { + const passportOptions = { passReqToCallback: true, - getSamlOptions: function (req, fn) { + getSamlOptions: function (req: express.Request, fn: SamlOptionsCallback) { try { - fn(); - strategy._passReqToCallback.should.eql(true); + fn(null, {}); + strategy._passReqToCallback!.should.eql(true); done(); } catch (err2) { done(err2); @@ -69,15 +74,14 @@ describe("MultiSamlStrategy#authenticate", function () { }, }; - var strategy = new MultiSamlStrategy(passportOptions, verify); - strategy.authenticate(); + const strategy = new MultiSamlStrategy(passportOptions, noop); + strategy.authenticate("random" as any, "random" as any); }); it("uses given options to setup internal saml provider", function (done) { - var superAuthenticateStub = this.superAuthenticateStub; - var samlOptions = { + const superAuthenticateStub = this.superAuthenticateStub; + const samlOptions: SamlConfig = { issuer: "http://foo.issuer", - authnRequestBinding: "HTTP-POST", callbackUrl: "http://foo.callback", cert: "deadbeef", host: "lvh", @@ -88,7 +92,7 @@ describe("MultiSamlStrategy#authenticate", function () { signatureAlgorithm: "sha256", }; - function getSamlOptions(req, fn) { + function getSamlOptions(req: express.Request, fn: SamlOptionsCallback) { try { fn(null, samlOptions); sinon.assert.calledOnce(superAuthenticateStub); @@ -101,18 +105,18 @@ describe("MultiSamlStrategy#authenticate", function () { } } - var strategy = new MultiSamlStrategy( - { getSamlOptions: getSamlOptions, cacheProvider: "mock cache provider" }, - verify + const strategy = new MultiSamlStrategy( + { getSamlOptions: getSamlOptions, cacheProvider: "mock cache provider" as any }, + noop ); - strategy.authenticate(); + strategy.authenticate("random" as any, "random" as any); }); }); describe("MultiSamlStrategy#authorize", function () { beforeEach(function () { - this.getAuthorizeFormStub = sinon.stub(saml.SAML.prototype, "getAuthorizeForm"); - this.getAuthorizeUrlStub = sinon.stub(saml.SAML.prototype, "getAuthorizeUrl"); + this.getAuthorizeFormStub = sinon.stub(SAML.prototype, "getAuthorizeForm"); + this.getAuthorizeUrlStub = sinon.stub(SAML.prototype, "getAuthorizeUrl"); }); afterEach(function () { @@ -121,20 +125,20 @@ describe("MultiSamlStrategy#authorize", function () { }); it("calls getAuthorizeForm when authnRequestBinding is HTTP-POST", function () { - function getSamlOptions(req, fn) { + function getSamlOptions(req: express.Request, fn: SamlOptionsCallback) { fn(null, { authnRequestBinding: "HTTP-POST" }); } - var strategy = new MultiSamlStrategy({ getSamlOptions }, verify); - strategy.authenticate({}, {}); + const strategy = new MultiSamlStrategy({ getSamlOptions }, noop); + strategy.authenticate({} as RequestWithUser, {}); sinon.assert.calledOnce(this.getAuthorizeFormStub); }); it("calls getAuthorizeUrl when authnRequestBinding is not HTTP-POST", function () { - function getSamlOptions(req, fn) { + function getSamlOptions(req: express.Request, fn: SamlOptionsCallback) { fn(null, {}); } - var strategy = new MultiSamlStrategy({ getSamlOptions }, verify); - strategy.authenticate({}, {}); + const strategy = new MultiSamlStrategy({ getSamlOptions }, noop); + strategy.authenticate({} as RequestWithUser, {}); sinon.assert.calledOnce(this.getAuthorizeUrlStub); }); }); @@ -149,10 +153,10 @@ describe("MultiSamlStrategy#logout", function () { }); it("calls super with request and auth options", function (done) { - var superLogoutMock = this.superLogoutMock; - function getSamlOptions(req, fn) { + const superLogoutMock = this.superLogoutMock; + function getSamlOptions(req: express.Request, fn: SamlOptionsCallback) { try { - fn(); + fn(null); sinon.assert.calledOnce(superLogoutMock); done(); } catch (err2) { @@ -160,17 +164,17 @@ describe("MultiSamlStrategy#logout", function () { } } - var strategy = new MultiSamlStrategy({ getSamlOptions: getSamlOptions }, verify); - strategy.logout(); + const strategy = new MultiSamlStrategy({ getSamlOptions: getSamlOptions }, noop); + strategy.logout("random" as any, "random" as any); }); it("passes options on to saml strategy", function (done) { - var passportOptions = { + const passportOptions = { passReqToCallback: true, - getSamlOptions: function (req, fn) { + getSamlOptions: function (req: express.Request, fn: SamlOptionsCallback) { try { - fn(); - strategy._passReqToCallback.should.eql(true); + fn(null, {}); + strategy._passReqToCallback!.should.eql(true); done(); } catch (err2) { done(err2); @@ -178,13 +182,13 @@ describe("MultiSamlStrategy#logout", function () { }, }; - var strategy = new MultiSamlStrategy(passportOptions, verify); - strategy.logout(); + const strategy = new MultiSamlStrategy(passportOptions, noop); + strategy.logout("random" as any, "random" as any); }); it("uses given options to setup internal saml provider", function (done) { - var superLogoutMock = this.superLogoutMock; - var samlOptions = { + const superLogoutMock = this.superLogoutMock; + const samlOptions: SamlConfig = { issuer: "http://foo.issuer", callbackUrl: "http://foo.callback", authnRequestBinding: "HTTP-POST", @@ -197,7 +201,7 @@ describe("MultiSamlStrategy#logout", function () { signatureAlgorithm: "sha256", }; - function getSamlOptions(req, fn) { + function getSamlOptions(req: express.Request, fn: SamlOptionsCallback) { try { fn(null, samlOptions); sinon.assert.calledOnce(superLogoutMock); @@ -208,8 +212,8 @@ describe("MultiSamlStrategy#logout", function () { } } - var strategy = new MultiSamlStrategy({ getSamlOptions: getSamlOptions }, verify); - strategy.logout(); + const strategy = new MultiSamlStrategy({ getSamlOptions: getSamlOptions }, noop); + strategy.logout("random" as any, sinon.spy()); }); }); @@ -225,10 +229,10 @@ describe("MultiSamlStrategy#generateServiceProviderMetadata", function () { }); it("calls super with request and generateServiceProviderMetadata options", function (done) { - var superGenerateServiceProviderMetadata = this.superGenerateServiceProviderMetadata; - function getSamlOptions(req, fn) { + const superGenerateServiceProviderMetadata = this.superGenerateServiceProviderMetadata; + function getSamlOptions(req: express.Request, fn: SamlOptionsCallback) { try { - fn(); + fn(null); sinon.assert.calledOnce(superGenerateServiceProviderMetadata); superGenerateServiceProviderMetadata.calledWith("bar", "baz"); req.should.eql("foo"); @@ -238,17 +242,18 @@ describe("MultiSamlStrategy#generateServiceProviderMetadata", function () { } } - var strategy = new MultiSamlStrategy({ getSamlOptions: getSamlOptions }, verify); - strategy.generateServiceProviderMetadata("foo", "bar", "baz", function () {}); + const strategy = new MultiSamlStrategy({ getSamlOptions: getSamlOptions }, noop); + strategy.generateServiceProviderMetadata("foo" as any, "bar", "baz", noop); }); it("passes options on to saml strategy", function (done) { - var passportOptions = { + const passportOptions = { passReqToCallback: true, - getSamlOptions: function (req, fn) { + + getSamlOptions: function (req: express.Request, fn: SamlOptionsCallback) { try { - fn(); - strategy._passReqToCallback.should.eql(true); + fn(null); + strategy._passReqToCallback!.should.eql(true); done(); } catch (err2) { done(err2); @@ -256,21 +261,21 @@ describe("MultiSamlStrategy#generateServiceProviderMetadata", function () { }, }; - var strategy = new MultiSamlStrategy(passportOptions, verify); - strategy.generateServiceProviderMetadata("foo", "bar", "baz", function () {}); + const strategy = new MultiSamlStrategy(passportOptions, noop); + strategy.generateServiceProviderMetadata("foo" as any, "bar", "baz", noop); }); it("should pass error to callback function", function (done) { - var passportOptions = { - getSamlOptions: function (req, fn) { - fn("My error"); + const passportOptions = { + getSamlOptions: function (req: express.Request, fn: SamlOptionsCallback) { + fn(new Error("My error"), {}); }, }; - var strategy = new MultiSamlStrategy(passportOptions, verify); - strategy.generateServiceProviderMetadata("foo", "bar", "baz", function (error, result) { + const strategy = new MultiSamlStrategy(passportOptions, noop); + strategy.generateServiceProviderMetadata("foo" as any, "bar", "baz", function (error, result) { try { - should(error).equal("My error"); + should(error?.message).equal("My error"); done(); } catch (err2) { done(err2); @@ -279,14 +284,14 @@ describe("MultiSamlStrategy#generateServiceProviderMetadata", function () { }); it("should pass result to callback function", function (done) { - var passportOptions = { - getSamlOptions: function (req, fn) { - fn(); + const passportOptions = { + getSamlOptions: function (req: express.Request, fn: SamlOptionsCallback) { + fn(null, {}); }, }; - var strategy = new MultiSamlStrategy(passportOptions, verify); - strategy.generateServiceProviderMetadata("foo", "bar", "baz", function (error, result) { + const strategy = new MultiSamlStrategy(passportOptions, noop); + strategy.generateServiceProviderMetadata("foo" as any, "bar", "baz", function (error, result) { try { should(result).equal("My Metadata Result"); done(); diff --git a/test/saml-post-signing-tests.js b/test/saml-post-signing-tests.spec.ts similarity index 66% rename from test/saml-post-signing-tests.js rename to test/saml-post-signing-tests.spec.ts index c4dfc61d..70509667 100644 --- a/test/saml-post-signing-tests.js +++ b/test/saml-post-signing-tests.spec.ts @@ -1,53 +1,52 @@ -const fs = require("fs"); -const should = require("should"); -const samlPostSigning = require("../lib/passport-saml/saml-post-signing"); -const signSamlPost = samlPostSigning.signSamlPost; -const signAuthnRequestPost = samlPostSigning.signAuthnRequestPost; +import * as fs from "fs"; +import "should"; +import { signSamlPost, signAuthnRequestPost } from "../src/passport-saml/saml-post-signing"; +import { SamlOptions, SamlSigningOptions } from "../src/passport-saml/types"; const signingKey = fs.readFileSync(__dirname + "/static/key.pem"); describe("SAML POST Signing", function () { it("should sign a simple saml request", function () { - var xml = + const xml = 'http://example.com'; - var result = signSamlPost(xml, "/SAMLRequest", { privateCert: signingKey }); - result.should.match(/[A-Za-z0-9\/\+\=]+<\/DigestValue>/); - result.should.match(/[A-Za-z0-9\/\+\=]+<\/SignatureValue>/); + const result = signSamlPost(xml, "/SAMLRequest", { privateCert: signingKey } as SamlOptions); + result.should.match(/[A-Za-z0-9/+=]+<\/DigestValue>/); + result.should.match(/[A-Za-z0-9/+=]+<\/SignatureValue>/); }); it("should sign a simple saml request when using a privateKey", function () { - var xml = + const xml = 'http://example.com'; - var result = signSamlPost(xml, "/SAMLRequest", { privateKey: signingKey }); - result.should.match(/[A-Za-z0-9\/\+\=]+<\/DigestValue>/); - result.should.match(/[A-Za-z0-9\/\+\=]+<\/SignatureValue>/); + const result = signSamlPost(xml, "/SAMLRequest", { privateKey: signingKey }); + result.should.match(/[A-Za-z0-9/+=]+<\/DigestValue>/); + result.should.match(/[A-Za-z0-9/+=]+<\/SignatureValue>/); }); it("should place the Signature element after the Issuer element", function () { - var xml = + const xml = 'http://example.com'; - var result = signSamlPost(xml, "/SAMLRequest", { privateCert: signingKey }); + const result = signSamlPost(xml, "/SAMLRequest", { privateCert: signingKey } as SamlOptions); result.should.match(/<\/saml2:Issuer>[A-Za-z0-9\/\+\=]+<\/DigestValue>/); - result.should.match(/[A-Za-z0-9\/\+\=]+<\/SignatureValue>/); + const result = signAuthnRequestPost(xml, { privateCert: signingKey } as SamlOptions); + result.should.match(/[A-Za-z0-9/+=]+<\/DigestValue>/); + result.should.match(/[A-Za-z0-9/+=]+<\/SignatureValue>/); }); it("should sign an AuthnRequest when using a privateKey", function () { - var xml = + const xml = 'http://example.com'; - var result = signAuthnRequestPost(xml, { privateKey: signingKey }); - result.should.match(/[A-Za-z0-9\/\+\=]+<\/DigestValue>/); - result.should.match(/[A-Za-z0-9\/\+\=]+<\/SignatureValue>/); + const result = signAuthnRequestPost(xml, { privateKey: signingKey }); + result.should.match(/[A-Za-z0-9/+=]+<\/DigestValue>/); + result.should.match(/[A-Za-z0-9/+=]+<\/SignatureValue>/); }); }); diff --git a/test/samlTests.js b/test/samlTests.spec.ts similarity index 69% rename from test/samlTests.js rename to test/samlTests.spec.ts index 29b4b174..2f6c1998 100644 --- a/test/samlTests.js +++ b/test/samlTests.spec.ts @@ -1,13 +1,21 @@ "use strict"; -var fs = require("fs"); -var url = require("url"); -var should = require("should"); - -var SAML = require("../lib/passport-saml/saml.js").SAML; +import * as fs from "fs"; +import * as url from "url"; +import * as should from "should"; +import express = require("express"); +import { SAML } from "../src/passport-saml/saml"; +import { + RequestWithUser, + Profile, + AuthenticateOptions, + AuthorizeOptions, +} from "../src/passport-saml/types"; describe("SAML.js", function () { describe("get Urls", function () { - var saml, req, options; + let saml: SAML; + let req: RequestWithUser; + let options: AuthenticateOptions & AuthorizeOptions; beforeEach(function () { saml = new SAML({ entryPoint: "https://exampleidp.com/path?key=value", @@ -25,7 +33,7 @@ describe("SAML.js", function () { samlLogoutRequest: { ID: 123, }, - }; + } as RequestWithUser; options = { additionalParams: { additionalKey: "additionalValue", @@ -37,7 +45,7 @@ describe("SAML.js", function () { it("calls callback with right host", function (done) { saml.getAuthorizeUrl(req, {}, function (err, target) { try { - url.parse(target).host.should.equal("exampleidp.com"); + url.parse(target!).host!.should.equal("exampleidp.com"); done(); } catch (err2) { done(err2); @@ -47,7 +55,7 @@ describe("SAML.js", function () { it("calls callback with right protocol", function (done) { saml.getAuthorizeUrl(req, {}, function (err, target) { try { - url.parse(target).protocol.should.equal("https:"); + url.parse(target!).protocol!.should.equal("https:"); done(); } catch (err2) { done(err2); @@ -57,7 +65,7 @@ describe("SAML.js", function () { it("calls callback with right path", function (done) { saml.getAuthorizeUrl(req, {}, function (err, target) { try { - url.parse(target).pathname.should.equal("/path"); + url.parse(target!).pathname!.should.equal("/path"); done(); } catch (err2) { done(err2); @@ -67,7 +75,7 @@ describe("SAML.js", function () { it("calls callback with original query string", function (done) { saml.getAuthorizeUrl(req, {}, function (err, target) { try { - url.parse(target, true).query["key"].should.equal("value"); + url.parse(target!, true).query["key"]!.should.equal("value"); done(); } catch (err2) { done(err2); @@ -77,10 +85,10 @@ describe("SAML.js", function () { it("calls callback with additional run-time params in query string", function (done) { saml.getAuthorizeUrl(req, options, function (err, target) { try { - Object.keys(url.parse(target, true).query).should.have.length(3); - url.parse(target, true).query["key"].should.equal("value"); - url.parse(target, true).query["SAMLRequest"].should.not.be.empty(); - url.parse(target, true).query["additionalKey"].should.equal("additionalValue"); + Object.keys(url.parse(target!, true).query).should.have.length(3); + url.parse(target!, true).query["key"]!.should.equal("value"); + url.parse(target!, true).query["SAMLRequest"]!.should.not.be.empty(); + url.parse(target!, true).query["additionalKey"]!.should.equal("additionalValue"); done(); } catch (err2) { done(err2); @@ -91,7 +99,7 @@ describe("SAML.js", function () { it("calls callback with saml request object", function (done) { saml.getAuthorizeUrl(req, {}, function (err, target) { try { - should(url.parse(target, true).query).have.property("SAMLRequest"); + should(url.parse(target!, true).query).have.property("SAMLRequest"); done(); } catch (err2) { done(err2); @@ -104,7 +112,7 @@ describe("SAML.js", function () { it("calls callback with right host", function (done) { saml.getLogoutUrl(req, {}, function (err, target) { try { - url.parse(target).host.should.equal("exampleidp.com"); + url.parse(target!).host!.should.equal("exampleidp.com"); done(); } catch (err2) { done(err2); @@ -114,7 +122,7 @@ describe("SAML.js", function () { it("calls callback with right protocol", function (done) { saml.getLogoutUrl(req, {}, function (err, target) { try { - url.parse(target).protocol.should.equal("https:"); + url.parse(target!).protocol!.should.equal("https:"); done(); } catch (err2) { done(err2); @@ -124,7 +132,7 @@ describe("SAML.js", function () { it("calls callback with right path", function (done) { saml.getLogoutUrl(req, {}, function (err, target) { try { - url.parse(target).pathname.should.equal("/path"); + url.parse(target!).pathname!.should.equal("/path"); done(); } catch (err2) { done(err2); @@ -134,7 +142,7 @@ describe("SAML.js", function () { it("calls callback with original query string", function (done) { saml.getLogoutUrl(req, {}, function (err, target) { try { - url.parse(target, true).query["key"].should.equal("value"); + url.parse(target!, true).query["key"]!.should.equal("value"); done(); } catch (err2) { done(err2); @@ -144,10 +152,10 @@ describe("SAML.js", function () { it("calls callback with additional run-time params in query string", function (done) { saml.getLogoutUrl(req, options, function (err, target) { try { - Object.keys(url.parse(target, true).query).should.have.length(3); - url.parse(target, true).query["key"].should.equal("value"); - url.parse(target, true).query["SAMLRequest"].should.not.be.empty(); - url.parse(target, true).query["additionalKey"].should.equal("additionalValue"); + Object.keys(url.parse(target!, true).query).should.have.length(3); + url.parse(target!, true).query["key"]!.should.equal("value"); + url.parse(target!, true).query["SAMLRequest"]!.should.not.be.empty(); + url.parse(target!, true).query["additionalKey"]!.should.equal("additionalValue"); done(); } catch (err2) { done(err2); @@ -158,7 +166,7 @@ describe("SAML.js", function () { it("calls callback with saml request object", function (done) { saml.getLogoutUrl(req, {}, function (err, target) { try { - should(url.parse(target, true).query).have.property("SAMLRequest"); + should(url.parse(target!, true).query).have.property("SAMLRequest"); done(); } catch (err2) { done(err2); @@ -171,7 +179,7 @@ describe("SAML.js", function () { it("calls callback with right host", function (done) { saml.getLogoutResponseUrl(req, {}, function (err, target) { try { - url.parse(target).host.should.equal("exampleidp.com"); + url.parse(target!).host!.should.equal("exampleidp.com"); done(); } catch (err2) { done(err2); @@ -181,7 +189,7 @@ describe("SAML.js", function () { it("calls callback with right protocol", function (done) { saml.getLogoutResponseUrl(req, {}, function (err, target) { try { - url.parse(target).protocol.should.equal("https:"); + url.parse(target!).protocol!.should.equal("https:"); done(); } catch (err2) { done(err2); @@ -191,7 +199,7 @@ describe("SAML.js", function () { it("calls callback with right path", function (done) { saml.getLogoutResponseUrl(req, {}, function (err, target) { try { - url.parse(target).pathname.should.equal("/path"); + url.parse(target!).pathname!.should.equal("/path"); done(); } catch (err2) { done(err2); @@ -201,7 +209,7 @@ describe("SAML.js", function () { it("calls callback with original query string", function (done) { saml.getLogoutResponseUrl(req, {}, function (err, target) { try { - url.parse(target, true).query["key"].should.equal("value"); + url.parse(target!, true).query["key"]!.should.equal("value"); done(); } catch (err2) { done(err2); @@ -211,10 +219,10 @@ describe("SAML.js", function () { it("calls callback with additional run-time params in query string", function (done) { saml.getLogoutResponseUrl(req, options, function (err, target) { try { - Object.keys(url.parse(target, true).query).should.have.length(3); - url.parse(target, true).query["key"].should.equal("value"); - url.parse(target, true).query["SAMLResponse"].should.not.be.empty(); - url.parse(target, true).query["additionalKey"].should.equal("additionalValue"); + Object.keys(url.parse(target!, true).query).should.have.length(3); + url.parse(target!, true).query["key"]!.should.equal("value"); + url.parse(target!, true).query["SAMLResponse"]!.should.not.be.empty(); + url.parse(target!, true).query["additionalKey"]!.should.equal("additionalValue"); done(); } catch (err2) { done(err2); @@ -225,7 +233,7 @@ describe("SAML.js", function () { it("calls callback with saml response object", function (done) { saml.getLogoutResponseUrl(req, {}, function (err, target) { try { - should(url.parse(target, true).query).have.property("SAMLResponse"); + should(url.parse(target!, true).query).have.property("SAMLResponse"); done(); } catch (err2) { done(err2); @@ -235,33 +243,33 @@ describe("SAML.js", function () { }); describe("keyToPEM", function () { - var [regular, singleline] = ["acme_tools_com.key", "singleline_acme_tools_com.key"].map( + const [regular, singleline] = ["acme_tools_com.key", "singleline_acme_tools_com.key"].map( keyFromFile ); it("formats singleline keys properly", function () { - var result = saml.keyToPEM(singleline); + const result = saml.keyToPEM(singleline); result.should.equal(regular); }); it("passes all other multiline keys", function () { - var result = saml.keyToPEM(regular); + const result = saml.keyToPEM(regular); result.should.equal(regular); }); it("does nothing to falsy", function () { - var result = saml.keyToPEM(null); + const result = saml.keyToPEM(null as any); should.equal(result, null); }); it("does nothing to non strings", function () { - var result = saml.keyToPEM(1); + const result = saml.keyToPEM(1 as any); should.equal(result, 1); }); }); }); }); -function keyFromFile(file) { +function keyFromFile(file: string) { return fs.readFileSync(`./test/static/${file}`).toString(); } diff --git a/test/strategy.js b/test/strategy.spec.ts similarity index 50% rename from test/strategy.js rename to test/strategy.spec.ts index 06f9c4eb..cdaf4228 100644 --- a/test/strategy.js +++ b/test/strategy.spec.ts @@ -1,15 +1,15 @@ "use strict"; -var sinon = require("sinon"); -var saml = require("../lib/passport-saml/saml.js"); -var SamlStrategy = require("../lib/passport-saml/index.js").Strategy; +import * as sinon from "sinon"; +import { Strategy as SamlStrategy, SAML } from "../src/passport-saml"; +import { RequestWithUser } from "../src/passport-saml/types"; -function verify() {} +const noop = () => undefined; describe("strategy#authorize", function () { beforeEach(function () { - this.getAuthorizeFormStub = sinon.stub(saml.SAML.prototype, "getAuthorizeForm"); - this.getAuthorizeUrlStub = sinon.stub(saml.SAML.prototype, "getAuthorizeUrl"); + this.getAuthorizeFormStub = sinon.stub(SAML.prototype, "getAuthorizeForm"); + this.getAuthorizeUrlStub = sinon.stub(SAML.prototype, "getAuthorizeUrl"); }); afterEach(function () { @@ -18,19 +18,19 @@ describe("strategy#authorize", function () { }); it("calls getAuthorizeForm when authnRequestBinding is HTTP-POST", function () { - var strategy = new SamlStrategy( + const strategy = new SamlStrategy( { authnRequestBinding: "HTTP-POST", }, - verify + noop ); - strategy.authenticate({}, {}); + strategy.authenticate({} as RequestWithUser, {}); sinon.assert.calledOnce(this.getAuthorizeFormStub); }); it("calls getAuthorizeUrl when authnRequestBinding is not HTTP-POST", function () { - var strategy = new SamlStrategy({}, verify); - strategy.authenticate({}, {}); + const strategy = new SamlStrategy({}, noop); + strategy.authenticate({} as RequestWithUser, {}); sinon.assert.calledOnce(this.getAuthorizeUrlStub); }); }); diff --git a/test/test-signatures.js b/test/test-signatures.spec.ts similarity index 85% rename from test/test-signatures.js rename to test/test-signatures.spec.ts index d46ffd2f..c9f8bcea 100644 --- a/test/test-signatures.js +++ b/test/test-signatures.spec.ts @@ -1,24 +1,28 @@ -const should = require("should"), - SAML = require("../lib/passport-saml/index.js").SAML, - fs = require("fs"), - cert = fs.readFileSync(__dirname + "/static/cert.pem", "ascii"), - sinon = require("sinon"); +import { SAML } from "../lib/passport-saml/index.js"; +import * as fs from "fs"; +import * as sinon from "sinon"; +import "should"; + +const cert = fs.readFileSync(__dirname + "/static/cert.pem", "ascii"); describe("Signatures", function () { - const INVALID_ROOT_SIGNATURE = "Invalid signature on documentElement", - INVALID_SIGNATURE = "Invalid signature", - createBody = (pathToXml) => ({ + const INVALID_SIGNATURE = "Invalid signature", + createBody = (pathToXml: string) => ({ SAMLResponse: fs.readFileSync(__dirname + "/static/signatures" + pathToXml, "base64"), }), - tryCatchTest = (done, func) => (...args) => { + tryCatchTest = (done: Mocha.Done, func: any) => (...args: any) => { try { func(...args); } catch (ex) { done(ex); } }, - testOneResponseBody = (samlResponseBody, shouldErrorWith, amountOfSignatureChecks = 1) => { - return (done) => { + testOneResponseBody = ( + samlResponseBody: Record, + shouldErrorWith: string | false | undefined, + amountOfSignatureChecks = 1 + ) => { + return (done: Mocha.Done) => { //== Instantiate new instance before every test const samlObj = new SAML({ cert }); //== Spy on `validateSignature` to be able to count how many times it has been called @@ -27,11 +31,9 @@ describe("Signatures", function () { //== Run the test in `func` samlObj.validatePostResponse( samlResponseBody, - tryCatchTest(done, function (error) { + tryCatchTest(done, function (error: any) { //== Assert error. If the error is `SAML assertion expired` we made it past the certificate validation - shouldErrorWith - ? error.should.eql(new Error(shouldErrorWith)) - : error.should.eql(new Error("SAML assertion expired")); + error.should.eql(new Error(shouldErrorWith || "SAML assertion expired")); //== Assert times `validateSignature` was called validateSignatureSpy.callCount.should.eql(amountOfSignatureChecks); done(); @@ -39,9 +41,13 @@ describe("Signatures", function () { ); }; }, - testOneResponse = (pathToXml, ...args) => { + testOneResponse = ( + pathToXml: string, + shouldErrorWith: string | false, + amountOfSignaturesChecks: number | undefined + ) => { //== Create a body based on an XML and run the test - return testOneResponseBody(createBody(pathToXml), ...args); + return testOneResponseBody(createBody(pathToXml), shouldErrorWith, amountOfSignaturesChecks); }; describe("Signatures on saml:Response - Only 1 saml:Assertion", () => { @@ -224,7 +230,7 @@ describe("Signatures", function () { __dirname + "/static/signatures/valid/response.root-signed.assertion-signed.xml" ) .toString(); - const makeBody = (str) => ({ SAMLResponse: Buffer.from(str).toString("base64") }); + const makeBody = (str: string) => ({ SAMLResponse: Buffer.from(str).toString("base64") }); it("CRLF line endings", (done) => { const body = makeBody(samlResponseXml.replace(/\n/g, "\r\n")); diff --git a/test/tests.js b/test/tests.spec.ts similarity index 89% rename from test/tests.js rename to test/tests.spec.ts index a8b38058..c19d67d6 100644 --- a/test/tests.js +++ b/test/tests.spec.ts @@ -1,27 +1,60 @@ "use strict"; -var express = require("express"); -var bodyParser = require("body-parser"); -var passport = require("passport"); -var SamlStrategy = require("../lib/passport-saml/index.js").Strategy; -var request = require("request"); -var should = require("should"); -var zlib = require("zlib"); -var querystring = require("querystring"); -var parseString = require("xml2js").parseString; -var SAML = require("../lib/passport-saml/index.js").SAML; -var fs = require("fs"); -var sinon = require("sinon"); +import * as express from "express"; +import * as bodyParser from "body-parser"; +import * as passport from "passport"; +import { Strategy as SamlStrategy, SAML } from "../src/passport-saml"; +import request = require("request"); +import url = require("url"); +import "should"; +import * as zlib from "zlib"; +import * as querystring from "querystring"; +import { parseString } from "xml2js"; +import * as fs from "fs"; +import * as sinon from "sinon"; +import { + AuthenticateOptions, + Profile, + RACComparision, + RequestWithUser, + SamlConfig, + SamlOptions, + VerifiedCallback, +} from "../src/passport-saml/types.js"; +import * as should from "should"; +import { Server } from "http"; // a certificate which is re-used by several tests -var TEST_CERT = +const TEST_CERT = "MIIEFzCCAv+gAwIBAgIUFJsUjPM7AmWvNtEvULSHlTTMiLQwDQYJKoZIhvcNAQEFBQAwWDELMAkGA1UEBhMCVVMxETAPBgNVBAoMCFN1YnNwYWNlMRUwEwYDVQQLDAxPbmVMb2dpbiBJZFAxHzAdBgNVBAMMFk9uZUxvZ2luIEFjY291bnQgNDIzNDkwHhcNMTQwNTEzMTgwNjEyWhcNMTkwNTE0MTgwNjEyWjBYMQswCQYDVQQGEwJVUzERMA8GA1UECgwIU3Vic3BhY2UxFTATBgNVBAsMDE9uZUxvZ2luIElkUDEfMB0GA1UEAwwWT25lTG9naW4gQWNjb3VudCA0MjM0OTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKrAzJdY9FzFLt5blArJfPzgi87EnFGlTfcV5T1TUDwLBlDkY/0ZGKnMOpf3D7ie2C4pPFOImOogcM5kpDDL7qxTXZ1ewXVyjBdMu29NG2C6NzWeQTUMUji01EcHkC8o+Pts8ANiNOYcjxEeyhEyzJKgEizblYzMMKzdrOET6QuqWo3C83K+5+5dsjDn1ooKGRwj3HvgsYcFrQl9NojgQFjoobwsiE/7A+OJhLpBcy/nSVgnoJaMfrO+JsnukZPztbntLvOl56+Vra0N8n5NAYhaSayPiv/ayhjVgjfXd1tjMVTOiDknUOwizZuJ1Y3QH94vUtBgp0WBpBSs/xMyTs8CAwEAAaOB2DCB1TAMBgNVHRMBAf8EAjAAMB0GA1UdDgQWBBRQO4WpM5fWwxib49WTuJkfYDbxODCBlQYDVR0jBIGNMIGKgBRQO4WpM5fWwxib49WTuJkfYDbxOKFcpFowWDELMAkGA1UEBhMCVVMxETAPBgNVBAoMCFN1YnNwYWNlMRUwEwYDVQQLDAxPbmVMb2dpbiBJZFAxHzAdBgNVBAMMFk9uZUxvZ2luIEFjY291bnQgNDIzNDmCFBSbFIzzOwJlrzbRL1C0h5U0zIi0MA4GA1UdDwEB/wQEAwIHgDANBgkqhkiG9w0BAQUFAAOCAQEACdDAAoaZFCEY5pmfwbKuKrXtO5iE8lWtiCPjCZEUuT6bXRNcqrdnuV/EAfX9WQoXjalPi0eM78zKmbvRGSTUHwWw49RHjFfeJUKvHNeNnFgTXDjEPNhMvh69kHm453lFRmB+kk6yjtXRZaQEwS8Uuo2Ot+krgNbl6oTBZJ0AHH1MtZECDloms1Km7zsK8wAi5i8TVIKkVr5b2VlhrLgFMvzZ5ViAxIMGB6w47yY4QGQB/5Q8ya9hBs9vkn+wubA+yr4j14JXZ7blVKDSTYva65Ea+PqHyrp+Wnmnbw2ObS7iWexiTy1jD3G0R2avDBFjM8Fj5DbfufsE1b0U10RTtg=="; -var ALT_TEST_CERT = +const ALT_TEST_CERT = "MIIEOTCCAyGgAwIBAgIJAKZgJdKdCdL6MA0GCSqGSIb3DQEBBQUAMHAxCzAJBgNVBAYTAkFVMREwDwYDVQQIEwhWaWN0b3JpYTESMBAGA1UEBxMJTWVsYm91cm5lMSEwHwYDVQQKExhUYWJjb3JwIEhvbGRpbmdzIExpbWl0ZWQxFzAVBgNVBAMTDnN0cy50YWIuY29tLmF1MB4XDTE3MDUzMDA4NTQwOFoXDTI3MDUyODA4NTQwOFowcDELMAkGA1UEBhMCQVUxETAPBgNVBAgTCFZpY3RvcmlhMRIwEAYDVQQHEwlNZWxib3VybmUxITAfBgNVBAoTGFRhYmNvcnAgSG9sZGluZ3MgTGltaXRlZDEXMBUGA1UEAxMOc3RzLnRhYi5jb20uYXUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQD0NuMcflq3rtupKYDf4a7lWmsXy66fYe9n8jB2DuLMakEJBlzn9j6B98IZftrilTq21VR7wUXROxG8BkN8IHY+l8X7lATmD28fFdZJj0c8Qk82eoq48faemth4fBMx2YrpnhU00jeXeP8dIIaJTPCHBTNgZltMMhphklN1YEPlzefJs3YD+Ryczy1JHbwETxt+BzO1JdjBe1fUTyl6KxAwWvtsNBURmQRYlDOk4GRgdkQnfxBuCpOMeOpV8wiBAi3h65Lab9C5avu4AJlA9e4qbOmWt6otQmgy5fiJVy6bH/d8uW7FJmSmePX9sqAWa9szhjdn36HHVQsfHC+IUEX7AgMBAAGjgdUwgdIwHQYDVR0OBBYEFN6z6cuxY7FTkg1S/lIjnS4x5ARWMIGiBgNVHSMEgZowgZeAFN6z6cuxY7FTkg1S/lIjnS4x5ARWoXSkcjBwMQswCQYDVQQGEwJBVTERMA8GA1UECBMIVmljdG9yaWExEjAQBgNVBAcTCU1lbGJvdXJuZTEhMB8GA1UEChMYVGFiY29ycCBIb2xkaW5ncyBMaW1pdGVkMRcwFQYDVQQDEw5zdHMudGFiLmNvbS5hdYIJAKZgJdKdCdL6MAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAMi5HyvXgRa4+kKz3dk4SwAEXzeZRcsbeDJWVUxdb6a+JQxIoG7L9rSbd6yZvP/Xel5TrcwpCpl5eikzXB02/C0wZKWicNmDEBlOfw0Pc5ngdoh6ntxHIWm5QMlAfjR0dgTlojN4Msw2qk7cP1QEkV96e2BJUaqaNnM3zMvd7cfRjPNfbsbwl6hCCCAdwrALKYtBnjKVrCGPwO+xiw5mUJhZ1n6ZivTOdQEWbl26UO60J9ItiWP8VK0d0aChn326Ovt7qC4S3AgDlaJwcKe5Ifxl/UOWePGRwXj2UUuDWFhjtVmRntMmNZbe5yE8MkEvU+4/c6LqGwTCgDenRbK53Dg="; +interface CapturedCheck { + samlRequest?: any; + result?: any; + name: string; + expectedStatusCode: number; + samlResponse?: any; + config: Partial; + expectedNameIDStartsWith?: string; + mockDate: string; +} + +interface SAMLCheck { + samlRequest?: any; + result?: any; + name: string; + samlResponse?: any; + config: Partial; + expectedNameIDStartsWith?: string; +} + +const noop = () => undefined; + describe("passport-saml /", function () { describe("captured saml responses /", function () { - var fakeClock; - var capturedChecks = [ + let fakeClock: sinon.SinonFakeTimers; + const capturedChecks: CapturedCheck[] = [ { name: "Okta -- valid config should succeed", samlResponse: { @@ -104,25 +137,30 @@ describe("passport-saml /", function () { }, ]; - var server; + let server: Server; - function testForCheck(check) { - return function (done) { - var pp = new passport.Authenticator(); - var app = express(); + function testForCheck(check: CapturedCheck) { + return function (done: Mocha.Done) { + const pp = new passport.Authenticator(); + const app = express(); app.use(bodyParser.urlencoded({ extended: false })); app.use(pp.initialize()); - var config = check.config; + const config = check.config; config.callbackUrl = "http://localhost:3033/login"; - var profile = null; + let profile: Profile; pp.use( - new SamlStrategy(config, function (_profile, done) { - profile = _profile; - done(null, { id: profile.nameID }); - }) + new SamlStrategy( + config, + function (_profile: Profile | null | undefined, done: VerifiedCallback): void { + if (_profile) { + profile = _profile; + done(null, { id: profile.nameID }); + } + } + ) ); - var userSerialized = false; + let userSerialized = false; pp.serializeUser(function (user, done) { userSerialized = true; done(null, user); @@ -134,26 +172,30 @@ describe("passport-saml /", function () { res.status(200).send("200 OK"); }); - app.use(function (err, req, res, next) { - // console.log( err.stack ); - res.status(500).send("500 Internal Server Error"); + app.use(function ( + err: Error, + _req: express.Request, + res: express.Response, + _next: express.NextFunction + ) { + res.status(500).send(err.stack); }); server = app.listen(3033, function () { - var requestOpts = { + const requestOpts = { url: "http://localhost:3033/login", method: "POST", form: check.samlResponse, }; - - request(requestOpts, function (err, response, body) { + // TODO remove usage of request module + request(requestOpts, function (err: Error | null, response: any, body: any) { try { should.not.exist(err); - response.statusCode.should.equal(check.expectedStatusCode); + response.statusCode.should.equal(check.expectedStatusCode, body); if (response.statusCode == 200) { userSerialized.should.be.true; if (check.expectedNameIDStartsWith) - profile.nameID.should.startWith(check.expectedNameIDStartsWith); + profile!.nameID!.should.startWith(check.expectedNameIDStartsWith); } done(); } catch (err2) { @@ -164,21 +206,30 @@ describe("passport-saml /", function () { }; } - function testPassReqToCallback(check) { - return function (done) { - var pp = new passport.Authenticator(); - var app = express(); + function testPassReqToCallback(check: CapturedCheck) { + return function (done: Mocha.Done) { + const pp = new passport.Authenticator(); + const app = express(); app.use(bodyParser.urlencoded({ extended: false })); app.use(pp.initialize()); - var config = check.config; + const config = { ...check.config }; config.callbackUrl = "http://localhost:3033/login"; config.passReqToCallback = true; - var passedRequest = null; + let passedRequest: express.Request | null = null; pp.use( - new SamlStrategy(config, function (req, _profile, done) { - passedRequest = req; - done(null, { id: _profile.nameID }); - }) + new SamlStrategy( + config, + function ( + req: express.Request, + _profile: Profile | null | undefined, + done: VerifiedCallback + ) { + if (_profile) { + passedRequest = req; + done(null, { id: _profile!.nameID }); + } + } + ) ); pp.serializeUser(function (user, done) { done(null, user); @@ -187,25 +238,31 @@ describe("passport-saml /", function () { app.post("/login", pp.authenticate("saml"), function (req, res) { res.status(200).send("200 OK"); }); - app.use(function (err, req, res, next) { + app.use(function ( + err: Error | null, + req: express.Request, + res: express.Response, + next: express.NextFunction + ) { // console.log( err.stack ); res.status(500).send("500 Internal Server Error"); }); server = app.listen(3033, function () { - var requestOpts = { + const requestOpts = { url: "http://localhost:3033/login", method: "POST", form: check.samlResponse, }; - request(requestOpts, function (err, response, body) { + // TODO remove usage of request module + request(requestOpts, function (err: any, response: any, body: any) { try { should.not.exist(err); response.statusCode.should.equal(check.expectedStatusCode); if (response.statusCode == 200) { should.exist(passedRequest); - passedRequest.url.should.eql("/login"); - passedRequest.method.should.eql("POST"); - should(passedRequest.body).match(check.samlResponse); + passedRequest!.url!.should.eql("/login"); + passedRequest!.method!.should.eql("POST"); + should(passedRequest!.body).match(check.samlResponse); } else { should.not.exist(passedRequest); } @@ -218,8 +275,7 @@ describe("passport-saml /", function () { }; } - for (var i = 0; i < capturedChecks.length; i++) { - var check = capturedChecks[i]; + for (const check of capturedChecks) { it(check.name, testForCheck(check)); it(check.name + " passReqToCallback", testPassReqToCallback(check)); } @@ -231,7 +287,7 @@ describe("passport-saml /", function () { }); describe("captured SAML requests /", function () { - var logoutChecks = [ + const logoutChecks: CapturedCheck[] = [ { name: "Logout", config: { @@ -274,7 +330,7 @@ describe("passport-saml /", function () { }, ]; - var capturedChecks = [ + const capturedChecks: SAMLCheck[] = [ { name: "Empty Config", config: {}, @@ -357,7 +413,7 @@ describe("passport-saml /", function () { issuer: "http://exampleSp.com/saml", identifierFormat: "alternateIdentifier", passive: true, - attributeConsumingServiceIndex: 123, + attributeConsumingServiceIndex: "123", forceAuthn: false, }, result: { @@ -406,7 +462,7 @@ describe("passport-saml /", function () { issuer: "http://exampleSp.com/saml", identifierFormat: "alternateIdentifier", passive: true, - attributeConsumingServiceIndex: 123, + attributeConsumingServiceIndex: "123", skipRequestCompression: true, }, result: { @@ -455,7 +511,7 @@ describe("passport-saml /", function () { issuer: "http://exampleSp.com/saml", identifierFormat: "alternateIdentifier", passive: true, - attributeConsumingServiceIndex: 123, + attributeConsumingServiceIndex: "123", skipRequestCompression: true, disableRequestedAuthnContext: true, forceAuthn: true, @@ -496,7 +552,7 @@ describe("passport-saml /", function () { issuer: "http://exampleSp.com/saml", identifierFormat: "alternateIdentifier", passive: true, - attributeConsumingServiceIndex: 123, + attributeConsumingServiceIndex: "123", authnContext: "myAuthnContext", }, result: { @@ -545,7 +601,7 @@ describe("passport-saml /", function () { issuer: "http://exampleSp.com/saml", identifierFormat: "alternateIdentifier", passive: true, - attributeConsumingServiceIndex: 123, + attributeConsumingServiceIndex: "123", authnContext: ["myAuthnContext", "myAuthnContext2"], }, result: { @@ -1111,25 +1167,30 @@ describe("passport-saml /", function () { }, ]; - var server; + let server: Server; - function testForCheck(check) { - return function (done) { - var app = express(); + function testForCheck(check: SAMLCheck) { + return function (done: Mocha.Done) { + const app = express(); app.use(bodyParser.urlencoded({ extended: false })); app.use(passport.initialize()); - var config = check.config; + const config = check.config; config.callbackUrl = "http://localhost:3033/login"; config.entryPoint = "https://wwwexampleIdp.com/saml"; - var profile = null; + let profile: Profile; passport.use( - new SamlStrategy(config, function (_profile, done) { - profile = _profile; - done(null, profile); - }) + new SamlStrategy( + config, + function (_profile: Profile | null | undefined, done: VerifiedCallback) { + if (_profile) { + profile = _profile; + done(null, profile); + } + } + ) ); - var userSerialized = false; + let userSerialized = false; passport.serializeUser(function (user, done) { userSerialized = true; done(null, user); @@ -1137,61 +1198,70 @@ describe("passport-saml /", function () { app.get( "/login", - passport.authenticate("saml", { samlFallback: "login-request", session: false }), + passport.authenticate("saml", { + samlFallback: "login-request", + session: false, + } as AuthenticateOptions), function (req, res) { res.status(200).send("200 OK"); } ); - app.use(function (err, req, res, next) { + app.use(function ( + err: Error | null, + req: express.Request, + res: express.Response, + next: express.NextFunction + ) { // console.log( err.stack ); res.status(500).send("500 Internal Server Error"); }); server = app.listen(3033, function () { - var requestOpts = { + const requestOpts = { url: "http://localhost:3033/login", method: "get", followRedirect: false, }; - request(requestOpts, function (err, response, body) { + function helper(err: Error | null, samlRequest: Buffer) { + try { + should.not.exist(err); + parseString(samlRequest.toString(), function (err, doc) { + try { + should.not.exist(err); + delete doc["samlp:AuthnRequest"]["$"]["ID"]; + delete doc["samlp:AuthnRequest"]["$"]["IssueInstant"]; + doc.should.eql(check.result); + done(); + } catch (err2) { + done(err2); + } + }); + } catch (err2) { + done(err2); + } + } + + // TODO remove usage of request module + request(requestOpts, function (err: Error | null, response: any, body: any) { try { should.not.exist(err); - var encodedSamlRequest; + let encodedSamlRequest; if (check.config.authnRequestBinding === "HTTP-POST") { response.statusCode.should.equal(200); body.should.match(/[^]*/); encodedSamlRequest = body.match(/, + expectedMetadata: string, + signingCert?: string + ) { + const samlObj = new SAML(samlConfig); + const decryptionCert = fs.readFileSync( __dirname + "/static/testshib encryption cert.pem", "utf-8" ); - var metadata = samlObj.generateServiceProviderMetadata(decryptionCert, signingCert); + let metadata = samlObj.generateServiceProviderMetadata(decryptionCert, signingCert); // splits are to get a nice diff if they don't match for some reason metadata.split("\n").should.eql(expectedMetadata.split("\n")); // verify that we are exposed through Strategy as well - var strategy = new SamlStrategy(samlConfig, function () {}); + const strategy = new SamlStrategy(samlConfig, noop); metadata = strategy.generateServiceProviderMetadata(decryptionCert, signingCert); metadata.split("\n").should.eql(expectedMetadata.split("\n")); } it("config with callbackUrl and decryptionPvk should pass", function () { - var samlConfig = { + const samlConfig: Partial = { issuer: "http://example.serviceprovider.com", callbackUrl: "http://example.serviceprovider.com/saml/callback", identifierFormat: "urn:oasis:names:tc:SAML:2.0:nameid-format:transient", decryptionPvk: fs.readFileSync(__dirname + "/static/testshib encryption pvk.pem"), }; - var expectedMetadata = fs.readFileSync( + const expectedMetadata = fs.readFileSync( __dirname + "/static/expected metadata.xml", "utf-8" ); @@ -1556,12 +1641,12 @@ describe("passport-saml /", function () { }); it("config with callbackUrl should pass", function () { - var samlConfig = { + const samlConfig = { issuer: "http://example.serviceprovider.com", callbackUrl: "http://example.serviceprovider.com/saml/callback", identifierFormat: "urn:oasis:names:tc:SAML:2.0:nameid-format:transient", }; - var expectedMetadata = fs.readFileSync( + const expectedMetadata = fs.readFileSync( __dirname + "/static/expected metadata without key.xml", "utf-8" ); @@ -1570,7 +1655,7 @@ describe("passport-saml /", function () { }); it("config with protocol, path, host, and decryptionPvk should pass", function () { - var samlConfig = { + const samlConfig = { issuer: "http://example.serviceprovider.com", protocol: "http://", host: "example.serviceprovider.com", @@ -1578,7 +1663,7 @@ describe("passport-saml /", function () { identifierFormat: "urn:oasis:names:tc:SAML:2.0:nameid-format:transient", decryptionPvk: fs.readFileSync(__dirname + "/static/testshib encryption pvk.pem"), }; - var expectedMetadata = fs.readFileSync( + const expectedMetadata = fs.readFileSync( __dirname + "/static/expected metadata.xml", "utf-8" ); @@ -1587,14 +1672,14 @@ describe("passport-saml /", function () { }); it("config with protocol, path, and host should pass", function () { - var samlConfig = { + const samlConfig = { issuer: "http://example.serviceprovider.com", protocol: "http://", host: "example.serviceprovider.com", path: "/saml/callback", identifierFormat: "urn:oasis:names:tc:SAML:2.0:nameid-format:transient", }; - var expectedMetadata = fs.readFileSync( + const expectedMetadata = fs.readFileSync( __dirname + "/static/expected metadata without key.xml", "utf-8" ); @@ -1603,7 +1688,7 @@ describe("passport-saml /", function () { }); it("config with protocol, path, host, decryptionPvk and privateCert should pass", function () { - var samlConfig = { + const samlConfig = { issuer: "http://example.serviceprovider.com", protocol: "http://", host: "example.serviceprovider.com", @@ -1612,17 +1697,17 @@ describe("passport-saml /", function () { decryptionPvk: fs.readFileSync(__dirname + "/static/testshib encryption pvk.pem"), privateCert: fs.readFileSync(__dirname + "/static/acme_tools_com.key"), }; - var expectedMetadata = fs.readFileSync( + const expectedMetadata = fs.readFileSync( __dirname + "/static/expectedMetadataWithBothKeys.xml", "utf-8" ); - var signingCert = fs.readFileSync(__dirname + "/static/acme_tools_com.cert").toString(); + const signingCert = fs.readFileSync(__dirname + "/static/acme_tools_com.cert").toString(); testMetadata(samlConfig, expectedMetadata, signingCert); }); it("config with protocol, path, host, decryptionPvk and privateKey should pass", function () { - var samlConfig = { + const samlConfig = { issuer: "http://example.serviceprovider.com", protocol: "http://", host: "example.serviceprovider.com", @@ -1631,18 +1716,18 @@ describe("passport-saml /", function () { decryptionPvk: fs.readFileSync(__dirname + "/static/testshib encryption pvk.pem"), privateKey: fs.readFileSync(__dirname + "/static/acme_tools_com.key"), }; - var expectedMetadata = fs.readFileSync( + const expectedMetadata = fs.readFileSync( __dirname + "/static/expectedMetadataWithBothKeys.xml", "utf-8" ); - var signingCert = fs.readFileSync(__dirname + "/static/acme_tools_com.cert").toString(); + const signingCert = fs.readFileSync(__dirname + "/static/acme_tools_com.cert").toString(); testMetadata(samlConfig, expectedMetadata, signingCert); }); }); it("generateServiceProviderMetadata contains logout callback url", function () { - var samlConfig = { + const samlConfig = { issuer: "http://example.serviceprovider.com", callbackUrl: "http://example.serviceprovider.com/saml/callback", identifierFormat: "urn:oasis:names:tc:SAML:2.0:nameid-format:transient", @@ -1650,26 +1735,26 @@ describe("passport-saml /", function () { logoutCallbackUrl: "http://example.serviceprovider.com/logout", }; - var samlObj = new SAML(samlConfig); - var decryptionCert = fs.readFileSync( + const samlObj = new SAML(samlConfig); + const decryptionCert = fs.readFileSync( __dirname + "/static/testshib encryption cert.pem", "utf-8" ); - var metadata = samlObj.generateServiceProviderMetadata(decryptionCert); + const metadata = samlObj.generateServiceProviderMetadata(decryptionCert); metadata.should.containEql("SingleLogoutService"); metadata.should.containEql(samlConfig.logoutCallbackUrl); }); it("#certToPEM should generate valid certificate", function (done) { - var samlConfig = { + const samlConfig = { entryPoint: "https://app.onelogin.com/trust/saml2/http-post/sso/371755", cert: "-----BEGIN CERTIFICATE-----" + TEST_CERT + "-----END CERTIFICATE-----", acceptedClockSkewMs: -1, }; - var samlObj = new SAML(samlConfig); - var certificate = samlObj.certToPEM(samlConfig.cert); + const samlObj = new SAML(samlConfig); + const certificate = samlObj.certToPEM(samlConfig.cert); - if (certificate.match(/BEGIN/g).length == 1 && certificate.match(/END/g).length == 1) { + if (certificate.match(/BEGIN/g)!.length == 1 && certificate.match(/END/g)!.length == 1) { done(); } else { done("Certificate should have only 1 BEGIN and 1 END block"); @@ -1678,11 +1763,11 @@ describe("passport-saml /", function () { describe("validatePostResponse checks /", function () { it("response with junk content should explain the XML or base64 is not valid", function (done) { - var samlObj = new SAML({ cert: TEST_CERT }); + const samlObj = new SAML({ cert: TEST_CERT }); samlObj.validatePostResponse({ SAMLResponse: "BOOM" }, function (err, profile, logout) { try { should.exist(err); - err.message.should.match(/SAMLResponse is not valid base64-encoded XML/); + err!.message!.should.match(/SAMLResponse is not valid base64-encoded XML/); done(); } catch (err2) { done(err2); @@ -1690,18 +1775,19 @@ describe("passport-saml /", function () { }); }); it("response with error status message should generate appropriate error", function (done) { - var xml = + const xml = 'https://idp.testshib.org/idp/shibbolethRequired NameID format not supported'; - var base64xml = Buffer.from(xml).toString("base64"); - var container = { SAMLResponse: base64xml }; - var samlObj = new SAML({ + const base64xml = Buffer.from(xml).toString("base64"); + const container = { SAMLResponse: base64xml }; + const samlObj = new SAML({ cert: "-----BEGIN CERTIFICATE-----" + TEST_CERT + "-----END CERTIFICATE-----", }); samlObj.validatePostResponse(container, function (err, profile, logout) { try { should.exist(err); - err.message.should.match(/Responder/); - err.message.should.match(/Required NameID format not supported/); + err!.message!.should.match(/Responder/); + err!.message!.should.match(/Required NameID format not supported/); + // @ts-expect-error adding extra attr to default Error object should.exist(err.statusXml); done(); } catch (err2) { @@ -1711,16 +1797,17 @@ describe("passport-saml /", function () { }); it("response with error status code should generate appropriate error", function (done) { - var xml = + const xml = 'https://idp.testshib.org/idp/shibboleth'; - var base64xml = Buffer.from(xml).toString("base64"); - var container = { SAMLResponse: base64xml }; - var samlObj = new SAML({}); + const base64xml = Buffer.from(xml).toString("base64"); + const container = { SAMLResponse: base64xml }; + const samlObj = new SAML({}); samlObj.validatePostResponse(container, function (err, profile, logout) { try { should.exist(err); - err.message.should.match(/Responder/); - err.message.should.match(/InvalidNameIDPolicy/); + err!.message!.should.match(/Responder/); + err!.message!.should.match(/InvalidNameIDPolicy/); + // @ts-expect-error adding extra attr to default Error object should.exist(err.statusXml); done(); } catch (err2) { @@ -1730,19 +1817,19 @@ describe("passport-saml /", function () { }); it("accept response with an attributeStatement element without attributeValue", function (done) { - var fakeClock = sinon.useFakeTimers(Date.parse("2015-08-31T08:55:00+00:00")); - var container = { + const fakeClock = sinon.useFakeTimers(Date.parse("2015-08-31T08:55:00+00:00")); + const container = { SAMLResponse: fs .readFileSync(__dirname + "/static/response-with-uncomplete-attribute.xml") .toString("base64"), }; - var samlObj = new SAML(); + const samlObj = new SAML({}); samlObj.validatePostResponse(container, function (err, profile) { try { should.not.exist(err); - profile.issuer.should.eql("https://evil-corp.com"); - profile.nameID.should.eql("vincent.vega@evil-corp.com"); + profile!.issuer!.should.eql("https://evil-corp.com"); + profile!.nameID!.should.eql("vincent.vega@evil-corp.com"); should(profile).have.property("evil-corp.egroupid").eql("vincent.vega@evil-corp.com"); // attributes without attributeValue child should be ignored should(profile).not.have.property("evilcorp.roles"); @@ -1756,47 +1843,43 @@ describe("passport-saml /", function () { }); }); - it("removes InResponseTo value if response validation fails", function (done) { - var requestId = "_a6fc46be84e1e3cf3c50"; - var xml = + it("removes InResponseTo value if response validation fails", async () => { + const requestId = "_a6fc46be84e1e3cf3c50"; + const xml = 'https://app.onelogin.com/saml/metadata/371755' + 'https://app.onelogin.com/saml/metadata/371755DCnPTQYBb1hKspbe6fg1U3q8xn4=e0+aFomA0+JAY0f9tKqzIuqIVSSw7LiFUsneEDKPBWdiTz1sMdgr/2y1e9+rjaS2mRmCi/vSQLY3zTYz0hp6nJNU19+TWoXo9kHQyWT4KkeQL4Xs/gZ/AoKC20iHVKtpPps0IQ0Ml/qRoouSitt6Sf/WDz2LV/pWcH2hx5tv3xSw36hK2NQc7qw7r1mEXnvcjXReYo8rrVf7XHGGxNoRIEICUIi110uvsWemSXf0Z0dyb0FVYOWuSsQMDlzNpheADBifFO4UTfSEhFZvn8kVCGZUIwrbOhZ2d/+YEtgyuTg+qtslgfy4dwd4TvEcfuRzQTazeefprSFyiQckAXOjcw==' + TEST_CERT + 'ben@subspacesw.com{audience}urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport' + ""; - var base64xml = Buffer.from(xml).toString("base64"); - var container = { SAMLResponse: base64xml }; - var samlConfig = { + const base64xml = Buffer.from(xml).toString("base64"); + const container = { SAMLResponse: base64xml }; + const samlConfig = { entryPoint: "https://app.onelogin.com/trust/saml2/http-post/sso/371755", cert: TEST_CERT, validateInResponseTo: true, }; - var samlObj = new SAML(samlConfig); + const samlObj = new SAML(samlConfig); // Mock the SAML request being passed through Passport-SAML - samlObj.cacheProvider.saveAsync(requestId, new Date().toISOString(), function () {}); - - samlObj.validatePostResponse(container, function (err, profile, logout) { - try { - should.exist(err); - err.message.should.match("Invalid signature"); - } catch (err2) { - done(err2); - } - samlObj.validatePostResponse(container, function (err, profile, logout) { - try { - should.exist(err); - err.message.should.match("InResponseTo is not valid"); - done(); - } catch (err2) { - done(err2); - } - }); - }); + await samlObj.cacheProvider.saveAsync(requestId, new Date().toISOString()); + try { + await samlObj.validatePostResponseAsync(container); + should.not.exist(true); + } catch (err) { + should.exist(err); + err!.message!.should.match("Invalid signature"); + } + try { + await samlObj.validatePostResponseAsync(container); + should.not.exist(true); + } catch (err) { + should.exist(err); + err!.message!.should.match("InResponseTo is not valid"); + } }); describe("validatePostResponse xml signature checks /", function () { - var fakeClock; + let fakeClock: sinon.SinonFakeTimers; beforeEach(function () { fakeClock = sinon.useFakeTimers(Date.parse("2014-05-28T00:13:09Z")); }); @@ -1804,24 +1887,24 @@ describe("passport-saml /", function () { fakeClock.restore(); }); - var samlConfig = { + const samlConfig = { entryPoint: "https://app.onelogin.com/trust/saml2/http-post/sso/371755", cert: TEST_CERT, }; it("valid onelogin xml document should validate", function (done) { - var xml = + const xml = 'https://app.onelogin.com/saml/metadata/371755' + 'https://app.onelogin.com/saml/metadata/371755DCnPTQYBb1hKspbe6fg1U3q8xn4=e0+aFomA0+JAY0f9tKqzIuqIVSSw7LiFUsneEDKPBWdiTz1sMdgr/2y1e9+rjaS2mRmCi/vSQLY3zTYz0hp6nJNU19+TWoXo9kHQyWT4KkeQL4Xs/gZ/AoKC20iHVKtpPps0IQ0Ml/qRoouSitt6Sf/WDz2LV/pWcH2hx5tv3xSw36hK2NQc7qw7r1mEXnvcjXReYo8rrVf7XHGGxNoRIEICUIi110uvsWemSXf0Z0dyb0FVYOWuSsQMDlzNpheADBifFO4UTfSEhFZvn8kVCGZUIwrbOhZ2d/+YEtgyuTg+qtslgfy4dwd4TvEcfuRzQTazeefprSFyiQckAXOjcw==' + TEST_CERT + 'ploer@subspacesw.com{audience}urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport' + ""; - var base64xml = Buffer.from(xml).toString("base64"); - var container = { SAMLResponse: base64xml }; - var samlObj = new SAML(samlConfig); + const base64xml = Buffer.from(xml).toString("base64"); + const container = { SAMLResponse: base64xml }; + const samlObj = new SAML(samlConfig); samlObj.validatePostResponse(container, function (err, profile, logout) { try { should.not.exist(err); - profile.nameID.should.startWith("ploer"); + profile!.nameID!.should.startWith("ploer"); done(); } catch (err2) { done(err2); @@ -1830,19 +1913,19 @@ describe("passport-saml /", function () { }); it("onelogin xml document with altered assertion should fail", function (done) { - var xml = + const xml = 'https://app.onelogin.com/saml/metadata/371755' + 'https://app.onelogin.com/saml/metadata/371755DCnPTQYBb1hKspbe6fg1U3q8xn4=e0+aFomA0+JAY0f9tKqzIuqIVSSw7LiFUsneEDKPBWdiTz1sMdgr/2y1e9+rjaS2mRmCi/vSQLY3zTYz0hp6nJNU19+TWoXo9kHQyWT4KkeQL4Xs/gZ/AoKC20iHVKtpPps0IQ0Ml/qRoouSitt6Sf/WDz2LV/pWcH2hx5tv3xSw36hK2NQc7qw7r1mEXnvcjXReYo8rrVf7XHGGxNoRIEICUIi110uvsWemSXf0Z0dyb0FVYOWuSsQMDlzNpheADBifFO4UTfSEhFZvn8kVCGZUIwrbOhZ2d/+YEtgyuTg+qtslgfy4dwd4TvEcfuRzQTazeefprSFyiQckAXOjcw==' + TEST_CERT + 'ben@subspacesw.com{audience}urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport' + ""; - var base64xml = Buffer.from(xml).toString("base64"); - var container = { SAMLResponse: base64xml }; - var samlObj = new SAML(samlConfig); + const base64xml = Buffer.from(xml).toString("base64"); + const container = { SAMLResponse: base64xml }; + const samlObj = new SAML(samlConfig); samlObj.validatePostResponse(container, function (err, profile, logout) { try { should.exist(err); - err.message.should.match(/Invalid signature/); + err!.message!.should.match(/Invalid signature/); done(); } catch (err2) { done(err2); @@ -1851,7 +1934,7 @@ describe("passport-saml /", function () { }); it("onelogin xml document with duplicate altered assertion should fail", function (done) { - var xml = + const xml = 'https://app.onelogin.com/saml/metadata/371755' + 'https://app.onelogin.com/saml/metadata/371755DCnPTQYBb1hKspbe6fg1U3q8xn4=e0+aFomA0+JAY0f9tKqzIuqIVSSw7LiFUsneEDKPBWdiTz1sMdgr/2y1e9+rjaS2mRmCi/vSQLY3zTYz0hp6nJNU19+TWoXo9kHQyWT4KkeQL4Xs/gZ/AoKC20iHVKtpPps0IQ0Ml/qRoouSitt6Sf/WDz2LV/pWcH2hx5tv3xSw36hK2NQc7qw7r1mEXnvcjXReYo8rrVf7XHGGxNoRIEICUIi110uvsWemSXf0Z0dyb0FVYOWuSsQMDlzNpheADBifFO4UTfSEhFZvn8kVCGZUIwrbOhZ2d/+YEtgyuTg+qtslgfy4dwd4TvEcfuRzQTazeefprSFyiQckAXOjcw==' + TEST_CERT + @@ -1860,13 +1943,13 @@ describe("passport-saml /", function () { TEST_CERT + 'ben@subspacesw.com{audience}urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport' + ""; - var base64xml = Buffer.from(xml).toString("base64"); - var container = { SAMLResponse: base64xml }; - var samlObj = new SAML(samlConfig); + const base64xml = Buffer.from(xml).toString("base64"); + const container = { SAMLResponse: base64xml }; + const samlObj = new SAML(samlConfig); samlObj.validatePostResponse(container, function (err, profile, logout) { try { should.exist(err); - err.message.should.match(/Invalid signature/); + err!.message!.should.match(/Invalid signature/); done(); } catch (err2) { done(err2); @@ -1875,20 +1958,20 @@ describe("passport-saml /", function () { }); it("onelogin xml document with extra unsigned & altered assertion should fail", function (done) { - var xml = + const xml = 'https://app.onelogin.com/saml/metadata/371755' + 'https://app.onelogin.com/saml/metadata/371755ben@subspacesw.com{audience}urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport' + 'https://app.onelogin.com/saml/metadata/371755DCnPTQYBb1hKspbe6fg1U3q8xn4=e0+aFomA0+JAY0f9tKqzIuqIVSSw7LiFUsneEDKPBWdiTz1sMdgr/2y1e9+rjaS2mRmCi/vSQLY3zTYz0hp6nJNU19+TWoXo9kHQyWT4KkeQL4Xs/gZ/AoKC20iHVKtpPps0IQ0Ml/qRoouSitt6Sf/WDz2LV/pWcH2hx5tv3xSw36hK2NQc7qw7r1mEXnvcjXReYo8rrVf7XHGGxNoRIEICUIi110uvsWemSXf0Z0dyb0FVYOWuSsQMDlzNpheADBifFO4UTfSEhFZvn8kVCGZUIwrbOhZ2d/+YEtgyuTg+qtslgfy4dwd4TvEcfuRzQTazeefprSFyiQckAXOjcw==' + TEST_CERT + 'ploer@subspacesw.com{audience}urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport' + ""; - var base64xml = Buffer.from(xml).toString("base64"); - var container = { SAMLResponse: base64xml }; - var samlObj = new SAML(samlConfig); + const base64xml = Buffer.from(xml).toString("base64"); + const container = { SAMLResponse: base64xml }; + const samlObj = new SAML(samlConfig); samlObj.validatePostResponse(container, function (err, profile, logout) { try { should.exist(err); - err.message.should.match(/Invalid signature/); + err!.message!.should.match(/Invalid signature/); done(); } catch (err2) { done(err2); @@ -1897,7 +1980,7 @@ describe("passport-saml /", function () { }); it("onelogin xml document with extra nexted assertion should fail", function (done) { - var xml = + const xml = 'https://app.onelogin.com/saml/metadata/371755' + 'https://app.onelogin.com/saml/metadata/371755DCnPTQYBb1hKspbe6fg1U3q8xn4=e0+aFomA0+JAY0f9tKqzIuqIVSSw7LiFUsneEDKPBWdiTz1sMdgr/2y1e9+rjaS2mRmCi/vSQLY3zTYz0hp6nJNU19+TWoXo9kHQyWT4KkeQL4Xs/gZ/AoKC20iHVKtpPps0IQ0Ml/qRoouSitt6Sf/WDz2LV/pWcH2hx5tv3xSw36hK2NQc7qw7r1mEXnvcjXReYo8rrVf7XHGGxNoRIEICUIi110uvsWemSXf0Z0dyb0FVYOWuSsQMDlzNpheADBifFO4UTfSEhFZvn8kVCGZUIwrbOhZ2d/+YEtgyuTg+qtslgfy4dwd4TvEcfuRzQTazeefprSFyiQckAXOjcw==' + TEST_CERT + @@ -1907,13 +1990,13 @@ describe("passport-saml /", function () { "" + 'ploer@subspacesw.com{audience}urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport' + ""; - var base64xml = Buffer.from(xml).toString("base64"); - var container = { SAMLResponse: base64xml }; - var samlObj = new SAML(samlConfig); + const base64xml = Buffer.from(xml).toString("base64"); + const container = { SAMLResponse: base64xml }; + const samlObj = new SAML(samlConfig); samlObj.validatePostResponse(container, function (err, profile, logout) { try { should.exist(err); - err.message.should.match(/Invalid signature/); + err!.message!.should.match(/Invalid signature/); done(); } catch (err2) { done(err2); @@ -1922,23 +2005,23 @@ describe("passport-saml /", function () { }); it("multiple certs should validate with one of the certs", function (done) { - var multiCertSamlConfig = { + const multiCertSamlConfig = { entryPoint: samlConfig.entryPoint, cert: [ALT_TEST_CERT, samlConfig.cert], }; - var xml = + const xml = 'https://app.onelogin.com/saml/metadata/371755' + 'https://app.onelogin.com/saml/metadata/371755DCnPTQYBb1hKspbe6fg1U3q8xn4=e0+aFomA0+JAY0f9tKqzIuqIVSSw7LiFUsneEDKPBWdiTz1sMdgr/2y1e9+rjaS2mRmCi/vSQLY3zTYz0hp6nJNU19+TWoXo9kHQyWT4KkeQL4Xs/gZ/AoKC20iHVKtpPps0IQ0Ml/qRoouSitt6Sf/WDz2LV/pWcH2hx5tv3xSw36hK2NQc7qw7r1mEXnvcjXReYo8rrVf7XHGGxNoRIEICUIi110uvsWemSXf0Z0dyb0FVYOWuSsQMDlzNpheADBifFO4UTfSEhFZvn8kVCGZUIwrbOhZ2d/+YEtgyuTg+qtslgfy4dwd4TvEcfuRzQTazeefprSFyiQckAXOjcw==' + TEST_CERT + 'ploer@subspacesw.com{audience}urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport' + ""; - var base64xml = Buffer.from(xml).toString("base64"); - var container = { SAMLResponse: base64xml }; - var samlObj = new SAML(multiCertSamlConfig); + const base64xml = Buffer.from(xml).toString("base64"); + const container = { SAMLResponse: base64xml }; + const samlObj = new SAML(multiCertSamlConfig); samlObj.validatePostResponse(container, function (err, profile, logout) { try { should.not.exist(err); - profile.nameID.should.startWith("ploer"); + profile!.nameID!.should.startWith("ploer"); done(); } catch (err2) { done(err2); @@ -1947,25 +2030,25 @@ describe("passport-saml /", function () { }); it("cert as a function should validate with the returned cert", function (done) { - var functionCertSamlConfig = { + const functionCertSamlConfig: Partial = { entryPoint: samlConfig.entryPoint, cert: function (callback) { callback(null, samlConfig.cert); }, }; - var xml = + const xml = 'https://app.onelogin.com/saml/metadata/371755' + 'https://app.onelogin.com/saml/metadata/371755DCnPTQYBb1hKspbe6fg1U3q8xn4=e0+aFomA0+JAY0f9tKqzIuqIVSSw7LiFUsneEDKPBWdiTz1sMdgr/2y1e9+rjaS2mRmCi/vSQLY3zTYz0hp6nJNU19+TWoXo9kHQyWT4KkeQL4Xs/gZ/AoKC20iHVKtpPps0IQ0Ml/qRoouSitt6Sf/WDz2LV/pWcH2hx5tv3xSw36hK2NQc7qw7r1mEXnvcjXReYo8rrVf7XHGGxNoRIEICUIi110uvsWemSXf0Z0dyb0FVYOWuSsQMDlzNpheADBifFO4UTfSEhFZvn8kVCGZUIwrbOhZ2d/+YEtgyuTg+qtslgfy4dwd4TvEcfuRzQTazeefprSFyiQckAXOjcw==' + TEST_CERT + 'ploer@subspacesw.com{audience}urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport' + ""; - var base64xml = Buffer.from(xml).toString("base64"); - var container = { SAMLResponse: base64xml }; - var samlObj = new SAML(functionCertSamlConfig); + const base64xml = Buffer.from(xml).toString("base64"); + const container = { SAMLResponse: base64xml }; + const samlObj = new SAML(functionCertSamlConfig); samlObj.validatePostResponse(container, function (err, profile, logout) { try { should.not.exist(err); - profile.nameID.should.startWith("ploer"); + profile!.nameID!.should.startWith("ploer"); done(); } catch (err2) { done(err2); @@ -1974,25 +2057,25 @@ describe("passport-saml /", function () { }); it("cert as a function should validate with one of the returned certs", function (done) { - var functionMultiCertSamlConfig = { + const functionMultiCertSamlConfig: Partial = { entryPoint: samlConfig.entryPoint, cert: function (callback) { callback(null, [ALT_TEST_CERT, samlConfig.cert]); }, }; - var xml = + const xml = 'https://app.onelogin.com/saml/metadata/371755' + 'https://app.onelogin.com/saml/metadata/371755DCnPTQYBb1hKspbe6fg1U3q8xn4=e0+aFomA0+JAY0f9tKqzIuqIVSSw7LiFUsneEDKPBWdiTz1sMdgr/2y1e9+rjaS2mRmCi/vSQLY3zTYz0hp6nJNU19+TWoXo9kHQyWT4KkeQL4Xs/gZ/AoKC20iHVKtpPps0IQ0Ml/qRoouSitt6Sf/WDz2LV/pWcH2hx5tv3xSw36hK2NQc7qw7r1mEXnvcjXReYo8rrVf7XHGGxNoRIEICUIi110uvsWemSXf0Z0dyb0FVYOWuSsQMDlzNpheADBifFO4UTfSEhFZvn8kVCGZUIwrbOhZ2d/+YEtgyuTg+qtslgfy4dwd4TvEcfuRzQTazeefprSFyiQckAXOjcw==' + TEST_CERT + 'ploer@subspacesw.com{audience}urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport' + ""; - var base64xml = Buffer.from(xml).toString("base64"); - var container = { SAMLResponse: base64xml }; - var samlObj = new SAML(functionMultiCertSamlConfig); + const base64xml = Buffer.from(xml).toString("base64"); + const container = { SAMLResponse: base64xml }; + const samlObj = new SAML(functionMultiCertSamlConfig); samlObj.validatePostResponse(container, function (err, profile, logout) { try { should.not.exist(err); - profile.nameID.should.startWith("ploer"); + profile!.nameID!.should.startWith("ploer"); done(); } catch (err2) { done(err2); @@ -2001,25 +2084,25 @@ describe("passport-saml /", function () { }); it("cert as a function should return an error if the cert function returns an error", function (done) { - var errorToReturn = new Error("test"); - var functionErrorCertSamlConfig = { + const errorToReturn = new Error("test"); + const functionErrorCertSamlConfig: Partial = { entryPoint: samlConfig.entryPoint, cert: function (callback) { callback(errorToReturn); }, }; - var xml = + const xml = 'https://app.onelogin.com/saml/metadata/371755' + 'https://app.onelogin.com/saml/metadata/371755DCnPTQYBb1hKspbe6fg1U3q8xn4=e0+aFomA0+JAY0f9tKqzIuqIVSSw7LiFUsneEDKPBWdiTz1sMdgr/2y1e9+rjaS2mRmCi/vSQLY3zTYz0hp6nJNU19+TWoXo9kHQyWT4KkeQL4Xs/gZ/AoKC20iHVKtpPps0IQ0Ml/qRoouSitt6Sf/WDz2LV/pWcH2hx5tv3xSw36hK2NQc7qw7r1mEXnvcjXReYo8rrVf7XHGGxNoRIEICUIi110uvsWemSXf0Z0dyb0FVYOWuSsQMDlzNpheADBifFO4UTfSEhFZvn8kVCGZUIwrbOhZ2d/+YEtgyuTg+qtslgfy4dwd4TvEcfuRzQTazeefprSFyiQckAXOjcw==' + TEST_CERT + 'ploer@subspacesw.com{audience}urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport' + ""; - var base64xml = Buffer.from(xml).toString("base64"); - var container = { SAMLResponse: base64xml }; - var samlObj = new SAML(functionErrorCertSamlConfig); + const base64xml = Buffer.from(xml).toString("base64"); + const container = { SAMLResponse: base64xml }; + const samlObj = new SAML(functionErrorCertSamlConfig); samlObj.validatePostResponse(container, function (err, profile, logout) { try { - err.should.eql(errorToReturn); + err!.should.eql(errorToReturn); done(); } catch (err2) { done(err2); @@ -2052,12 +2135,12 @@ describe("passport-saml /", function () { "" + "" + ""; - var base64xml = Buffer.from(xml).toString("base64"); - var container = { SAMLResponse: base64xml }; - var samlObj = new SAML(); + const base64xml = Buffer.from(xml).toString("base64"); + const container = { SAMLResponse: base64xml }; + const samlObj = new SAML({}); samlObj.validatePostResponse(container, function (err, profile, logout) { should.not.exist(err); - const eptid = profile["urn:oid:1.3.6.1.4.1.5923.1.1.1.10"]; + const eptid = profile!["urn:oid:1.3.6.1.4.1.5923.1.1.1.10"] as any; const nameid = eptid["NameID"][0]; nameid._.should.eql(nameid_opaque_string); nameid.$.NameQualifier.should.equal(nameQualifier); @@ -2082,19 +2165,19 @@ describe("passport-saml /", function () { "" + "" + ""; - var base64xml = Buffer.from(xml).toString("base64"); - var container = { SAMLResponse: base64xml }; - var samlObj = new SAML(); + const base64xml = Buffer.from(xml).toString("base64"); + const container = { SAMLResponse: base64xml }; + const samlObj = new SAML({}); samlObj.validatePostResponse(container, function (err, profile, logout) { should.not.exist(err); - should(profile["attributeName"]).be.undefined(); + should(profile!["attributeName"]).be.undefined(); done(); }); }); }); describe("getAuthorizeUrl request signature checks /", function () { - var fakeClock; + let fakeClock: sinon.SinonFakeTimers; beforeEach(function () { fakeClock = sinon.useFakeTimers(Date.parse("2014-05-28T00:13:09Z")); }); @@ -2103,7 +2186,7 @@ describe("passport-saml /", function () { }); it("acme_tools request signed with sha256", function (done) { - var samlConfig = { + const samlConfig: Partial = { entryPoint: "https://adfs.acme_tools.com/adfs/ls/", issuer: "acme_tools_com", callbackUrl: "https://relyingparty/adfs/postResponse", @@ -2116,18 +2199,18 @@ describe("passport-saml /", function () { customQueryStringParam: "CustomQueryStringParamValue", }, }; - var samlObj = new SAML(samlConfig); + const samlObj = new SAML(samlConfig); samlObj.generateUniqueID = function () { return "12345678901234567890"; }; - samlObj.getAuthorizeUrl({}, {}, function (err, url) { + samlObj.getAuthorizeUrl({} as express.Request, {}, function (err, authorizeUrl) { try { - var qry = require("querystring").parse(require("url").parse(url).query); - qry.SigAlg.should.match("http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"); - qry.Signature.should.match( + const qry = querystring.parse(url.parse(authorizeUrl).query || ""); + qry.SigAlg?.should.match("http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"); + qry.Signature?.should.match( "hel9NaoLU0brY/VhrQsY+lTtuAbTsT/ul6nZ/eVlSMXQRaKn5LTbKadzxmPghX7s4xoHwdah+yZHK/0u4StYSj4b5MKcqbsJapVr2R7H90z8YfGfR2C/G0Gng721YV9Da6VBzKg8Was91zQotgsMpZ9pGX1kPKi6cgFwPwM4NEFugn8AYgXEriNvO5+Q23K/MdBT2bgwRTj2FQCWTuQcgwbyWHXoquHztZ0lbh8UhY5BfQRv7c6D9XPkQEMMQFQeME4PIEg3JnynwFZk5wwhkphMd5nXxau+zt7Nfp4fRm0G8WYnxV1etBnWimwSglZVaSHFYeQBRsC2wvKSiVS8JA==" ); - qry.customQueryStringParam.should.match("CustomQueryStringParamValue"); + qry.customQueryStringParam?.should.match("CustomQueryStringParamValue"); done(); } catch (err2) { done(err2); @@ -2135,7 +2218,7 @@ describe("passport-saml /", function () { }); }); it("acme_tools request signed with sha256 when using privateKey", function (done) { - var samlConfig = { + const samlConfig: Partial = { entryPoint: "https://adfs.acme_tools.com/adfs/ls/", issuer: "acme_tools_com", callbackUrl: "https://relyingparty/adfs/postResponse", @@ -2148,18 +2231,18 @@ describe("passport-saml /", function () { customQueryStringParam: "CustomQueryStringParamValue", }, }; - var samlObj = new SAML(samlConfig); + const samlObj = new SAML(samlConfig); samlObj.generateUniqueID = function () { return "12345678901234567890"; }; - samlObj.getAuthorizeUrl({}, {}, function (err, url) { + samlObj.getAuthorizeUrl({} as express.Request, {}, function (err, authorizeUrl) { try { - var qry = require("querystring").parse(require("url").parse(url).query); - qry.SigAlg.should.match("http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"); - qry.Signature.should.match( + const qry = querystring.parse(url.parse(authorizeUrl).query || ""); + qry.SigAlg?.should.match("http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"); + qry.Signature?.should.match( "hel9NaoLU0brY/VhrQsY+lTtuAbTsT/ul6nZ/eVlSMXQRaKn5LTbKadzxmPghX7s4xoHwdah+yZHK/0u4StYSj4b5MKcqbsJapVr2R7H90z8YfGfR2C/G0Gng721YV9Da6VBzKg8Was91zQotgsMpZ9pGX1kPKi6cgFwPwM4NEFugn8AYgXEriNvO5+Q23K/MdBT2bgwRTj2FQCWTuQcgwbyWHXoquHztZ0lbh8UhY5BfQRv7c6D9XPkQEMMQFQeME4PIEg3JnynwFZk5wwhkphMd5nXxau+zt7Nfp4fRm0G8WYnxV1etBnWimwSglZVaSHFYeQBRsC2wvKSiVS8JA==" ); - qry.customQueryStringParam.should.match("CustomQueryStringParamValue"); + qry.customQueryStringParam?.should.match("CustomQueryStringParamValue"); done(); } catch (err2) { done(err2); @@ -2167,7 +2250,7 @@ describe("passport-saml /", function () { }); }); it("acme_tools request not signed if missing entry point", function (done) { - var samlConfig = { + const samlConfig: Partial = { entryPoint: "", issuer: "acme_tools_com", callbackUrl: "https://relyingparty/adfs/postResponse", @@ -2179,17 +2262,19 @@ describe("passport-saml /", function () { customQueryStringParam: "CustomQueryStringParamValue", }, }; - var samlObj = new SAML(samlConfig); + const samlObj = new SAML(samlConfig); samlObj.generateUniqueID = function () { return "12345678901234567890"; }; - var request = + const request = 'onelogin_samlurn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport'; samlObj.requestToUrl(request, null, "authorize", {}, function (err) { try { should.exist(err); - err.message.should.eql('"entryPoint" config parameter is required for signed messages'); + err!.message!.should.eql( + '"entryPoint" config parameter is required for signed messages' + ); done(); } catch (err2) { done(err2); @@ -2197,7 +2282,7 @@ describe("passport-saml /", function () { }); }); it("acme_tools request not signed if missing entry point when using privateKey", function (done) { - var samlConfig = { + const samlConfig: Partial = { entryPoint: "", issuer: "acme_tools_com", callbackUrl: "https://relyingparty/adfs/postResponse", @@ -2209,17 +2294,19 @@ describe("passport-saml /", function () { customQueryStringParam: "CustomQueryStringParamValue", }, }; - var samlObj = new SAML(samlConfig); + const samlObj = new SAML(samlConfig); samlObj.generateUniqueID = function () { return "12345678901234567890"; }; - var request = + const request = 'onelogin_samlurn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport'; samlObj.requestToUrl(request, null, "authorize", {}, function (err) { try { should.exist(err); - err.message.should.eql('"entryPoint" config parameter is required for signed messages'); + err!.message!.should.eql( + '"entryPoint" config parameter is required for signed messages' + ); done(); } catch (err2) { done(err2); @@ -2227,7 +2314,7 @@ describe("passport-saml /", function () { }); }); it("acme_tools request signed with sha1", function (done) { - var samlConfig = { + const samlConfig: Partial = { entryPoint: "https://adfs.acme_tools.com/adfs/ls/", issuer: "acme_tools_com", callbackUrl: "https://relyingparty/adfs/postResponse", @@ -2240,18 +2327,18 @@ describe("passport-saml /", function () { customQueryStringParam: "CustomQueryStringParamValue", }, }; - var samlObj = new SAML(samlConfig); + const samlObj = new SAML(samlConfig); samlObj.generateUniqueID = function () { return "12345678901234567890"; }; - samlObj.getAuthorizeUrl({}, {}, function (err, url) { + samlObj.getAuthorizeUrl({} as express.Request, {}, function (err, authorizeUrl) { try { - var qry = require("querystring").parse(require("url").parse(url).query); - qry.SigAlg.should.match("http://www.w3.org/2000/09/xmldsig#rsa-sha1"); - qry.Signature.should.match( + const qry = querystring.parse(url.parse(authorizeUrl).query || ""); + qry.SigAlg?.should.match("http://www.w3.org/2000/09/xmldsig#rsa-sha1"); + qry.Signature?.should.match( "MeFo+LjufxP5A+sCRwzR/YH/RV6W14aYSFjUdie62JxkI6hDcVhoSZQUJ3wtWMhL59gJj05tTFnXAZRqUQVsavyy41cmUZVeCsat0gaHBQOILXpp9deB0iSJt1EVQTOJkVx8uu2/WYu/bBiH7w2bpwuCf1gJhlqZb/ca3B6yjHSMjnnVfc2LbNPWHpE5464lrs79VjDXf9GQWfrBr95dh3P51IAb7C+77KDWQUl9WfZfyyuEgS83vyZ0UGOxT4AObJ6NOcLs8+iidDdWJJkBaKQev6U+AghCjLQUYOrflivLIIyqATKu2q9PbOse6Phmnxok50+broXSG23+e+742Q==" ); - qry.customQueryStringParam.should.match("CustomQueryStringParamValue"); + qry.customQueryStringParam?.should.match("CustomQueryStringParamValue"); done(); } catch (err2) { done(err2); @@ -2259,7 +2346,7 @@ describe("passport-saml /", function () { }); }); it("acme_tools request signed with sha1 when using privateKey", function (done) { - var samlConfig = { + const samlConfig: Partial = { entryPoint: "https://adfs.acme_tools.com/adfs/ls/", issuer: "acme_tools_com", callbackUrl: "https://relyingparty/adfs/postResponse", @@ -2272,18 +2359,18 @@ describe("passport-saml /", function () { customQueryStringParam: "CustomQueryStringParamValue", }, }; - var samlObj = new SAML(samlConfig); + const samlObj = new SAML(samlConfig); samlObj.generateUniqueID = function () { return "12345678901234567890"; }; - samlObj.getAuthorizeUrl({}, {}, function (err, url) { + samlObj.getAuthorizeUrl({} as express.Request, {}, function (err, authorizeUrl) { try { - var qry = require("querystring").parse(require("url").parse(url).query); - qry.SigAlg.should.match("http://www.w3.org/2000/09/xmldsig#rsa-sha1"); - qry.Signature.should.match( + const qry = querystring.parse(url.parse(authorizeUrl).query || ""); + qry.SigAlg?.should.match("http://www.w3.org/2000/09/xmldsig#rsa-sha1"); + qry.Signature?.should.match( "MeFo+LjufxP5A+sCRwzR/YH/RV6W14aYSFjUdie62JxkI6hDcVhoSZQUJ3wtWMhL59gJj05tTFnXAZRqUQVsavyy41cmUZVeCsat0gaHBQOILXpp9deB0iSJt1EVQTOJkVx8uu2/WYu/bBiH7w2bpwuCf1gJhlqZb/ca3B6yjHSMjnnVfc2LbNPWHpE5464lrs79VjDXf9GQWfrBr95dh3P51IAb7C+77KDWQUl9WfZfyyuEgS83vyZ0UGOxT4AObJ6NOcLs8+iidDdWJJkBaKQev6U+AghCjLQUYOrflivLIIyqATKu2q9PbOse6Phmnxok50+broXSG23+e+742Q==" ); - qry.customQueryStringParam.should.match("CustomQueryStringParamValue"); + qry.customQueryStringParam?.should.match("CustomQueryStringParamValue"); done(); } catch (err2) { done(err2); @@ -2294,26 +2381,26 @@ describe("passport-saml /", function () { describe("getAdditionalParams checks /", function () { it("should not pass any additional params by default", function () { - var samlConfig = { + const samlConfig = { entryPoint: "https://app.onelogin.com/trust/saml2/http-post/sso/371755", }; - var samlObj = new SAML(samlConfig); + const samlObj = new SAML(samlConfig); ["logout", "authorize"].forEach(function (operation) { - var additionalParams = samlObj.getAdditionalParams({}, operation); + const additionalParams = samlObj.getAdditionalParams({} as express.Request, operation); additionalParams.should.be.empty; }); }); it("should not pass any additional params by default apart from the RelayState in request query", function () { - var samlConfig = { + const samlConfig = { entryPoint: "https://app.onelogin.com/trust/saml2/http-post/sso/371755", }; - var samlObj = new SAML(samlConfig); + const samlObj = new SAML(samlConfig); ["logout", "authorize"].forEach(function (operation) { - var additionalParams = samlObj.getAdditionalParams( - { query: { RelayState: "test" } }, + const additionalParams = samlObj.getAdditionalParams( + ({ query: { RelayState: "test" } } as unknown) as express.Request, operation ); @@ -2323,14 +2410,14 @@ describe("passport-saml /", function () { }); it("should not pass any additional params by default apart from the RelayState in request body", function () { - var samlConfig = { + const samlConfig = { entryPoint: "https://app.onelogin.com/trust/saml2/http-post/sso/371755", }; - var samlObj = new SAML(samlConfig); + const samlObj = new SAML(samlConfig); ["logout", "authorize"].forEach(function (operation) { - var additionalParams = samlObj.getAdditionalParams( - { body: { RelayState: "test" } }, + const additionalParams = samlObj.getAdditionalParams( + { body: { RelayState: "test" } } as express.Request, operation ); @@ -2340,57 +2427,63 @@ describe("passport-saml /", function () { }); it("should pass additional params with all operations if set in additionalParams", function () { - var samlConfig = { + const samlConfig = { entryPoint: "https://app.onelogin.com/trust/saml2/http-post/sso/371755", additionalParams: { queryParam: "queryParamValue", }, }; - var samlObj = new SAML(samlConfig); + const samlObj = new SAML(samlConfig); ["logout", "authorize"].forEach(function (operation) { - var additionalParams = samlObj.getAdditionalParams({}, operation); + const additionalParams = samlObj.getAdditionalParams({} as express.Request, operation); Object.keys(additionalParams).should.have.length(1); additionalParams.should.containEql({ queryParam: "queryParamValue" }); }); }); it('should pass additional params with "authorize" operations if set in additionalAuthorizeParams', function () { - var samlConfig = { + const samlConfig = { entryPoint: "https://app.onelogin.com/trust/saml2/http-post/sso/371755", additionalAuthorizeParams: { queryParam: "queryParamValue", }, }; - var samlObj = new SAML(samlConfig); + const samlObj = new SAML(samlConfig); - var additionalAuthorizeParams = samlObj.getAdditionalParams({}, "authorize"); + const additionalAuthorizeParams = samlObj.getAdditionalParams( + {} as express.Request, + "authorize" + ); Object.keys(additionalAuthorizeParams).should.have.length(1); additionalAuthorizeParams.should.containEql({ queryParam: "queryParamValue" }); - var additionalLogoutParams = samlObj.getAdditionalParams({}, "logout"); + const additionalLogoutParams = samlObj.getAdditionalParams({} as express.Request, "logout"); additionalLogoutParams.should.be.empty; }); it('should pass additional params with "logout" operations if set in additionalLogoutParams', function () { - var samlConfig = { + const samlConfig = { entryPoint: "https://app.onelogin.com/trust/saml2/http-post/sso/371755", additionalLogoutParams: { queryParam: "queryParamValue", }, }; - var samlObj = new SAML(samlConfig); + const samlObj = new SAML(samlConfig); - var additionalAuthorizeParams = samlObj.getAdditionalParams({}, "authorize"); + const additionalAuthorizeParams = samlObj.getAdditionalParams( + {} as express.Request, + "authorize" + ); additionalAuthorizeParams.should.be.empty; - var additionalLogoutParams = samlObj.getAdditionalParams({}, "logout"); + const additionalLogoutParams = samlObj.getAdditionalParams({} as express.Request, "logout"); Object.keys(additionalLogoutParams).should.have.length(1); additionalLogoutParams.should.containEql({ queryParam: "queryParamValue" }); }); it("should merge additionalLogoutParams and additionalAuthorizeParams with additionalParams", function () { - var samlConfig = { + const samlConfig = { entryPoint: "https://app.onelogin.com/trust/saml2/http-post/sso/371755", additionalParams: { queryParam1: "queryParamValue", @@ -2402,16 +2495,19 @@ describe("passport-saml /", function () { queryParam2: "queryParamValueLogout", }, }; - var samlObj = new SAML(samlConfig); + const samlObj = new SAML(samlConfig); - var additionalAuthorizeParams = samlObj.getAdditionalParams({}, "authorize"); + const additionalAuthorizeParams = samlObj.getAdditionalParams( + {} as express.Request, + "authorize" + ); Object.keys(additionalAuthorizeParams).should.have.length(2); additionalAuthorizeParams.should.containEql({ queryParam1: "queryParamValue", queryParam2: "queryParamValueAuthorize", }); - var additionalLogoutParams = samlObj.getAdditionalParams({}, "logout"); + const additionalLogoutParams = samlObj.getAdditionalParams({} as express.Request, "logout"); Object.keys(additionalLogoutParams).should.have.length(2); additionalLogoutParams.should.containEql({ queryParam1: "queryParamValue", @@ -2420,7 +2516,7 @@ describe("passport-saml /", function () { }); it("should merge run-time params additionalLogoutParams and additionalAuthorizeParams with additionalParams", function () { - var samlConfig = { + const samlConfig = { entryPoint: "https://app.onelogin.com/trust/saml2/http-post/sso/371755", additionalParams: { queryParam1: "queryParamValue", @@ -2432,15 +2528,15 @@ describe("passport-saml /", function () { queryParam2: "queryParamValueLogout", }, }; - var samlObj = new SAML(samlConfig); - var options = { + const samlObj = new SAML(samlConfig); + const options = { additionalParams: { queryParam3: "queryParamRuntimeValue", }, }; - var additionalAuthorizeParams = samlObj.getAdditionalParams( - {}, + const additionalAuthorizeParams = samlObj.getAdditionalParams( + {} as express.Request, "authorize", options.additionalParams ); @@ -2451,8 +2547,8 @@ describe("passport-saml /", function () { queryParam3: "queryParamRuntimeValue", }); - var additionalLogoutParams = samlObj.getAdditionalParams( - {}, + const additionalLogoutParams = samlObj.getAdditionalParams( + {} as express.Request, "logout", options.additionalParams ); @@ -2465,7 +2561,7 @@ describe("passport-saml /", function () { }); it("should prioritize additionalLogoutParams and additionalAuthorizeParams over additionalParams", function () { - var samlConfig = { + const samlConfig = { entryPoint: "https://app.onelogin.com/trust/saml2/http-post/sso/371755", additionalParams: { queryParam: "queryParamValue", @@ -2477,19 +2573,22 @@ describe("passport-saml /", function () { queryParam: "queryParamValueLogout", }, }; - var samlObj = new SAML(samlConfig); + const samlObj = new SAML(samlConfig); - var additionalAuthorizeParams = samlObj.getAdditionalParams({}, "authorize"); + const additionalAuthorizeParams = samlObj.getAdditionalParams( + {} as express.Request, + "authorize" + ); Object.keys(additionalAuthorizeParams).should.have.length(1); additionalAuthorizeParams.should.containEql({ queryParam: "queryParamValueAuthorize" }); - var additionalLogoutParams = samlObj.getAdditionalParams({}, "logout"); + const additionalLogoutParams = samlObj.getAdditionalParams({} as express.Request, "logout"); Object.keys(additionalLogoutParams).should.have.length(1); additionalLogoutParams.should.containEql({ queryParam: "queryParamValueLogout" }); }); it("should prioritize run-time params over all other params", function () { - var samlConfig = { + const samlConfig = { entryPoint: "https://app.onelogin.com/trust/saml2/http-post/sso/371755", additionalParams: { queryParam: "queryParamValue", @@ -2501,23 +2600,23 @@ describe("passport-saml /", function () { queryParam: "queryParamValueLogout", }, }; - var samlObj = new SAML(samlConfig); - var options = { + const samlObj = new SAML(samlConfig); + const options = { additionalParams: { queryParam: "queryParamRuntimeValue", }, }; - var additionalAuthorizeParams = samlObj.getAdditionalParams( - {}, + const additionalAuthorizeParams = samlObj.getAdditionalParams( + {} as express.Request, "authorize", options.additionalParams ); Object.keys(additionalAuthorizeParams).should.have.length(1); additionalAuthorizeParams.should.containEql({ queryParam: "queryParamRuntimeValue" }); - var additionalLogoutParams = samlObj.getAdditionalParams( - {}, + const additionalLogoutParams = samlObj.getAdditionalParams( + {} as express.Request, "logout", options.additionalParams ); @@ -2526,13 +2625,15 @@ describe("passport-saml /", function () { }); it("should check the value of the option `RACComparison`", function () { - var samlObjBadComparisonType = new SAML({ RACComparison: "bad_value" }); - should.equal(samlObjBadComparisonType.options.RACComparison, "exact", [ - "the default value of the option `RACComparison` must be exact", - ]); + const samlObjBadComparisonType = new SAML({ RACComparison: "bad_value" as any }); + should.equal( + samlObjBadComparisonType.options.RACComparison, + "exact", + "the default value of the option `RACComparison` must be exact" + ); - var validComparisonTypes = ["exact", "minimum", "maximum", "better"], - samlObjValidComparisonType; + const validComparisonTypes: RACComparision[] = ["exact", "minimum", "maximum", "better"]; + let samlObjValidComparisonType: SAML; validComparisonTypes.forEach(function (RACComparison) { samlObjValidComparisonType = new SAML({ RACComparison: RACComparison }); should.equal(samlObjValidComparisonType.options.RACComparison, RACComparison); @@ -2541,32 +2642,31 @@ describe("passport-saml /", function () { }); describe("InResponseTo validation checks /", function () { - var fakeClock = null; + let fakeClock: sinon.SinonFakeTimers; afterEach(function () { if (fakeClock) { fakeClock.restore(); - fakeClock = null; } }); it("onelogin xml document with InResponseTo from request should validate", async () => { - var requestId = "_a6fc46be84e1e3cf3c50"; - var xml = + const requestId = "_a6fc46be84e1e3cf3c50"; + const xml = 'https://app.onelogin.com/saml/metadata/371755' + 'https://app.onelogin.com/saml/metadata/371755DCnPTQYBb1hKspbe6fg1U3q8xn4=e0+aFomA0+JAY0f9tKqzIuqIVSSw7LiFUsneEDKPBWdiTz1sMdgr/2y1e9+rjaS2mRmCi/vSQLY3zTYz0hp6nJNU19+TWoXo9kHQyWT4KkeQL4Xs/gZ/AoKC20iHVKtpPps0IQ0Ml/qRoouSitt6Sf/WDz2LV/pWcH2hx5tv3xSw36hK2NQc7qw7r1mEXnvcjXReYo8rrVf7XHGGxNoRIEICUIi110uvsWemSXf0Z0dyb0FVYOWuSsQMDlzNpheADBifFO4UTfSEhFZvn8kVCGZUIwrbOhZ2d/+YEtgyuTg+qtslgfy4dwd4TvEcfuRzQTazeefprSFyiQckAXOjcw==' + TEST_CERT + 'ploer@subspacesw.com{audience}urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport' + ""; - var base64xml = Buffer.from(xml).toString("base64"); - var container = { SAMLResponse: base64xml }; + const base64xml = Buffer.from(xml).toString("base64"); + const container = { SAMLResponse: base64xml }; - var samlConfig = { + const samlConfig = { entryPoint: "https://app.onelogin.com/trust/saml2/http-post/sso/371755", cert: TEST_CERT, validateInResponseTo: true, }; - var samlObj = new SAML(samlConfig); + const samlObj = new SAML(samlConfig); fakeClock = sinon.useFakeTimers(Date.parse("2014-05-28T00:13:09Z")); @@ -2574,35 +2674,35 @@ describe("passport-saml /", function () { await samlObj.cacheProvider.saveAsync(requestId, new Date().toISOString()); const { profile, loggedOut } = await samlObj.validatePostResponseAsync(container); - profile.nameID.should.startWith("ploer"); + profile!.nameID!.should.startWith("ploer"); const value = await samlObj.cacheProvider.getAsync(requestId); should.not.exist(value); }); it("onelogin xml document without InResponseTo from request should fail", function (done) { - var requestId = "_a6fc46be84e1e3cf3c50"; - var xml = + const requestId = "_a6fc46be84e1e3cf3c50"; + const xml = 'https://app.onelogin.com/saml/metadata/371755' + 'https://app.onelogin.com/saml/metadata/371755DCnPTQYBb1hKspbe6fg1U3q8xn4=e0+aFomA0+JAY0f9tKqzIuqIVSSw7LiFUsneEDKPBWdiTz1sMdgr/2y1e9+rjaS2mRmCi/vSQLY3zTYz0hp6nJNU19+TWoXo9kHQyWT4KkeQL4Xs/gZ/AoKC20iHVKtpPps0IQ0Ml/qRoouSitt6Sf/WDz2LV/pWcH2hx5tv3xSw36hK2NQc7qw7r1mEXnvcjXReYo8rrVf7XHGGxNoRIEICUIi110uvsWemSXf0Z0dyb0FVYOWuSsQMDlzNpheADBifFO4UTfSEhFZvn8kVCGZUIwrbOhZ2d/+YEtgyuTg+qtslgfy4dwd4TvEcfuRzQTazeefprSFyiQckAXOjcw==' + TEST_CERT + 'ploer@subspacesw.com{audience}urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport' + ""; - var base64xml = Buffer.from(xml).toString("base64"); - var container = { SAMLResponse: base64xml }; + const base64xml = Buffer.from(xml).toString("base64"); + const container = { SAMLResponse: base64xml }; - var samlConfig = { + const samlConfig = { entryPoint: "https://app.onelogin.com/trust/saml2/http-post/sso/371755", cert: TEST_CERT, validateInResponseTo: true, }; - var samlObj = new SAML(samlConfig); + const samlObj = new SAML(samlConfig); fakeClock = sinon.useFakeTimers(Date.parse("2014-05-28T00:13:09Z")); samlObj.validatePostResponse(container, function (err, profile, logout) { try { should.exist(err); - err.message.should.match("InResponseTo is not valid"); + err!.message!.should.match("InResponseTo is not valid"); done(); } catch (err2) { done(err2); @@ -2611,19 +2711,19 @@ describe("passport-saml /", function () { }); it("xml document with SubjectConfirmation InResponseTo from request should be valid", async () => { - var requestId = "_dfab47d5d46374cd4b71"; - var xml = + const requestId = "_dfab47d5d46374cd4b71"; + const xml = 'Verizon IDP HubQecaVjMY/2M4VMJsakvX8uh2Mrg=QTJ//ZHEQRe9/nA5qTkhECZc2u6M1dHzTkujKBedskLSRPL8LRBb4Yftla0zu848sYvLd3SXzEysYu/jrAjaVDevYZIAdyj/3HCw8pS0ZnQDaCgYuAkH4JmYxBfW1Sc9Kr0vbR58ihwWOZd4xHIn/b8xLs8WNsyTHix2etrLGznioLwTOBO3+SgjwSiSP9NUhrlOvolbuu/6xhLi37/L08JaBvOw3o0k4V8xS87SFczhm4f6wvQM5mP6sZAreoNcWZqQM7vIHFjL0/H9vTaLAN8+fQOc81xFtateTKwFQlJMUmdWKZ8L7ns0Uf1xASQjXtSAACbXI+PuVLjz8nnm3g==MIIC7TCCAdmgAwIBAgIQuIdqos+9yKBC4oygbhtdfzAJBgUrDgMCHQUAMBIxEDAOBgNVBAMTB1Rlc3RTVFMwHhcNMTQwNDE2MTIyMTEwWhcNMzkxMjMxMjM1OTU5WjASMRAwDgYDVQQDEwdUZXN0U1RTMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAmhReamVYbeOWwrrAvHPvS9KKBwv4Tj7wOSGDXbNgfjhSvVyXsnpYRcuoJkvE8b9tCjFTbXCfbhnaVrpoXaWFtP1YvUIZvCJGdOOTXltMNDlNIaFmsIsomza8IyOHXe+3xHWVtxO8FG3qnteSkkVIQuAvBqpPfQtxrXCZOlbQZm7q69QIQ64JvLJfRwHN1EywMBVwbJgrV8gBdE3RITI76coSOK13OBTlGtB0kGKLDrF2JW+5mB+WnFR7GlXUj+V0R9WStBomVipJEwr6Q3fU0deKZ5lLw0+qJ0T6APInwN5TIN/AbFCHd51aaf3zEP+tZacQ9fbZqy9XBAtL2pCAJQIDAQABo0cwRTBDBgNVHQEEPDA6gBDECazhZ8Ar+ULXb0YTs5MvoRQwEjEQMA4GA1UEAxMHVGVzdFNUU4IQuIdqos+9yKBC4oygbhtdfzAJBgUrDgMCHQUAA4IBAQAioMSOU9QFw+yhVxGUNK0p/ghVsHnYdeOE3vSRhmFPsetBt8S35sI4QwnQNiuiEYqp++FabiHgePOiqq5oeY6ekJik1qbs7fgwnaQXsxxSucHvc4BU81x24aKy6jeJzxmFxo3mh6y/OI1peCMSH48iUzmhnoSulp0+oAs3gMEFI0ONbgAA/XoAHaVEsrPj10i3gkztoGdpH0DYUe9rABOJxX/3mNF+dCVJG7t7BoSlNAWlSDErKciNNax1nBskFqNWNIKzUKBIb+GVKkIB2QpATMQB6Oe7inUdT9kkZ/Q7oPBATZk+3mFsIoWr8QRFSqvToOhun7EY2/VtuiV1d932Verizon IDP HubUIS/jochen-workUIS/jochen-workUIS usere9aba0c4-ece8-4b44-9526-d24418aa95dctestorgTest User::1'; - var base64xml = Buffer.from(xml).toString("base64"); - var container = { SAMLResponse: base64xml }; + const base64xml = Buffer.from(xml).toString("base64"); + const container = { SAMLResponse: base64xml }; - var samlConfig = { + const samlConfig = { entryPoint: "https://app.onelogin.com/trust/saml2/http-post/sso/371755", cert: "MIIC7TCCAdmgAwIBAgIQuIdqos+9yKBC4oygbhtdfzAJBgUrDgMCHQUAMBIxEDAOBgNVBAMTB1Rlc3RTVFMwHhcNMTQwNDE2MTIyMTEwWhcNMzkxMjMxMjM1OTU5WjASMRAwDgYDVQQDEwdUZXN0U1RTMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAmhReamVYbeOWwrrAvHPvS9KKBwv4Tj7wOSGDXbNgfjhSvVyXsnpYRcuoJkvE8b9tCjFTbXCfbhnaVrpoXaWFtP1YvUIZvCJGdOOTXltMNDlNIaFmsIsomza8IyOHXe+3xHWVtxO8FG3qnteSkkVIQuAvBqpPfQtxrXCZOlbQZm7q69QIQ64JvLJfRwHN1EywMBVwbJgrV8gBdE3RITI76coSOK13OBTlGtB0kGKLDrF2JW+5mB+WnFR7GlXUj+V0R9WStBomVipJEwr6Q3fU0deKZ5lLw0+qJ0T6APInwN5TIN/AbFCHd51aaf3zEP+tZacQ9fbZqy9XBAtL2pCAJQIDAQABo0cwRTBDBgNVHQEEPDA6gBDECazhZ8Ar+ULXb0YTs5MvoRQwEjEQMA4GA1UEAxMHVGVzdFNUU4IQuIdqos+9yKBC4oygbhtdfzAJBgUrDgMCHQUAA4IBAQAioMSOU9QFw+yhVxGUNK0p/ghVsHnYdeOE3vSRhmFPsetBt8S35sI4QwnQNiuiEYqp++FabiHgePOiqq5oeY6ekJik1qbs7fgwnaQXsxxSucHvc4BU81x24aKy6jeJzxmFxo3mh6y/OI1peCMSH48iUzmhnoSulp0+oAs3gMEFI0ONbgAA/XoAHaVEsrPj10i3gkztoGdpH0DYUe9rABOJxX/3mNF+dCVJG7t7BoSlNAWlSDErKciNNax1nBskFqNWNIKzUKBIb+GVKkIB2QpATMQB6Oe7inUdT9kkZ/Q7oPBATZk+3mFsIoWr8QRFSqvToOhun7EY2/VtuiV1d932", validateInResponseTo: true, }; - var samlObj = new SAML(samlConfig); + const samlObj = new SAML(samlConfig); fakeClock = sinon.useFakeTimers(Date.parse("2014-06-05T12:07:07.662Z")); @@ -2631,56 +2731,53 @@ describe("passport-saml /", function () { await samlObj.cacheProvider.saveAsync(requestId, new Date().toISOString()); const { profile } = await samlObj.validatePostResponseAsync(container); - profile.nameID.should.startWith("UIS/jochen-work"); + profile!.nameID!.should.startWith("UIS/jochen-work"); const value = await samlObj.cacheProvider.getAsync(requestId); should.not.exist(value); }); - it("xml document with SubjectConfirmation and missing InResponseTo from request should not be valid", function (done) { - var requestId = "_dfab47d5d46374cd4b71"; - var xml = + it("xml document with SubjectConfirmation and missing InResponseTo from request should not be valid", async () => { + const requestId = "_dfab47d5d46374cd4b71"; + const xml = 'Verizon IDP Hubc8xR7YMU8KAYbkV7Jx3WEBhIqso=jPOrsXdG/YVyGrykXYUbgVK7iX+tNFjMJnOA2iFWOjjtWco9M5DT9tyUsYAag4o4oDUEJribGWhCYn6nvQ24zfW+eJYGwbxO0TSZ26J0iuhnxr+MMFmJVGjxArD70dea0kITssqCxJNKUwmTqteAQ73+qk91H9E9IDoOjMwQERoyD4sAtvfJErRrRontvg9xeQ0BFtyMzJZkwU24QqHvoHyw9/dVO8/NFPydwjaI9uZMu6/QUYKKvkbf6VUXXQUHIiZgX0GCudpB908BqWIcj0dWv8oKGGajQWp+d8Jlx/nxbUTAs8vL1f0dxW3LYCZsDExHmjRQTBhM0pQVMT+HlA==MIICrjCCAZYCCQDWybyUsLVkXzANBgkqhkiG9w0BAQsFADAZMRcwFQYDVQQDFA5hY21lX3Rvb2xzLmNvbTAeFw0xNTA4MTgwODQ3MzZaFw0yNTA4MTcwODQ3MzZaMBkxFzAVBgNVBAMUDmFjbWVfdG9vbHMuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAlyT+OzEymhaZFNfx4+HFxZbBP3egvcUgPvGa7wWCV7vyuCauLBqwO1FQqzaRDxkEihkHqmUz63D25v2QixLxXyqaFQ8TxDFKwYATtSL7x5G2Gww56H0L1XGgYdNW1akPx90P+USmVn1Wb//7AwU+TV+u4jIgKZyTaIFWdFlwBhlp4OBEHCyYwngFgMyVoCBsSmwb4if7Mi5T746J9ZMQpC+ts+kfzley59Nz55pa5fRLwu4qxFUv2oRdXAf2ZLuxB7DPQbRH82/ewZZ8N4BUGiQyAwOsHgp0sb9JJ8uEM/qhyS1dXXxjo+kxsI5HXhxp4P5R9VADuOquaLIo8ptIrQIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQBW/Y7leJnV76+6bzeqqi+buTLyWc1mASi5LVH68mdailg2WmGfKlSMLGzFkNtg8fJnfaRZ/GtxmSxhpQRHn63ZlyzqVrFcJa0qzPG21PXPHG/ny8pN+BV8fk74CIb/+YN7NvDUrV7jlsPxNT2rQk8G2fM7jsTMYvtz0MBkrZZsUzTv4rZkF/v44J/ACDirKJiE+TYArm70yQPweX6RvYHNZLSzgg4o+hoyBXo5BGQetAjmcIhC6ZOwN3iVhGjp0YpWM0pkqStPy3sIR0//LZbskWWlSRb0fX1c4632Xb+zikfec4DniYV6CxkB2U+plHpOX1rt1R+UiTEIhTSXPNt/Verizon IDP HubUIS/jochen-workUIS/jochen-workUIS usere9aba0c4-ece8-4b44-9526-d24418aa95dctestorgTest User::1'; - var base64xml = Buffer.from(xml).toString("base64"); - var container = { SAMLResponse: base64xml }; + const base64xml = Buffer.from(xml).toString("base64"); + const container = { SAMLResponse: base64xml }; - var samlConfig = { + const samlConfig = { entryPoint: "https://app.onelogin.com/trust/saml2/http-post/sso/371755", cert: "MIICrjCCAZYCCQDWybyUsLVkXzANBgkqhkiG9w0BAQsFADAZMRcwFQYDVQQDFA5hY21lX3Rvb2xzLmNvbTAeFw0xNTA4MTgwODQ3MzZaFw0yNTA4MTcwODQ3MzZaMBkxFzAVBgNVBAMUDmFjbWVfdG9vbHMuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAlyT+OzEymhaZFNfx4+HFxZbBP3egvcUgPvGa7wWCV7vyuCauLBqwO1FQqzaRDxkEihkHqmUz63D25v2QixLxXyqaFQ8TxDFKwYATtSL7x5G2Gww56H0L1XGgYdNW1akPx90P+USmVn1Wb//7AwU+TV+u4jIgKZyTaIFWdFlwBhlp4OBEHCyYwngFgMyVoCBsSmwb4if7Mi5T746J9ZMQpC+ts+kfzley59Nz55pa5fRLwu4qxFUv2oRdXAf2ZLuxB7DPQbRH82/ewZZ8N4BUGiQyAwOsHgp0sb9JJ8uEM/qhyS1dXXxjo+kxsI5HXhxp4P5R9VADuOquaLIo8ptIrQIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQBW/Y7leJnV76+6bzeqqi+buTLyWc1mASi5LVH68mdailg2WmGfKlSMLGzFkNtg8fJnfaRZ/GtxmSxhpQRHn63ZlyzqVrFcJa0qzPG21PXPHG/ny8pN+BV8fk74CIb/+YN7NvDUrV7jlsPxNT2rQk8G2fM7jsTMYvtz0MBkrZZsUzTv4rZkF/v44J/ACDirKJiE+TYArm70yQPweX6RvYHNZLSzgg4o+hoyBXo5BGQetAjmcIhC6ZOwN3iVhGjp0YpWM0pkqStPy3sIR0//LZbskWWlSRb0fX1c4632Xb+zikfec4DniYV6CxkB2U+plHpOX1rt1R+UiTEIhTSXPNt/", validateInResponseTo: true, }; - var samlObj = new SAML(samlConfig); + const samlObj = new SAML(samlConfig); fakeClock = sinon.useFakeTimers(Date.parse("2014-06-05T12:07:07.662Z")); // Mock the SAML request being passed through Passport-SAML - samlObj.cacheProvider.saveAsync(requestId, new Date().toISOString(), function () {}); + await samlObj.cacheProvider.saveAsync(requestId, new Date().toISOString()); - samlObj.validatePostResponse(container, function (err, profile, logout) { - try { - should.exist(err); - err.message.should.eql("InResponseTo is missing from response"); - done(); - } catch (err2) { - done(err2); - } - }); + try { + const { profile } = await samlObj.validatePostResponseAsync(container); + should.not.exist(profile); + } catch (err) { + err!.message!.should.eql("InResponseTo is missing from response"); + } }); it("xml document with SubjectConfirmation and missing InResponseTo from request should be not problematic if not validated", async () => { - var requestId = "_dfab47d5d46374cd4b71"; - var xml = + const requestId = "_dfab47d5d46374cd4b71"; + const xml = 'Verizon IDP Hubc8xR7YMU8KAYbkV7Jx3WEBhIqso=jPOrsXdG/YVyGrykXYUbgVK7iX+tNFjMJnOA2iFWOjjtWco9M5DT9tyUsYAag4o4oDUEJribGWhCYn6nvQ24zfW+eJYGwbxO0TSZ26J0iuhnxr+MMFmJVGjxArD70dea0kITssqCxJNKUwmTqteAQ73+qk91H9E9IDoOjMwQERoyD4sAtvfJErRrRontvg9xeQ0BFtyMzJZkwU24QqHvoHyw9/dVO8/NFPydwjaI9uZMu6/QUYKKvkbf6VUXXQUHIiZgX0GCudpB908BqWIcj0dWv8oKGGajQWp+d8Jlx/nxbUTAs8vL1f0dxW3LYCZsDExHmjRQTBhM0pQVMT+HlA==MIICrjCCAZYCCQDWybyUsLVkXzANBgkqhkiG9w0BAQsFADAZMRcwFQYDVQQDFA5hY21lX3Rvb2xzLmNvbTAeFw0xNTA4MTgwODQ3MzZaFw0yNTA4MTcwODQ3MzZaMBkxFzAVBgNVBAMUDmFjbWVfdG9vbHMuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAlyT+OzEymhaZFNfx4+HFxZbBP3egvcUgPvGa7wWCV7vyuCauLBqwO1FQqzaRDxkEihkHqmUz63D25v2QixLxXyqaFQ8TxDFKwYATtSL7x5G2Gww56H0L1XGgYdNW1akPx90P+USmVn1Wb//7AwU+TV+u4jIgKZyTaIFWdFlwBhlp4OBEHCyYwngFgMyVoCBsSmwb4if7Mi5T746J9ZMQpC+ts+kfzley59Nz55pa5fRLwu4qxFUv2oRdXAf2ZLuxB7DPQbRH82/ewZZ8N4BUGiQyAwOsHgp0sb9JJ8uEM/qhyS1dXXxjo+kxsI5HXhxp4P5R9VADuOquaLIo8ptIrQIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQBW/Y7leJnV76+6bzeqqi+buTLyWc1mASi5LVH68mdailg2WmGfKlSMLGzFkNtg8fJnfaRZ/GtxmSxhpQRHn63ZlyzqVrFcJa0qzPG21PXPHG/ny8pN+BV8fk74CIb/+YN7NvDUrV7jlsPxNT2rQk8G2fM7jsTMYvtz0MBkrZZsUzTv4rZkF/v44J/ACDirKJiE+TYArm70yQPweX6RvYHNZLSzgg4o+hoyBXo5BGQetAjmcIhC6ZOwN3iVhGjp0YpWM0pkqStPy3sIR0//LZbskWWlSRb0fX1c4632Xb+zikfec4DniYV6CxkB2U+plHpOX1rt1R+UiTEIhTSXPNt/Verizon IDP HubUIS/jochen-workUIS/jochen-workUIS usere9aba0c4-ece8-4b44-9526-d24418aa95dctestorgTest User::1'; - var base64xml = Buffer.from(xml).toString("base64"); - var container = { SAMLResponse: base64xml }; + const base64xml = Buffer.from(xml).toString("base64"); + const container = { SAMLResponse: base64xml }; - var samlConfig = { + const samlConfig = { entryPoint: "https://app.onelogin.com/trust/saml2/http-post/sso/371755", cert: "MIICrjCCAZYCCQDWybyUsLVkXzANBgkqhkiG9w0BAQsFADAZMRcwFQYDVQQDFA5hY21lX3Rvb2xzLmNvbTAeFw0xNTA4MTgwODQ3MzZaFw0yNTA4MTcwODQ3MzZaMBkxFzAVBgNVBAMUDmFjbWVfdG9vbHMuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAlyT+OzEymhaZFNfx4+HFxZbBP3egvcUgPvGa7wWCV7vyuCauLBqwO1FQqzaRDxkEihkHqmUz63D25v2QixLxXyqaFQ8TxDFKwYATtSL7x5G2Gww56H0L1XGgYdNW1akPx90P+USmVn1Wb//7AwU+TV+u4jIgKZyTaIFWdFlwBhlp4OBEHCyYwngFgMyVoCBsSmwb4if7Mi5T746J9ZMQpC+ts+kfzley59Nz55pa5fRLwu4qxFUv2oRdXAf2ZLuxB7DPQbRH82/ewZZ8N4BUGiQyAwOsHgp0sb9JJ8uEM/qhyS1dXXxjo+kxsI5HXhxp4P5R9VADuOquaLIo8ptIrQIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQBW/Y7leJnV76+6bzeqqi+buTLyWc1mASi5LVH68mdailg2WmGfKlSMLGzFkNtg8fJnfaRZ/GtxmSxhpQRHn63ZlyzqVrFcJa0qzPG21PXPHG/ny8pN+BV8fk74CIb/+YN7NvDUrV7jlsPxNT2rQk8G2fM7jsTMYvtz0MBkrZZsUzTv4rZkF/v44J/ACDirKJiE+TYArm70yQPweX6RvYHNZLSzgg4o+hoyBXo5BGQetAjmcIhC6ZOwN3iVhGjp0YpWM0pkqStPy3sIR0//LZbskWWlSRb0fX1c4632Xb+zikfec4DniYV6CxkB2U+plHpOX1rt1R+UiTEIhTSXPNt/", validateInResponseTo: false, }; - var samlObj = new SAML(samlConfig); + const samlObj = new SAML(samlConfig); fakeClock = sinon.useFakeTimers(Date.parse("2014-06-05T12:07:07.662Z")); @@ -2688,26 +2785,26 @@ describe("passport-saml /", function () { await samlObj.cacheProvider.saveAsync(requestId, new Date().toISOString()); const { profile } = await samlObj.validatePostResponseAsync(container); - profile.nameID.should.startWith("UIS/jochen-work"); + profile!.nameID!.should.startWith("UIS/jochen-work"); const value = await samlObj.cacheProvider.getAsync(requestId); should.exist(value); - value.should.eql("2014-06-05T12:07:07.662Z"); + value!.should.eql("2014-06-05T12:07:07.662Z"); }); it("xml document with multiple AttributeStatements should have all attributes present on profile", async () => { - var requestId = "_dfab47d5d46374cd4b71"; - var xml = + const requestId = "_dfab47d5d46374cd4b71"; + const xml = 'Verizon IDP HubVerizon IDP HubUIS/jochen-workUIS/jochen-workUIS usere9aba0c4-ece8-4b44-9526-d24418aa95dctestorgTest User::1qD+sVCaEdy1dTJoUQdo6o+tYsuU=aLl+1yT7zdT4WnRXKh9cx7WWZnUi/NoxMJWhXP5d+Zu9A4/fjKApSywimU0MTTQxYpvZLjOZPsSwmvc1boJOlXveDsL7A3YWi/f7/zqlVWOfXLE8TVLqUE4jtLsJHFWIJXmh8CI0loqQNf6QcYi9BwCK82FhhXC+qWA5WCZIIWUUMxjxnPbunQ7mninEeW568wqyhb9pLV8QkThzZrZINCqxNvWyGuK/XGPx7ciD6ywbBkdOjlDbwRMaKQ9YeCzZGGzJwOe/NuCXj+oUyzfmzUCobIIR0HYLc4B5UplL7XIKQzpOA2lDDsLe6ZzdTv1qjxSm+dlVfo24onmiPlQUgA=='; - var base64xml = Buffer.from(xml).toString("base64"); - var container = { SAMLResponse: base64xml }; + const base64xml = Buffer.from(xml).toString("base64"); + const container = { SAMLResponse: base64xml }; - var samlConfig = { + const samlConfig = { entryPoint: "https://app.onelogin.com/trust/saml2/http-post/sso/371755", cert: "MIIDtTCCAp2gAwIBAgIJAKg4VeVcIDz1MA0GCSqGSIb3DQEBBQUAMEUxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpTb21lLVN0YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwHhcNMTUwODEzMDE1NDIwWhcNMTUwOTEyMDE1NDIwWjBFMQswCQYDVQQGEwJVUzETMBEGA1UECBMKU29tZS1TdGF0ZTEhMB8GA1UEChMYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxG3ouM7U+fXbJt69X1H6d4UNg/uRr06pFuU9RkfIwNC+yaXyptqB3ynXKsL7BFt4DCd0fflRvJAx3feJIDp16wN9GDVHcufWMYPhh2j5HcTW/j9JoIJzGhJyvO00YKBt+hHy83iN1SdChKv5y0iSyiPP5GnqFw+ayyHoM6hSO0PqBou1Xb0ZSIE+DHosBnvVna5w2AiPY4xrJl9yZHZ4Q7DfMiYTgstjETio4bX+6oLiBnYktn7DjdEslqhffVme4PuBxNojI+uCeg/sn4QVLd/iogMJfDWNuLD8326Mi/FE9cCRvFlvAiMSaebMI3zPaySsxTK7Zgj5TpEbmbHI9wIDAQABo4GnMIGkMB0GA1UdDgQWBBSVGgvoW4MhMuzBGce29PY8vSzHFzB1BgNVHSMEbjBsgBSVGgvoW4MhMuzBGce29PY8vSzHF6FJpEcwRTELMAkGA1UEBhMCVVMxEzARBgNVBAgTClNvbWUtU3RhdGUxITAfBgNVBAoTGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZIIJAKg4VeVcIDz1MAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAJu1rqs+anD74dbdwgd3CnqnQsQDJiEXmBhG2leaGt3ve9b/9gKaJg2pyb2NyppDe1uLqh6nNXDuzg1oNZrPz5pJL/eCXPl7FhxhMUi04TtLf8LeNTCIWYZiFuO4pmhohHcv8kRvYR1+6SkLTC8j/TZerm7qvesSiTQFNapa1eNdVQ8nFwVkEtWl+JzKEM1BlRcn42sjJkijeFp7DpI7pU+PnYeiaXpRv5pJo8ogM1iFxN+SnfEs0EuQ7fhKIG9aHKi7bKZ7L6SyX7MDIGLeulEU6lf5D9BfXNmcMambiS0pXhL2QXajt96UBq8FT2KNXY8XNtR4y6MyyCzhaiZZcc8=", validateInResponseTo: true, }; - var samlObj = new SAML(samlConfig); + const samlObj = new SAML(samlConfig); fakeClock = sinon.useFakeTimers(Date.parse("2014-06-05T12:07:07.662Z")); @@ -2715,52 +2812,45 @@ describe("passport-saml /", function () { await samlObj.cacheProvider.saveAsync(requestId, new Date().toISOString()); const { profile } = await samlObj.validatePostResponseAsync(container); - profile.nameID.should.startWith("UIS/jochen-work"); - profile["vz::identity"].should.equal("UIS/jochen-work"); - profile["vz::subjecttype"].should.equal("UIS user"); - profile["vz::account"].should.equal("e9aba0c4-ece8-4b44-9526-d24418aa95dc"); - profile["vz::org"].should.equal("testorg"); - profile["vz::name"].should.equal("Test User"); - profile["net::ip"].should.equal("::1"); + profile!.nameID!.should.startWith("UIS/jochen-work"); + (profile!["vz::identity"] as string).should.equal("UIS/jochen-work"); + (profile!["vz::subjecttype"] as string).should.equal("UIS user"); + (profile!["vz::account"] as string).should.equal("e9aba0c4-ece8-4b44-9526-d24418aa95dc"); + (profile!["vz::org"] as string).should.equal("testorg"); + (profile!["vz::name"] as string).should.equal("Test User"); + (profile!["net::ip"] as string).should.equal("::1"); const value = await samlObj.cacheProvider.getAsync(requestId); should.not.exist(value); }); describe("InResponseTo server cache expiration tests /", function () { it("should expire a cached request id after the time", async () => { - var requestId = "_dfab47d5d46374cd4b71"; + const requestId = "_dfab47d5d46374cd4b71"; - var samlConfig = { + const samlConfig = { validateInResponseTo: true, requestIdExpirationPeriodMs: 100, }; - var samlObj = new SAML(samlConfig); + const samlObj = new SAML(samlConfig); // Mock the SAML request being passed through Passport-SAML await samlObj.cacheProvider.saveAsync(requestId, new Date().toISOString()); - setTimeout(function () { - samlObj.cacheProvider.getAsync(requestId, function (err, value) { - try { - should.not.exist(value); - done(); - } catch (err2) { - done(err2); - } - }); - }, 300); + await new Promise((resolve) => setTimeout(resolve, 300)); + const value = await samlObj.cacheProvider.getAsync(requestId); + should.not.exist(value); }); it("should expire many cached request ids after the time", async () => { - var expiredRequestId1 = "_dfab47d5d46374cd4b71"; - var expiredRequestId2 = "_dfab47d5d46374cd4b72"; - var requestId = "_dfab47d5d46374cd4b73"; + const expiredRequestId1 = "_dfab47d5d46374cd4b71"; + const expiredRequestId2 = "_dfab47d5d46374cd4b72"; + const requestId = "_dfab47d5d46374cd4b73"; - var samlConfig = { + const samlConfig = { validateInResponseTo: true, requestIdExpirationPeriodMs: 100, }; - var samlObj = new SAML(samlConfig); + const samlObj = new SAML(samlConfig); await samlObj.cacheProvider.saveAsync(expiredRequestId1, new Date().toISOString()); await samlObj.cacheProvider.saveAsync(expiredRequestId2, new Date().toISOString()); @@ -2783,11 +2873,11 @@ describe("passport-saml /", function () { }); describe("assertion condition checks /", function () { - var samlConfig = { + const samlConfig = { entryPoint: "https://app.onelogin.com/trust/saml2/http-post/sso/371755", cert: TEST_CERT, }; - var fakeClock; + let fakeClock: sinon.SinonFakeTimers; beforeEach(function () { fakeClock = sinon.useFakeTimers(Date.parse("2014-05-28T00:13:09Z")); @@ -2798,15 +2888,15 @@ describe("passport-saml /", function () { }); it("onelogin xml document with current time after NotBefore time should validate", function (done) { - var xml = + const xml = 'https://app.onelogin.com/saml/metadata/371755' + 'https://app.onelogin.com/saml/metadata/371755DCnPTQYBb1hKspbe6fg1U3q8xn4=e0+aFomA0+JAY0f9tKqzIuqIVSSw7LiFUsneEDKPBWdiTz1sMdgr/2y1e9+rjaS2mRmCi/vSQLY3zTYz0hp6nJNU19+TWoXo9kHQyWT4KkeQL4Xs/gZ/AoKC20iHVKtpPps0IQ0Ml/qRoouSitt6Sf/WDz2LV/pWcH2hx5tv3xSw36hK2NQc7qw7r1mEXnvcjXReYo8rrVf7XHGGxNoRIEICUIi110uvsWemSXf0Z0dyb0FVYOWuSsQMDlzNpheADBifFO4UTfSEhFZvn8kVCGZUIwrbOhZ2d/+YEtgyuTg+qtslgfy4dwd4TvEcfuRzQTazeefprSFyiQckAXOjcw==' + TEST_CERT + 'ploer@subspacesw.com{audience}urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport' + ""; - var base64xml = Buffer.from(xml).toString("base64"); - var container = { SAMLResponse: base64xml }; - var samlObj = new SAML(samlConfig); + const base64xml = Buffer.from(xml).toString("base64"); + const container = { SAMLResponse: base64xml }; + const samlObj = new SAML(samlConfig); // Fake the current date to be within the valid time range fakeClock.restore(); @@ -2815,7 +2905,7 @@ describe("passport-saml /", function () { samlObj.validatePostResponse(container, function (err, profile, logout) { try { should.not.exist(err); - profile.nameID.should.startWith("ploer"); + profile!.nameID!.should.startWith("ploer"); done(); } catch (err2) { done(err2); @@ -2824,15 +2914,15 @@ describe("passport-saml /", function () { }); it("onelogin xml document with current time equal to NotBefore (plus default clock skew) time should validate", function (done) { - var xml = + const xml = 'https://app.onelogin.com/saml/metadata/371755' + 'https://app.onelogin.com/saml/metadata/371755DCnPTQYBb1hKspbe6fg1U3q8xn4=e0+aFomA0+JAY0f9tKqzIuqIVSSw7LiFUsneEDKPBWdiTz1sMdgr/2y1e9+rjaS2mRmCi/vSQLY3zTYz0hp6nJNU19+TWoXo9kHQyWT4KkeQL4Xs/gZ/AoKC20iHVKtpPps0IQ0Ml/qRoouSitt6Sf/WDz2LV/pWcH2hx5tv3xSw36hK2NQc7qw7r1mEXnvcjXReYo8rrVf7XHGGxNoRIEICUIi110uvsWemSXf0Z0dyb0FVYOWuSsQMDlzNpheADBifFO4UTfSEhFZvn8kVCGZUIwrbOhZ2d/+YEtgyuTg+qtslgfy4dwd4TvEcfuRzQTazeefprSFyiQckAXOjcw==' + TEST_CERT + 'ploer@subspacesw.com{audience}urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport' + ""; - var base64xml = Buffer.from(xml).toString("base64"); - var container = { SAMLResponse: base64xml }; - var samlObj = new SAML(samlConfig); + const base64xml = Buffer.from(xml).toString("base64"); + const container = { SAMLResponse: base64xml }; + const samlObj = new SAML(samlConfig); // Fake the current date to be within the valid time range fakeClock.restore(); @@ -2841,7 +2931,7 @@ describe("passport-saml /", function () { samlObj.validatePostResponse(container, function (err, profile, logout) { try { should.not.exist(err); - profile.nameID.should.startWith("ploer"); + profile!.nameID!.should.startWith("ploer"); done(); } catch (err2) { done(err2); @@ -2850,15 +2940,15 @@ describe("passport-saml /", function () { }); it("onelogin xml document with current time before NotBefore time should fail", function (done) { - var xml = + const xml = 'https://app.onelogin.com/saml/metadata/371755' + 'https://app.onelogin.com/saml/metadata/371755DCnPTQYBb1hKspbe6fg1U3q8xn4=e0+aFomA0+JAY0f9tKqzIuqIVSSw7LiFUsneEDKPBWdiTz1sMdgr/2y1e9+rjaS2mRmCi/vSQLY3zTYz0hp6nJNU19+TWoXo9kHQyWT4KkeQL4Xs/gZ/AoKC20iHVKtpPps0IQ0Ml/qRoouSitt6Sf/WDz2LV/pWcH2hx5tv3xSw36hK2NQc7qw7r1mEXnvcjXReYo8rrVf7XHGGxNoRIEICUIi110uvsWemSXf0Z0dyb0FVYOWuSsQMDlzNpheADBifFO4UTfSEhFZvn8kVCGZUIwrbOhZ2d/+YEtgyuTg+qtslgfy4dwd4TvEcfuRzQTazeefprSFyiQckAXOjcw==' + TEST_CERT + 'ploer@subspacesw.com{audience}urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport' + ""; - var base64xml = Buffer.from(xml).toString("base64"); - var container = { SAMLResponse: base64xml }; - var samlObj = new SAML(samlConfig); + const base64xml = Buffer.from(xml).toString("base64"); + const container = { SAMLResponse: base64xml }; + const samlObj = new SAML(samlConfig); // Fake the current date to be after the valid time range fakeClock.restore(); @@ -2867,7 +2957,7 @@ describe("passport-saml /", function () { samlObj.validatePostResponse(container, function (err, profile, logout) { try { should.exist(err); - err.message.should.match("SAML assertion not yet valid"); + err!.message!.should.match("SAML assertion not yet valid"); done(); } catch (err2) { done(err2); @@ -2876,15 +2966,15 @@ describe("passport-saml /", function () { }); it("onelogin xml document with current time equal to NotOnOrAfter (minus default clock skew) time should fail", function (done) { - var xml = + const xml = 'https://app.onelogin.com/saml/metadata/371755' + 'https://app.onelogin.com/saml/metadata/371755DCnPTQYBb1hKspbe6fg1U3q8xn4=e0+aFomA0+JAY0f9tKqzIuqIVSSw7LiFUsneEDKPBWdiTz1sMdgr/2y1e9+rjaS2mRmCi/vSQLY3zTYz0hp6nJNU19+TWoXo9kHQyWT4KkeQL4Xs/gZ/AoKC20iHVKtpPps0IQ0Ml/qRoouSitt6Sf/WDz2LV/pWcH2hx5tv3xSw36hK2NQc7qw7r1mEXnvcjXReYo8rrVf7XHGGxNoRIEICUIi110uvsWemSXf0Z0dyb0FVYOWuSsQMDlzNpheADBifFO4UTfSEhFZvn8kVCGZUIwrbOhZ2d/+YEtgyuTg+qtslgfy4dwd4TvEcfuRzQTazeefprSFyiQckAXOjcw==' + TEST_CERT + 'ploer@subspacesw.com{audience}urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport' + ""; - var base64xml = Buffer.from(xml).toString("base64"); - var container = { SAMLResponse: base64xml }; - var samlObj = new SAML(samlConfig); + const base64xml = Buffer.from(xml).toString("base64"); + const container = { SAMLResponse: base64xml }; + const samlObj = new SAML(samlConfig); // Fake the current date to be after the valid time range fakeClock.restore(); @@ -2893,7 +2983,7 @@ describe("passport-saml /", function () { samlObj.validatePostResponse(container, function (err, profile, logout) { try { should.exist(err); - err.message.should.match("SAML assertion expired"); + err!.message!.should.match("SAML assertion expired"); done(); } catch (err2) { done(err2); @@ -2902,15 +2992,15 @@ describe("passport-saml /", function () { }); it("onelogin xml document with current time after NotOnOrAfter time (minus default clock skew) should fail", function (done) { - var xml = + const xml = 'https://app.onelogin.com/saml/metadata/371755' + 'https://app.onelogin.com/saml/metadata/371755DCnPTQYBb1hKspbe6fg1U3q8xn4=e0+aFomA0+JAY0f9tKqzIuqIVSSw7LiFUsneEDKPBWdiTz1sMdgr/2y1e9+rjaS2mRmCi/vSQLY3zTYz0hp6nJNU19+TWoXo9kHQyWT4KkeQL4Xs/gZ/AoKC20iHVKtpPps0IQ0Ml/qRoouSitt6Sf/WDz2LV/pWcH2hx5tv3xSw36hK2NQc7qw7r1mEXnvcjXReYo8rrVf7XHGGxNoRIEICUIi110uvsWemSXf0Z0dyb0FVYOWuSsQMDlzNpheADBifFO4UTfSEhFZvn8kVCGZUIwrbOhZ2d/+YEtgyuTg+qtslgfy4dwd4TvEcfuRzQTazeefprSFyiQckAXOjcw==' + TEST_CERT + 'ploer@subspacesw.com{audience}urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport' + ""; - var base64xml = Buffer.from(xml).toString("base64"); - var container = { SAMLResponse: base64xml }; - var samlObj = new SAML(samlConfig); + const base64xml = Buffer.from(xml).toString("base64"); + const container = { SAMLResponse: base64xml }; + const samlObj = new SAML(samlConfig); // Fake the current date to be after the valid time range fakeClock.restore(); @@ -2919,7 +3009,7 @@ describe("passport-saml /", function () { samlObj.validatePostResponse(container, function (err, profile, logout) { try { should.exist(err); - err.message.should.match("SAML assertion expired"); + err!.message!.should.match("SAML assertion expired"); done(); } catch (err2) { done(err2); @@ -2928,21 +3018,21 @@ describe("passport-saml /", function () { }); it("onelogin xml document with current time after NotOnOrAfter time with accepted clock skew equal to -1 should pass", function (done) { - var xml = + const xml = 'https://app.onelogin.com/saml/metadata/371755' + 'https://app.onelogin.com/saml/metadata/371755DCnPTQYBb1hKspbe6fg1U3q8xn4=e0+aFomA0+JAY0f9tKqzIuqIVSSw7LiFUsneEDKPBWdiTz1sMdgr/2y1e9+rjaS2mRmCi/vSQLY3zTYz0hp6nJNU19+TWoXo9kHQyWT4KkeQL4Xs/gZ/AoKC20iHVKtpPps0IQ0Ml/qRoouSitt6Sf/WDz2LV/pWcH2hx5tv3xSw36hK2NQc7qw7r1mEXnvcjXReYo8rrVf7XHGGxNoRIEICUIi110uvsWemSXf0Z0dyb0FVYOWuSsQMDlzNpheADBifFO4UTfSEhFZvn8kVCGZUIwrbOhZ2d/+YEtgyuTg+qtslgfy4dwd4TvEcfuRzQTazeefprSFyiQckAXOjcw==' + TEST_CERT + 'ploer@subspacesw.com{audience}urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport' + ""; - var base64xml = Buffer.from(xml).toString("base64"); - var container = { SAMLResponse: base64xml }; + const base64xml = Buffer.from(xml).toString("base64"); + const container = { SAMLResponse: base64xml }; - var samlConfig = { + const samlConfig = { entryPoint: "https://app.onelogin.com/trust/saml2/http-post/sso/371755", cert: TEST_CERT, acceptedClockSkewMs: -1, }; - var samlObj = new SAML(samlConfig); + const samlObj = new SAML(samlConfig); // Fake the current date to be after the valid time range fakeClock.restore(); @@ -2951,7 +3041,7 @@ describe("passport-saml /", function () { samlObj.validatePostResponse(container, function (err, profile, logout) { try { should.not.exist(err); - profile.nameID.should.startWith("ploer"); + profile!.nameID!.should.startWith("ploer"); done(); } catch (err2) { done(err2); @@ -2960,26 +3050,26 @@ describe("passport-saml /", function () { }); it("onelogin xml document with audience and no AudienceRestriction should not pass", function (done) { - var xml = + const xml = 'https://app.onelogin.com/saml/metadata/371755' + 'https://app.onelogin.com/saml/metadata/371755DCnPTQYBb1hKspbe6fg1U3q8xn4=e0+aFomA0+JAY0f9tKqzIuqIVSSw7LiFUsneEDKPBWdiTz1sMdgr/2y1e9+rjaS2mRmCi/vSQLY3zTYz0hp6nJNU19+TWoXo9kHQyWT4KkeQL4Xs/gZ/AoKC20iHVKtpPps0IQ0Ml/qRoouSitt6Sf/WDz2LV/pWcH2hx5tv3xSw36hK2NQc7qw7r1mEXnvcjXReYo8rrVf7XHGGxNoRIEICUIi110uvsWemSXf0Z0dyb0FVYOWuSsQMDlzNpheADBifFO4UTfSEhFZvn8kVCGZUIwrbOhZ2d/+YEtgyuTg+qtslgfy4dwd4TvEcfuRzQTazeefprSFyiQckAXOjcw==' + TEST_CERT + 'ploer@subspacesw.comurn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport' + ""; - var base64xml = Buffer.from(xml).toString("base64"); - var container = { SAMLResponse: base64xml }; + const base64xml = Buffer.from(xml).toString("base64"); + const container = { SAMLResponse: base64xml }; - var samlConfig = { + const samlConfig = { entryPoint: "https://app.onelogin.com/trust/saml2/http-post/sso/371755", audience: "http://sp.example.com", acceptedClockSkewMs: -1, }; - var samlObj = new SAML(samlConfig); + const samlObj = new SAML(samlConfig); samlObj.validatePostResponse(container, function (err, profile, logout) { try { should.exist(err); - err.message.should.match("SAML assertion has no AudienceRestriction"); + err!.message!.should.match("SAML assertion has no AudienceRestriction"); done(); } catch (err2) { done(err2); @@ -2988,26 +3078,26 @@ describe("passport-saml /", function () { }); it("onelogin xml document with audience not matching AudienceRestriction should not pass", function (done) { - var xml = + const xml = 'https://app.onelogin.com/saml/metadata/371755' + 'https://app.onelogin.com/saml/metadata/371755DCnPTQYBb1hKspbe6fg1U3q8xn4=e0+aFomA0+JAY0f9tKqzIuqIVSSw7LiFUsneEDKPBWdiTz1sMdgr/2y1e9+rjaS2mRmCi/vSQLY3zTYz0hp6nJNU19+TWoXo9kHQyWT4KkeQL4Xs/gZ/AoKC20iHVKtpPps0IQ0Ml/qRoouSitt6Sf/WDz2LV/pWcH2hx5tv3xSw36hK2NQc7qw7r1mEXnvcjXReYo8rrVf7XHGGxNoRIEICUIi110uvsWemSXf0Z0dyb0FVYOWuSsQMDlzNpheADBifFO4UTfSEhFZvn8kVCGZUIwrbOhZ2d/+YEtgyuTg+qtslgfy4dwd4TvEcfuRzQTazeefprSFyiQckAXOjcw==' + TEST_CERT + 'ploer@subspacesw.com{audience}urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport' + ""; - var base64xml = Buffer.from(xml).toString("base64"); - var container = { SAMLResponse: base64xml }; + const base64xml = Buffer.from(xml).toString("base64"); + const container = { SAMLResponse: base64xml }; - var samlConfig = { + const samlConfig = { entryPoint: "https://app.onelogin.com/trust/saml2/http-post/sso/371755", audience: "http://sp.example.com", acceptedClockSkewMs: -1, }; - var samlObj = new SAML(samlConfig); + const samlObj = new SAML(samlConfig); samlObj.validatePostResponse(container, function (err, profile, logout) { try { should.exist(err); - err.message.should.match("SAML assertion audience mismatch"); + err!.message!.should.match("SAML assertion audience mismatch"); done(); } catch (err2) { done(err2); @@ -3016,26 +3106,26 @@ describe("passport-saml /", function () { }); it("onelogin xml document with audience matching AudienceRestriction should pass", function (done) { - var xml = + const xml = 'https://app.onelogin.com/saml/metadata/371755' + 'https://app.onelogin.com/saml/metadata/371755DCnPTQYBb1hKspbe6fg1U3q8xn4=e0+aFomA0+JAY0f9tKqzIuqIVSSw7LiFUsneEDKPBWdiTz1sMdgr/2y1e9+rjaS2mRmCi/vSQLY3zTYz0hp6nJNU19+TWoXo9kHQyWT4KkeQL4Xs/gZ/AoKC20iHVKtpPps0IQ0Ml/qRoouSitt6Sf/WDz2LV/pWcH2hx5tv3xSw36hK2NQc7qw7r1mEXnvcjXReYo8rrVf7XHGGxNoRIEICUIi110uvsWemSXf0Z0dyb0FVYOWuSsQMDlzNpheADBifFO4UTfSEhFZvn8kVCGZUIwrbOhZ2d/+YEtgyuTg+qtslgfy4dwd4TvEcfuRzQTazeefprSFyiQckAXOjcw==' + TEST_CERT + 'ploer@subspacesw.comhttp://sp.example.comurn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport' + ""; - var base64xml = Buffer.from(xml).toString("base64"); - var container = { SAMLResponse: base64xml }; + const base64xml = Buffer.from(xml).toString("base64"); + const container = { SAMLResponse: base64xml }; - var samlConfig = { + const samlConfig = { entryPoint: "https://app.onelogin.com/trust/saml2/http-post/sso/371755", audience: "http://sp.example.com", acceptedClockSkewMs: -1, }; - var samlObj = new SAML(samlConfig); + const samlObj = new SAML(samlConfig); samlObj.validatePostResponse(container, function (err, profile, logout) { try { should.not.exist(err); - profile.nameID.should.startWith("ploer"); + profile!.nameID!.should.startWith("ploer"); done(); } catch (err2) { done(err2); @@ -3045,7 +3135,7 @@ describe("passport-saml /", function () { }); }); describe("validatePostRequest()", function () { - var samlObj; + let samlObj: SAML; beforeEach(function () { samlObj = new SAML({ cert: fs.readFileSync(__dirname + "/static/cert.pem", "ascii"), @@ -3053,7 +3143,7 @@ describe("passport-saml /", function () { }); it("errors if bad xml", function (done) { - var body = { + const body = { SAMLRequest: "asdf", }; samlObj.validatePostRequest(body, function (err) { @@ -3066,7 +3156,7 @@ describe("passport-saml /", function () { }); }); it("errors if bad signature", function (done) { - var body = { + const body = { SAMLRequest: fs.readFileSync( __dirname + "/static/logout_request_with_bad_signature.xml", "base64" @@ -3075,7 +3165,7 @@ describe("passport-saml /", function () { samlObj.validatePostRequest(body, function (err) { try { should.exist(err); - err.should.eql(new Error("Invalid signature on documentElement")); + err!.should.eql(new Error("Invalid signature on documentElement")); done(); } catch (err2) { done(err2); @@ -3083,7 +3173,7 @@ describe("passport-saml /", function () { }); }); it("returns profile for valid signature", function (done) { - var body = { + const body = { SAMLRequest: fs.readFileSync( __dirname + "/static/logout_request_with_good_signature.xml", "base64" @@ -3092,7 +3182,7 @@ describe("passport-saml /", function () { samlObj.validatePostRequest(body, function (err, profile) { try { should.not.exist(err); - profile.should.eql({ + profile!.should.eql({ ID: "pfxd4d369e8-9ea1-780c-aff8-a1d11a9862a1", issuer: "http://sp.example.com/demo1/metadata.php", nameID: "ONELOGIN_f92cc1834efc0f73e9c09f482fce80037a6251e7", @@ -3105,7 +3195,7 @@ describe("passport-saml /", function () { }); }); it("returns profile for valid signature including session index", function (done) { - var body = { + const body = { SAMLRequest: fs.readFileSync( __dirname + "/static/logout_request_with_session_index.xml", "base64" @@ -3114,7 +3204,7 @@ describe("passport-saml /", function () { samlObj.validatePostRequest(body, function (err, profile) { try { should.not.exist(err); - profile.should.eql({ + profile!.should.eql({ ID: "pfxd4d369e8-9ea1-780c-aff8-a1d11a9862a1", issuer: "http://sp.example.com/demo1/metadata.php", nameID: "ONELOGIN_f92cc1834efc0f73e9c09f482fce80037a6251e7", @@ -3128,11 +3218,11 @@ describe("passport-saml /", function () { }); }); it("returns profile for valid signature with encrypted nameID", function (done) { - var samlObj = new SAML({ + const samlObj = new SAML({ cert: fs.readFileSync(__dirname + "/static/cert.pem", "ascii"), decryptionPvk: fs.readFileSync(__dirname + "/static/key.pem", "ascii"), }); - var body = { + const body = { SAMLRequest: fs.readFileSync( __dirname + "/static/logout_request_with_encrypted_name_id.xml", "base64" @@ -3141,7 +3231,7 @@ describe("passport-saml /", function () { samlObj.validatePostRequest(body, function (err, profile) { try { should.not.exist(err); - profile.should.eql({ + profile!.should.eql({ ID: "pfx00cb5227-d9d0-1d4b-bdb2-c7ad6c3c6906", issuer: "http://sp.example.com/demo1/metadata.php", nameID: "ONELOGIN_f92cc1834efc0f73e9c09f482fce80037a6251e7", @@ -3156,11 +3246,11 @@ describe("passport-saml /", function () { }); }); it("validatePostRequest errors for encrypted nameID with wrong decryptionPvk", function (done) { - var samlObj = new SAML({ + const samlObj = new SAML({ cert: fs.readFileSync(__dirname + "/static/cert.pem", "ascii"), decryptionPvk: fs.readFileSync(__dirname + "/static/acme_tools_com.key", "ascii"), }); - var body = { + const body = { SAMLRequest: fs.readFileSync( __dirname + "/static/logout_request_with_encrypted_name_id.xml", "base64" @@ -3175,7 +3265,7 @@ describe("passport-saml /", function () { } }); it("errors if bad privateCert to requestToURL", function (done) { - var samlObj = new SAML({ + const samlObj = new SAML({ entryPoint: "foo", privateCert: "-----BEGIN CERTIFICATE-----\n" + @@ -3205,12 +3295,12 @@ describe("passport-saml /", function () { "PCiRGSm8eupuxfix05LMMreo4mC7e3Ir4JhdCsXxAMZIvbNyXcvUMA==\n" + "-----END CERTIFICATE-----\n", }); - var request = + const request = 'onelogin_samlurn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport'; samlObj.requestToUrl(request, null, "authorize", {}, function (err) { try { should.exist(err); - err.message.should.containEql("no start line"); + err!.message.should.containEql("no start line"); done(); } catch (err2) { done(err2); @@ -3218,7 +3308,7 @@ describe("passport-saml /", function () { }); }); it("errors if bad privateKey to requestToURL", function (done) { - var samlObj = new SAML({ + const samlObj = new SAML({ entryPoint: "foo", privateKey: "-----BEGIN CERTIFICATE-----\n" + @@ -3248,12 +3338,12 @@ describe("passport-saml /", function () { "PCiRGSm8eupuxfix05LMMreo4mC7e3Ir4JhdCsXxAMZIvbNyXcvUMA==\n" + "-----END CERTIFICATE-----\n", }); - var request = + const request = 'onelogin_samlurn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport'; samlObj.requestToUrl(request, null, "authorize", {}, function (err) { try { should.exist(err); - err.message.should.containEql("no start line"); + err!.message!.should.containEql("no start line"); done(); } catch (err2) { done(err2); @@ -3264,20 +3354,23 @@ describe("passport-saml /", function () { describe("validateRedirect()", function () { describe("idp slo", function () { - var samlObj; + let samlObj: SAML; beforeEach(function () { samlObj = new SAML({ cert: fs.readFileSync(__dirname + "/static/acme_tools_com.cert", "ascii"), idpIssuer: "http://localhost:20000/saml2/idp/metadata.php", }); - this.request = Object.assign({}, require("./static/idp_slo_redirect")); + this.request = Object.assign( + {}, + JSON.parse(fs.readFileSync(__dirname + "/static/idp_slo_redirect.json", "utf8")) + ); this.clock = sinon.useFakeTimers(Date.parse("2018-04-11T14:08:00Z")); }); afterEach(function () { this.clock.restore(); }); it("errors if bad xml", function (done) { - var body = { + const body = { SAMLRequest: "asdf", }; samlObj.validateRedirect(body, this.request.originalQuery, function (err) { @@ -3294,7 +3387,7 @@ describe("passport-saml /", function () { samlObj.validateRedirect(this.request, this.request.originalQuery, function (err) { try { should.exist(err); - err.message.should.eql( + err!.message!.should.eql( "Unknown SAML issuer. Expected: foo Received: http://localhost:20000/saml2/idp/metadata.php" ); done(); @@ -3308,7 +3401,7 @@ describe("passport-saml /", function () { samlObj.validateRedirect(this.request, this.request.originalQuery, function (err) { try { should.exist(err); - err.message.should.eql("SAML assertion expired"); + err!.message!.should.eql("SAML assertion expired"); done(); } catch (err2) { done(err2); @@ -3317,21 +3410,25 @@ describe("passport-saml /", function () { }); it("errors if request has a bad signature", function (done) { this.request.Signature = "foo"; - samlObj.validateRedirect(this.request, this.request.originalQuery, function (err) { - try { - should.exist(err); - err.message.should.eql("Invalid signature"); - done(); - } catch (err2) { - done(err2); + samlObj.validateRedirect( + this.request, + this.request.originalQuery, + function (err: Error | null) { + try { + should.exist(err); + err!.message!.should.eql("Invalid signature"); + done(); + } catch (err2) { + done(err2); + } } - }); + ); }); it("returns profile for valid signature including session index", function (done) { samlObj.validateRedirect(this.request, this.request.originalQuery, function (err, profile) { try { should.not.exist(err); - profile.should.eql({ + profile!.should.eql({ ID: "_8f0effde308adfb6ae7f1e29b414957fc320f5636f", issuer: "http://localhost:20000/saml2/idp/metadata.php", nameID: "stavros@workable.com", @@ -3346,14 +3443,17 @@ describe("passport-saml /", function () { }); }); describe("sp slo", function () { - var samlObj; + let samlObj: SAML; beforeEach(function () { samlObj = new SAML({ cert: fs.readFileSync(__dirname + "/static/acme_tools_com.cert", "ascii"), idpIssuer: "http://localhost:20000/saml2/idp/metadata.php", validateInResponseTo: true, }); - this.request = Object.assign({}, require("./static/sp_slo_redirect")); + this.request = Object.assign( + {}, + JSON.parse(fs.readFileSync(__dirname + "/static/sp_slo_redirect.json", "utf8")) + ); this.clock = sinon.useFakeTimers(Date.parse("2018-04-11T14:08:00Z")); }); afterEach(async function () { @@ -3361,7 +3461,7 @@ describe("passport-saml /", function () { await samlObj.cacheProvider.removeAsync("_79db1e7ad12ca1d63e5b"); }); it("errors if bad xml", function (done) { - var body = { + const body = { SAMLRequest: "asdf", }; samlObj.validateRedirect(body, null, function (err) { @@ -3378,7 +3478,7 @@ describe("passport-saml /", function () { samlObj.validateRedirect(this.request, this.request.originalQuery, function (err) { try { should.exist(err); - err.message.should.eql( + err!.message!.should.eql( "Unknown SAML issuer. Expected: foo Received: http://localhost:20000/saml2/idp/metadata.php" ); done(); @@ -3388,11 +3488,15 @@ describe("passport-saml /", function () { }); }); it("errors if unsuccessful", function (done) { - this.request = require("./static/sp_slo_redirect_failure"); + this.request = JSON.parse( + fs.readFileSync(__dirname + "/static/sp_slo_redirect_failure.json", "utf8") + ); samlObj.validateRedirect(this.request, this.request.originalQuery, function (err) { try { should.exist(err); - err.message.should.eql("Bad status code: urn:oasis:names:tc:SAML:2.0:status:Requester"); + err!.message!.should.eql( + "Bad status code: urn:oasis:names:tc:SAML:2.0:status:Requester" + ); done(); } catch (err2) { done(err2); @@ -3403,71 +3507,44 @@ describe("passport-saml /", function () { samlObj.validateRedirect(this.request, this.request.originalQuery, function (err) { try { should.exist(err); - err.message.should.eql("InResponseTo is not valid"); + err!.message!.should.eql("InResponseTo is not valid"); done(); } catch (err2) { done(err2); } }); }); - it("errors if bad signature", function (done) { - samlObj.cacheProvider.saveAsync( - "_79db1e7ad12ca1d63e5b", - new Date().toISOString(), - function () {} - ); + it("errors if bad signature", async function () { + await samlObj.cacheProvider.saveAsync("_79db1e7ad12ca1d63e5b", new Date().toISOString()); this.request.Signature = "foo"; - samlObj.validateRedirect(this.request, this.request.originalQuery, function (err) { - try { - should.exist(err); - err.message.should.eql("Invalid signature"); - done(); - } catch (err2) { - done(err2); - } - }); + try { + await samlObj.validateRedirectAsync(this.request, this.request.originalQuery); + } catch (err) { + should.exist(err); + err!.message!.should.eql("Invalid signature"); + } }); - it("returns true for valid signature", function (done) { - samlObj.cacheProvider.saveAsync( - "_79db1e7ad12ca1d63e5b", - new Date().toISOString(), - function () {} - ); - samlObj.validateRedirect( + it("returns true for valid signature", async function () { + await samlObj.cacheProvider.saveAsync("_79db1e7ad12ca1d63e5b", new Date().toISOString()); + const { loggedOut } = await samlObj.validateRedirectAsync( this.request, - this.request.originalQuery, - function (err, _data, success) { - try { - should.not.exist(err); - success.should.eql(true); - done(); - } catch (err2) { - done(err2); - } - } + this.request.originalQuery ); + loggedOut!.should.eql(true); }); - it("accepts cert without header and footer line", function (done) { + it("accepts cert without header and footer line", async function () { samlObj.options.cert = fs.readFileSync( __dirname + "/static/acme_tools_com_without_header_and_footer.cert", "ascii" ); - samlObj.cacheProvider.saveAsync( - "_79db1e7ad12ca1d63e5b", - new Date().toISOString(), - function () {} - ); - samlObj.validateRedirect( + await samlObj.cacheProvider.saveAsync("_79db1e7ad12ca1d63e5b", new Date().toISOString()); + const { loggedOut } = await samlObj.validateRedirectAsync( this.request, - this.request.originalQuery, - function (err, _data, success) { - should.not.exist(err); - success.should.eql(true); - done(); - } + this.request.originalQuery ); + loggedOut!.should.eql(true); }); }); }); diff --git a/tsconfig.json b/tsconfig.json index 53f83e82..2b0f3fd4 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -44,7 +44,6 @@ // "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */ // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ - // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ // "typeRoots": [], /* List of folders to include type definitions from. */ // "types": [], /* Type declaration files to be included in compilation. */ // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ @@ -70,8 +69,8 @@ "node_modules", "docs", "lib", - "test", "multiSamlStrategy.js", - "multiSamlStrategy.d.ts" + "multiSamlStrategy.d.ts", + "test" ] }