Skip to content

Commit 224f25f

Browse files
authored
Require cert for every strategy (#548)
A lot of tests are fixed. There is one test that wasn't hermetically sealed.
1 parent d89bdfd commit 224f25f

21 files changed

+2342
-1950
lines changed

.prettierignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,5 @@
22
node_modules
33
lib
44
package-lock.json
5+
.eslintcache
6+
.prettierignore

README.md

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,8 @@ passport.use(new SamlStrategy(
3030
{
3131
path: '/login/callback',
3232
entryPoint: 'https://openidp.feide.no/simplesaml/saml2/idp/SSOService.php',
33-
issuer: 'passport-saml'
33+
issuer: 'passport-saml',
34+
cert: 'fake cert', // cert must be provided
3435
},
3536
function(profile, done) {
3637
findByEmail(profile.email, function(err, user) {
@@ -53,7 +54,7 @@ const { MultiSamlStrategy } = require('passport-saml');
5354

5455
passport.use(new MultiSamlStrategy(
5556
{
56-
passReqToCallback: true, //makes req available in callback
57+
passReqToCallback: true, // makes req available in callback
5758
getSamlOptions: function(request, done) {
5859
findProvider(request, function(err, provider) {
5960
if (err) {
@@ -124,18 +125,18 @@ type Profile = {
124125
- **Additional SAML behaviors**
125126
- `additionalParams`: dictionary of additional query params to add to all requests; if an object with this key is passed to `authenticate`, the dictionary of additional query params will be appended to those present on the returned URL, overriding any specified by initialization options' additional parameters (`additionalParams`, `additionalAuthorizeParams`, and `additionalLogoutParams`)
126127
- `additionalAuthorizeParams`: dictionary of additional query params to add to 'authorize' requests
127-
- `identifierFormat`: if truthy, name identifier format to request from identity provider (default: `urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress`)
128+
- `identifierFormat`: optional name identifier format to request from identity provider (default: `urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress`)
128129
- `acceptedClockSkewMs`: Time in milliseconds of skew that is acceptable between client and server when checking `OnBefore` and `NotOnOrAfter` assertion condition validity timestamps. Setting to `-1` will disable checking these conditions entirely. Default is `0`.
129130
- `attributeConsumingServiceIndex`: optional `AttributeConsumingServiceIndex` attribute to add to AuthnRequest to instruct the IDP which attribute set to attach to the response ([link](http://blog.aniljohn.com/2014/01/data-minimization-front-channel-saml-attribute-requests.html))
130131
- `disableRequestedAuthnContext`: if truthy, do not request a specific authentication context. This is [known to help when authenticating against Active Directory](https://github.com/node-saml/passport-saml/issues/226) (AD FS) servers.
131132
- `authnContext`: if truthy, name identifier format to request auth context (default: `urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport`); array of values is also supported
132-
- `RACComparison`: Requested Authentication Context comparison type. Possible values are 'exact','minimum','maximum','better'. Default is 'exact'.
133+
- `racComparison`: Requested Authentication Context comparison type. Possible values are 'exact','minimum','maximum','better'. Default is 'exact'.
133134

134135
- `forceAuthn`: if set to true, the initial SAML request from the service provider specifies that the IdP should force re-authentication of the user, even if they possess a valid session.
135136
- `providerName`: optional human-readable name of the requester for use by the presenter's user agent or the identity provider
136137
- `skipRequestCompression`: if set to true, the SAML request from the service provider won't be compressed.
137138
- `authnRequestBinding`: if set to `HTTP-POST`, will request authentication from IDP via HTTP POST binding, otherwise defaults to HTTP Redirect
138-
- `disableRequestACSUrl`: if truthy, SAML AuthnRequest from the service provider will not include the optional AssertionConsumerServiceURL. Default is falsy so it is automatically included.
139+
- `disableRequestAcsUrl`: if truthy, SAML AuthnRequest from the service provider will not include the optional AssertionConsumerServiceURL. Default is falsy so it is automatically included.
139140
- `scoping`: An optional configuration which implements the functionality [explained in the SAML spec paragraph "3.4.1.2 Element <Scoping>"](https://docs.oasis-open.org/security/saml/v2.0/saml-core-2.0-os.pdf). The config object is structured as following:
140141

141142
```javascript

docs/adfs/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ passport.use(
5555
identifierFormat: null,
5656
// this is configured under the Advanced tab in AD FS relying party
5757
signatureAlgorithm: "sha256",
58-
RACComparison: "exact", // default to exact RequestedAuthnContext Comparison Type
58+
racComparison: "exact", // default to exact RequestedAuthnContext Comparison Type
5959
},
6060
function (profile, done) {
6161
return done(null, {

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
"prettier-format": "prettier --config .prettierrc.json --write .",
4646
"prettier-watch": "onchange -k -p 100 \".\" -- prettier --config .prettierrc.json --write {{file}}",
4747
"test": "npm run prettier-check && npm run lint && npm run tsc && mocha",
48+
"test-watch": "mocha --watch",
4849
"tsc": "tsc",
4950
"tsc-watch": "tsc --watch",
5051
"watch": "concurrently --kill-others \"npm:*-watch\""

src/passport-saml/inmemory-cache-provider.ts

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -29,15 +29,10 @@ export class CacheProvider {
2929
constructor(options: Partial<CacheProviderOptions>) {
3030
this.cacheKeys = {};
3131

32-
if (!options) {
33-
options = {};
34-
}
35-
36-
if (!options.keyExpirationPeriodMs) {
37-
options.keyExpirationPeriodMs = 28800000; // 8 hours
38-
}
39-
40-
this.options = options as CacheProviderOptions;
32+
this.options = {
33+
...options,
34+
keyExpirationPeriodMs: options?.keyExpirationPeriodMs ?? 28800000, // 8 hours,
35+
};
4136

4237
// Expire old cache keys
4338
const expirationTimer = setInterval(() => {

src/passport-saml/multiSamlStrategy.ts

Lines changed: 19 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -8,35 +8,35 @@ import {
88
AuthorizeOptions,
99
MultiSamlConfig,
1010
RequestWithUser,
11+
SamlConfig,
1112
VerifyWithoutRequest,
1213
VerifyWithRequest,
1314
} from "./types";
1415

1516
class MultiSamlStrategy extends SamlStrategy {
16-
_options: MultiSamlConfig;
17+
static readonly newSamlProviderOnConstruct = false;
18+
19+
_options: SamlConfig & MultiSamlConfig;
1720

1821
constructor(options: MultiSamlConfig, verify: VerifyWithRequest);
1922
constructor(options: MultiSamlConfig, verify: VerifyWithoutRequest);
2023
constructor(options: MultiSamlConfig, verify: never) {
21-
if (!options || typeof options.getSamlOptions != "function") {
24+
if (!options || typeof options.getSamlOptions !== "function") {
2225
throw new Error("Please provide a getSamlOptions function");
2326
}
2427

25-
if (!options.requestIdExpirationPeriodMs) {
26-
options.requestIdExpirationPeriodMs = 28800000; // 8 hours
27-
}
28-
29-
if (!options.cacheProvider) {
30-
options.cacheProvider = new InMemoryCacheProvider({
31-
keyExpirationPeriodMs: options.requestIdExpirationPeriodMs,
32-
});
33-
}
28+
// Force the type on this since we've disabled `newOnConstruct`
29+
// so the `SAML` constructor will not be called at this time
30+
// and there are defaults for all `strategy`-required options.
31+
const samlConfig = {
32+
...options,
33+
} as SamlConfig & MultiSamlConfig;
3434

35-
super(options, verify);
36-
this._options = options;
35+
super(samlConfig, verify);
36+
this._options = samlConfig;
3737
}
3838

39-
authenticate(req: RequestWithUser, options: AuthenticateOptions) {
39+
authenticate(req: RequestWithUser, options: AuthenticateOptions): void {
4040
this._options.getSamlOptions(req, (err, samlOptions) => {
4141
if (err) {
4242
return this.error(err);
@@ -90,6 +90,11 @@ class MultiSamlStrategy extends SamlStrategy {
9090
);
9191
});
9292
}
93+
94+
// This is reduntant, but helps with testing
95+
error(err: Error): void {
96+
super.error(err);
97+
}
9398
}
9499

95100
export = MultiSamlStrategy;

src/passport-saml/saml-post-signing.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { SignedXml } from "xml-crypto";
22
import * as algorithms from "./algorithms";
3-
import { SamlOptions, SamlSigningOptions } from "./types";
3+
import { SamlSigningOptions } from "./types";
44

55
const authnRequestXPath =
66
'/*[local-name(.)="AuthnRequest" and namespace-uri(.)="urn:oasis:names:tc:SAML:2.0:protocol"]';
@@ -11,7 +11,11 @@ const defaultTransforms = [
1111
"http://www.w3.org/2001/10/xml-exc-c14n#",
1212
];
1313

14-
export function signSamlPost(samlMessage: string, xpath: string, options: SamlSigningOptions) {
14+
export function signSamlPost(
15+
samlMessage: string,
16+
xpath: string,
17+
options: SamlSigningOptions
18+
): string {
1519
if (!samlMessage) throw new Error("samlMessage is required");
1620
if (!xpath) throw new Error("xpath is required");
1721
if (!options) {

0 commit comments

Comments
 (0)