saml-test-server 0.0.0-stage → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +19 -0
- package/README.md +134 -2
- package/assets/idp-private-key.pem +27 -0
- package/assets/idp-public-cert.pem +20 -0
- package/dist/cli.js +106 -0
- package/dist/cli.js.map +1 -0
- package/dist/index.js +2170 -0
- package/dist/index.js.map +1 -0
- package/dist/types/app.d.ts +16 -0
- package/dist/types/cli-program.d.ts +10 -0
- package/dist/types/cli.d.ts +2 -0
- package/dist/types/default-config.d.ts +12 -0
- package/dist/types/index.d.ts +41 -0
- package/dist/types/log.d.ts +26 -0
- package/dist/types/options.d.ts +111 -0
- package/dist/types/profile-mapper.d.ts +10 -0
- package/dist/types/session.d.ts +36 -0
- package/dist/types/state.d.ts +49 -0
- package/dist/types/vendor/saml/encrypt.d.ts +16 -0
- package/dist/types/vendor/saml/index.d.ts +7 -0
- package/dist/types/vendor/saml/saml20.d.ts +47 -0
- package/dist/types/vendor/saml/sign.d.ts +30 -0
- package/dist/types/vendor/saml/utils.d.ts +18 -0
- package/dist/types/vendor/samlp/auth.d.ts +87 -0
- package/dist/types/vendor/samlp/constants.d.ts +30 -0
- package/dist/types/vendor/samlp/encoders.d.ts +5 -0
- package/dist/types/vendor/samlp/index.d.ts +14 -0
- package/dist/types/vendor/samlp/logout.d.ts +66 -0
- package/dist/types/vendor/samlp/metadata.d.ts +22 -0
- package/dist/types/vendor/samlp/profile-mapper.d.ts +38 -0
- package/dist/types/vendor/samlp/session-participants.d.ts +22 -0
- package/dist/types/vendor/samlp/signers.d.ts +43 -0
- package/dist/types/vendor/samlp/templates.d.ts +52 -0
- package/dist/types/vendor/samlp/trim-xml.d.ts +2 -0
- package/dist/types/vendor/samlp/utils.d.ts +55 -0
- package/dist/types/vendor/samlp/xml.d.ts +7 -0
- package/dist/types/views/AutoPostForm.d.ts +12 -0
- package/dist/types/views/ErrorPage.d.ts +8 -0
- package/dist/types/views/Layout.d.ts +4 -0
- package/dist/types/views/SettingsPage.d.ts +5 -0
- package/dist/types/views/UserPage.d.ts +20 -0
- package/dist/types/views/index.d.ts +5 -0
- package/package.json +58 -4
package/LICENSE
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
4
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
5
|
+
in the Software without restriction, including without limitation the rights
|
|
6
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
7
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
8
|
+
furnished to do so, subject to the following conditions:
|
|
9
|
+
|
|
10
|
+
The above copyright notice and this permission notice shall be included in all
|
|
11
|
+
copies or substantial portions of the Software.
|
|
12
|
+
|
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
15
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
16
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
17
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
18
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
19
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,135 @@
|
|
|
1
|
-
#
|
|
1
|
+
# saml-test-server
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
A simple SAML 2.0 Identity Provider (IdP) to test Service Providers (SPs) in **E2E tests**.
|
|
4
|
+
|
|
5
|
+
> **Not intended for use with production systems!** The bundled signing key is public.
|
|
6
|
+
|
|
7
|
+
## Usage
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
import { runServer } from 'saml-test-server';
|
|
11
|
+
|
|
12
|
+
export default async function globalSetup() {
|
|
13
|
+
const idp = await runServer({
|
|
14
|
+
port: 7000,
|
|
15
|
+
acsUrl: 'http://localhost:3000/auth/saml/callback',
|
|
16
|
+
sloUrl: 'http://localhost:3000/auth/saml/logout',
|
|
17
|
+
audience: 'http://localhost:3000/auth/saml/metadata',
|
|
18
|
+
logLevel: 'silent',
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
// Change the signed-in user at any time, without going through the UI:
|
|
22
|
+
idp.state.user.userName = 'jane@example.com';
|
|
23
|
+
idp.state.user.email = 'jane@example.com';
|
|
24
|
+
// ...and restore the defaults between tests:
|
|
25
|
+
idp.state.reset();
|
|
26
|
+
|
|
27
|
+
return () => idp.close();
|
|
28
|
+
}
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Custom claims (the user is merged with the defaults):
|
|
32
|
+
|
|
33
|
+
```ts
|
|
34
|
+
await runServer({
|
|
35
|
+
acsUrl,
|
|
36
|
+
audience,
|
|
37
|
+
config: {
|
|
38
|
+
user: { userName: 'jane@example.com', email: 'jane@example.com', userType: 'Admin' },
|
|
39
|
+
metadata: [
|
|
40
|
+
{ id: 'email', displayName: 'E-Mail Address', optional: false },
|
|
41
|
+
{ id: 'userType', displayName: 'User Type', optional: true, options: ['Admin', 'Editor'] },
|
|
42
|
+
],
|
|
43
|
+
},
|
|
44
|
+
});
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
`configFile` (a `.js`, `.mjs` or `.json` module exporting `{ user, metadata }`) is also supported.
|
|
48
|
+
|
|
49
|
+
### In-process (no port)
|
|
50
|
+
|
|
51
|
+
```ts
|
|
52
|
+
import { createIdp } from 'saml-test-server';
|
|
53
|
+
|
|
54
|
+
const { app, state } = await createIdp({ acsUrl, audience, logLevel: 'silent' });
|
|
55
|
+
const res = await app.request('/metadata');
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### Command line
|
|
59
|
+
|
|
60
|
+
```shell
|
|
61
|
+
saml-idp --acsUrl {POST URL} --audience {audience}
|
|
62
|
+
saml-idp --acsUrl {POST URL} --sloUrl {POST URL} --audience {audience}
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Open `http://localhost:7000` in your browser to start an IdP initiated flow to your SP.
|
|
66
|
+
All flags of the original CLI and their aliases (`--acs`, `--aud`, `--slo`, `--iss`, `--spId`, `--rs`, `--static`,
|
|
67
|
+
`--enc`, `--encCert`, `--encKey`, `--conf`, `--acr`, `--acd`) are supported. Run `saml-idp --help`.
|
|
68
|
+
`HOST` and `PORT` environment variables override `--host` and `--port`.
|
|
69
|
+
|
|
70
|
+
### Options
|
|
71
|
+
|
|
72
|
+
Passed as `--<option>` to the CLI or in the options object of `runServer` / `createIdp`.
|
|
73
|
+
|
|
74
|
+
| Option (* required) | Description | Default |
|
|
75
|
+
| ---------------------- | ------------------------------------------------------------------------ | ---------------------------------------- |
|
|
76
|
+
| `host` | IdP web server listener host | `localhost` |
|
|
77
|
+
| `port` | IdP web server listener port (`0` picks a free port) | `7000` |
|
|
78
|
+
| `cert` | IdP signature public key certificate (PEM or path) | bundled test certificate |
|
|
79
|
+
| `key` | IdP signature private key (PEM or path, PKCS#1 or PKCS#8) | bundled test key |
|
|
80
|
+
| `issuer` | IdP issuer URI | `urn:example:idp` |
|
|
81
|
+
| `acsUrl` * | SP Assertion Consumer Service URL | |
|
|
82
|
+
| `sloUrl` | SP Single Logout URL (enables SLO) | |
|
|
83
|
+
| `audience` * | SP audience URI | |
|
|
84
|
+
| `serviceProviderId` | SP issuer / entity URI | `audience` |
|
|
85
|
+
| `relayState` | Default RelayState | |
|
|
86
|
+
| `disableRequestAcsUrl` | Ignore the ACS URL of the AuthnRequest | `false` |
|
|
87
|
+
| `encryptAssertion` | Encrypt the assertion with the SP public key | `false` |
|
|
88
|
+
| `encryptionCert` | SP certificate (PEM or path) for assertion encryption | |
|
|
89
|
+
| `encryptionPublicKey` | SP RSA public key (PEM or path); derived from `encryptionCert` if absent | |
|
|
90
|
+
| `https` | Enable the HTTPS listener (needs `httpsPrivateKey` and `httpsCert`) | `false` |
|
|
91
|
+
| `httpsPrivateKey` | Web server TLS private key (PEM or path) | |
|
|
92
|
+
| `httpsCert` | Web server TLS certificate (PEM or path) | |
|
|
93
|
+
| `signResponse` | Sign the SAML Response (the Assertion is always signed) | `true` |
|
|
94
|
+
| `configFile` | Module exporting `{ user, metadata }` | built-in defaults |
|
|
95
|
+
| `config` | Inline `{ user, metadata }` (library only) | |
|
|
96
|
+
| `rollSession` | New session for every authn request | `false` |
|
|
97
|
+
| `authnContextClassRef` | Authentication Context Class Reference | `...:PasswordProtectedTransport` |
|
|
98
|
+
| `authnContextDecl` | Authentication Context Declaration (XML or path to an XML file) | |
|
|
99
|
+
| `logLevel` | `silent` (no output), `error` (unexpected errors only) or `info`. Env: `SAML_IDP_LOG_LEVEL` | `info` |
|
|
100
|
+
| `silent` | Deprecated, same as `logLevel: 'silent'` | `false` |
|
|
101
|
+
|
|
102
|
+
### Endpoints
|
|
103
|
+
|
|
104
|
+
| Path | Description |
|
|
105
|
+
| ------------------------------ | -------------------------------------------------------------------- |
|
|
106
|
+
| `/`, `/idp`, `/saml/sso` | SSO (HTTP-Redirect and HTTP-POST). Shows the sign in form |
|
|
107
|
+
| `/signin` (POST) | Mints the SAML Response and POSTs it to the ACS URL |
|
|
108
|
+
| `/saml/slo` | Single Logout (SP or IdP initiated, HTTP-Redirect and HTTP-POST) |
|
|
109
|
+
| `/signout` | Starts IdP initiated logout (or just ends the session without SLO) |
|
|
110
|
+
| `/metadata` | IdP metadata. `POST` adds an attribute (`attributeName`, `displayName`, ...) |
|
|
111
|
+
| `/settings` | Edit the IdP settings at runtime |
|
|
112
|
+
|
|
113
|
+
Useful element ids for browser tests: `#userName`, `#nameIdFormat`, one input per attribute (`#email`, `#groups`, ...),
|
|
114
|
+
`#btn-sign-in`, `#btn-sign-out`.
|
|
115
|
+
|
|
116
|
+
## Development
|
|
117
|
+
|
|
118
|
+
```shell
|
|
119
|
+
npm install
|
|
120
|
+
npm run dev # Vite dev server (SAML_ACS_URL, SAML_AUDIENCE, SAML_SLO_URL env vars)
|
|
121
|
+
npm test # Vitest
|
|
122
|
+
npm run typecheck
|
|
123
|
+
npm run build # dist/index.js, dist/cli.js, dist/types
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
Generate your own IdP key pair with:
|
|
127
|
+
|
|
128
|
+
```shell
|
|
129
|
+
openssl req -x509 -new -newkey rsa:2048 -nodes -subj '/C=US/ST=California/L=San Francisco/O=JankyCo/CN=Test Identity Provider' \
|
|
130
|
+
-keyout idp-private-key.pem -out idp-public-cert.pem -days 7300
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
## License
|
|
134
|
+
|
|
135
|
+
MIT, see [LICENSE](./LICENSE). Vendored code keeps its own license in `src/vendor/*/LICENSE`.
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
-----BEGIN RSA PRIVATE KEY-----
|
|
2
|
+
MIIEpAIBAAKCAQEAwa8mlFNNqxFRI+pxuoIb5mdMC6ue7S9Wkgb2XMTGeHXWj8Ea
|
|
3
|
+
bO7uUH7IsdwR3CsoEGal6kME8JjGlVqO+4SFl0oXH8V3LqPmYfeutbQGH/N3foKh
|
|
4
|
+
Q2NtuEfJON4zsU3bIXe72VzK5u687OvcHeBfrojAmVTjr7DNxBWFI6UyTa1dH58g
|
|
5
|
+
pATEWs64r21tfckgFSoi2kj4ecCjDnT0KczXfiy/Dw9MUuRHr8JwyqcN8MrWb33c
|
|
6
|
+
/KgLVj43hf1eSFKYRCgCUAc8jkseu+66MzXee+2hVi8piiPtJyC6aSlY0S9frnYt
|
|
7
|
+
9c6cpiWQHxnJu7GYV8iTN51CtsmzI6F8vTdwwQIDAQABAoIBABoE0YXzeueUN/Y/
|
|
8
|
+
NG/9sonNLqYStBLg3RxY2xezR4Y/Lke5hTdFEba2kXdzPOL62TL61ycVQcCghWzi
|
|
9
|
+
8XpQXzHnvhuCh3Q4iGZ4w3udZ4U7S74vBkLrRH4RBrMhBoi3EMz6LL4SfR9wyuh4
|
|
10
|
+
9uT8k136+QSgJsYfVkUVLwGO26cbepPHH9e7/MatV3iItEs7zZhF6CKYiexhZTte
|
|
11
|
+
y9HTRgkxiAUesSpO7/oXBAB7hTdrFbQOClxcW4peRTS9/834M3PdwE7OCwsznKp3
|
|
12
|
+
EDTsX1IcXojaYTZSb/Ftsc/WOQTOWo8jV7vVMU515zKCF/5YQntWfRwpT8PdABXa
|
|
13
|
+
TdT/OJECgYEA/87wQUT7ACCcdf/ZzBdRGZakRbPI0bGLnj+YKMCwgPMhptUzjJKg
|
|
14
|
+
k2LwkPyya1xB09XI6E6WHadjPgsBPYGpXc+SeKknT3iz7Q283iqn6jMgHq7NyLeY
|
|
15
|
+
Au0AlTGDaSNmDfUaT8luh+EtKIh+UDHtcJ/GZgw8Kr0RsSZG0cS1s3cCgYEAwdRM
|
|
16
|
+
Ir+UvjfO/CWghFMKyRR2RC/m9IJS+uY9eO2dcnGzPqXVDYiqOVRoyOfoI/Lavnei
|
|
17
|
+
n++XG24iSRDQ9fl9Gu7odlP+Or5liIES9Z9Gw9Ov1RUmspGJ/PT9RjSBvRymGHSu
|
|
18
|
+
IePjA5DFEDoaTAxCcdIIrURQz/HnWQYCDFfZ24cCgYEA0nrPLKU5ca1jj6Po7Qjy
|
|
19
|
+
bf+JQuDfB8ryek8RP9bsY+2pbsBWHgA3p43tKx8BH11VvJtXJ+4hjlzAR7h+WMFr
|
|
20
|
+
PgkhF6727Wj/grmfAZ5Pc2CFuTlTkSefKCkiJaAL5GKAAlP+RlATZjJdGdDVPd8W
|
|
21
|
+
42hTsnu2tePDvuMjuk4Ew5kCgYBE/Ccg0ujAjqJXcZjgn6GeEW9KM3ZSnbOk4p8M
|
|
22
|
+
VB2OZtMxv9nFGFflUZp9rrQbX0rlMTUrER3JwHfpY1fLw8D3/gG0Y45/H9aJL88s
|
|
23
|
+
pkSMW+Ifx4hfU5c19YfTHcrzIKgo2hZDr5TTZwoMDXHgkRhw5Bz8de0BJYp+T7oY
|
|
24
|
+
5IeALwKBgQDyYbssjxY2TJYr0Z+mvGNLnmEvuOT5z1Ek0XewQfCsGbC+3TOZ43NS
|
|
25
|
+
kXJHUt26kh/Jh7pZDmiEiRDS1IZSn2ytMqJT233EDn9jrPS0efpqzx8uGlRVROZN
|
|
26
|
+
oSMw69DxVGpBjGfLtlGF1i2TFcBEXD8jqaVSKzoKhXZkuHsP+q3WWg==
|
|
27
|
+
-----END RSA PRIVATE KEY-----
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
-----BEGIN CERTIFICATE-----
|
|
2
|
+
MIIDPDCCAiQCCQDydJgOlszqbzANBgkqhkiG9w0BAQUFADBgMQswCQYDVQQGEwJV
|
|
3
|
+
UzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNU2FuIEZyYW5jaXNjbzEQ
|
|
4
|
+
MA4GA1UEChMHSmFua3lDbzESMBAGA1UEAxMJbG9jYWxob3N0MB4XDTE0MDMxMjE5
|
|
5
|
+
NDYzM1oXDTI3MTExOTE5NDYzM1owYDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCkNh
|
|
6
|
+
bGlmb3JuaWExFjAUBgNVBAcTDVNhbiBGcmFuY2lzY28xEDAOBgNVBAoTB0phbmt5
|
|
7
|
+
Q28xEjAQBgNVBAMTCWxvY2FsaG9zdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC
|
|
8
|
+
AQoCggEBAMGvJpRTTasRUSPqcbqCG+ZnTAurnu0vVpIG9lzExnh11o/BGmzu7lB+
|
|
9
|
+
yLHcEdwrKBBmpepDBPCYxpVajvuEhZdKFx/Fdy6j5mH3rrW0Bh/zd36CoUNjbbhH
|
|
10
|
+
yTjeM7FN2yF3u9lcyubuvOzr3B3gX66IwJlU46+wzcQVhSOlMk2tXR+fIKQExFrO
|
|
11
|
+
uK9tbX3JIBUqItpI+HnAow509CnM134svw8PTFLkR6/CcMqnDfDK1m993PyoC1Y+
|
|
12
|
+
N4X9XkhSmEQoAlAHPI5LHrvuujM13nvtoVYvKYoj7ScgumkpWNEvX652LfXOnKYl
|
|
13
|
+
kB8ZybuxmFfIkzedQrbJsyOhfL03cMECAwEAATANBgkqhkiG9w0BAQUFAAOCAQEA
|
|
14
|
+
eHwzqwnzGEkxjzSD47imXaTqtYyETZow7XwBc0ZaFS50qRFJUgKTAmKS1xQBP/qH
|
|
15
|
+
pStsROT35DUxJAE6NY1Kbq3ZbCuhGoSlY0L7VzVT5tpu4EY8+Dq/u2EjRmmhoL7U
|
|
16
|
+
kskvIZ2n1DdERtd+YUMTeqYl9co43csZwDno/IKomeN5qaPc39IZjikJ+nUC6kPF
|
|
17
|
+
Keu/3j9rgHNlRtocI6S1FdtFz9OZMQlpr0JbUt2T3xS/YoQJn6coDmJL5GTiiKM6
|
|
18
|
+
cOe+Ur1VwzS1JEDbSS2TWWhzq8ojLdrotYLGd9JOsoQhElmz+tMfCFQUFLExinPA
|
|
19
|
+
yy7YHlSiVX13QH2XTu/iQQ==
|
|
20
|
+
-----END CERTIFICATE-----
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { Command, Option, InvalidArgumentError } from "commander";
|
|
3
|
+
import { L as LOG_LEVELS, OptionsError, runServer } from "./index.js";
|
|
4
|
+
import pc from "picocolors";
|
|
5
|
+
const ALIASES = {
|
|
6
|
+
"--iss": "--issuer",
|
|
7
|
+
"--acs": "--acsUrl",
|
|
8
|
+
"--slo": "--sloUrl",
|
|
9
|
+
"--aud": "--audience",
|
|
10
|
+
"--spId": "--serviceProviderId",
|
|
11
|
+
"--rs": "--relayState",
|
|
12
|
+
"--static": "--disableRequestAcsUrl",
|
|
13
|
+
"--enc": "--encryptAssertion",
|
|
14
|
+
"--encCert": "--encryptionCert",
|
|
15
|
+
"--encKey": "--encryptionPublicKey",
|
|
16
|
+
"--conf": "--configFile",
|
|
17
|
+
"--acr": "--authnContextClassRef",
|
|
18
|
+
"--acd": "--authnContextDecl"
|
|
19
|
+
};
|
|
20
|
+
const BOOLEAN_OPTIONS = [
|
|
21
|
+
"disableRequestAcsUrl",
|
|
22
|
+
"encryptAssertion",
|
|
23
|
+
"https",
|
|
24
|
+
"signResponse",
|
|
25
|
+
"rollSession",
|
|
26
|
+
"silent"
|
|
27
|
+
];
|
|
28
|
+
function normalizeArgv(argv) {
|
|
29
|
+
return argv.map((arg) => {
|
|
30
|
+
if (!arg.startsWith("--")) return arg;
|
|
31
|
+
const [flag, ...rest] = arg.split("=");
|
|
32
|
+
const value = rest.length ? `=${rest.join("=")}` : "";
|
|
33
|
+
const canonical = ALIASES[flag] ?? flag;
|
|
34
|
+
const negated = /^--no-(.+)$/.exec(canonical);
|
|
35
|
+
if (negated && BOOLEAN_OPTIONS.includes(negated[1]) && !value) {
|
|
36
|
+
return `--${negated[1]}=false`;
|
|
37
|
+
}
|
|
38
|
+
return canonical + value;
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
function parseBoolean(value) {
|
|
42
|
+
if (["true", "yes", "1"].includes(value.toLowerCase())) return true;
|
|
43
|
+
if (["false", "no", "0"].includes(value.toLowerCase())) return false;
|
|
44
|
+
throw new InvalidArgumentError("Expected true or false.");
|
|
45
|
+
}
|
|
46
|
+
function booleanOption(flag, description, defaultValue = false) {
|
|
47
|
+
return new Option(`${flag} [bool]`, description).argParser(parseBoolean).preset(true).default(defaultValue);
|
|
48
|
+
}
|
|
49
|
+
function buildProgram(onRun) {
|
|
50
|
+
const program2 = new Command("saml-idp").description(
|
|
51
|
+
"Simple IdP for SAML 2.0 WebSSO & SLO Profile\n\nLaunches an IdP web server that mints SAML assertions or logout responses for a Service Provider (SP)"
|
|
52
|
+
).usage("--acsUrl {url} --audience {uri} [options]").showHelpAfterError().configureHelp({ sortOptions: false }).addHelpText(
|
|
53
|
+
"after",
|
|
54
|
+
"\nAliases: --iss, --acs, --slo, --aud, --spId, --rs, --static, --enc, --encCert, --encKey, --conf, --acr, --acd\n\nExample:\n saml-idp --acsUrl http://acme.okta.com/auth/saml20/exampleidp --audience https://www.okta.com/saml2/service-provider/spf5aFRRXFGIMAYXQPNV"
|
|
55
|
+
);
|
|
56
|
+
program2.addOption(new Option("--host <host>", "IdP Web Server Listener Host").default("localhost")).addOption(
|
|
57
|
+
new Option("-p, --port <port>", "IdP Web Server Listener Port").default(7e3).argParser((v) => {
|
|
58
|
+
const port = Number.parseInt(v, 10);
|
|
59
|
+
if (Number.isNaN(port) || port < 0 || port > 65535) throw new InvalidArgumentError("Invalid port.");
|
|
60
|
+
return port;
|
|
61
|
+
})
|
|
62
|
+
).option("--cert <cert>", "IdP Signature PublicKey Certificate (PEM or path) [default: bundled test certificate]").option("--key <key>", "IdP Signature PrivateKey Certificate (PEM or path) [default: bundled test key]").addOption(new Option("--issuer <uri>", "IdP Issuer URI").default("urn:example:idp")).option("--acsUrl <url>", "SP Assertion Consumer URL").option("--sloUrl <url>", "SP Single Logout URL").option("--audience <uri>", "SP Audience URI").option("--serviceProviderId <uri>", "SP Issuer/Entity URI").option("--relayState <state>", "Default SAML RelayState for SAMLResponse").addOption(
|
|
63
|
+
booleanOption(
|
|
64
|
+
"--disableRequestAcsUrl",
|
|
65
|
+
"Disables ability for SP AuthnRequest to specify Assertion Consumer URL"
|
|
66
|
+
)
|
|
67
|
+
).addOption(booleanOption("--encryptAssertion", "Encrypts assertion with SP Public Key")).option("--encryptionCert <cert>", "SP Certificate (pem) for Assertion Encryption").option(
|
|
68
|
+
"--encryptionPublicKey <key>",
|
|
69
|
+
"SP RSA Public Key (pem) for Assertion Encryption (derived from the certificate when omitted)"
|
|
70
|
+
).option("--httpsPrivateKey <key>", "Web Server TLS/SSL Private Key (pem)").option("--httpsCert <cert>", "Web Server TLS/SSL Certificate (pem)").addOption(booleanOption("--https", "Enables HTTPS Listener (requires httpsPrivateKey and httpsCert)")).addOption(booleanOption("--signResponse", "Enables signing of responses", true)).addOption(
|
|
71
|
+
new Option("--configFile <path>", "Path to a SAML attribute config file").default("saml-idp/config.js")
|
|
72
|
+
).addOption(
|
|
73
|
+
booleanOption(
|
|
74
|
+
"--rollSession",
|
|
75
|
+
"Create a new session for every authn request instead of reusing an existing session"
|
|
76
|
+
)
|
|
77
|
+
).addOption(
|
|
78
|
+
new Option("--authnContextClassRef <uri>", "Authentication Context Class Reference").default(
|
|
79
|
+
"urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport"
|
|
80
|
+
)
|
|
81
|
+
).option("--authnContextDecl <path>", "Authentication Context Declaration (XML FilePath)").addOption(
|
|
82
|
+
new Option("--logLevel <level>", "Verbosity of the logs (env: SAML_IDP_LOG_LEVEL)").choices(LOG_LEVELS)
|
|
83
|
+
).addOption(booleanOption("--silent", "Disables logging (same as --logLevel silent)")).action(async (opts) => {
|
|
84
|
+
if (!opts.acsUrl) program2.error("error: required option '--acsUrl <url>' not specified");
|
|
85
|
+
if (!opts.audience) program2.error("error: required option '--audience <uri>' not specified");
|
|
86
|
+
try {
|
|
87
|
+
await onRun({
|
|
88
|
+
...opts,
|
|
89
|
+
host: process.env.HOST ?? opts.host,
|
|
90
|
+
port: process.env.PORT ? Number.parseInt(process.env.PORT, 10) : opts.port
|
|
91
|
+
});
|
|
92
|
+
} catch (error) {
|
|
93
|
+
if (error instanceof OptionsError) {
|
|
94
|
+
console.error(pc.red(error.message));
|
|
95
|
+
process.exit(1);
|
|
96
|
+
}
|
|
97
|
+
throw error;
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
return program2;
|
|
101
|
+
}
|
|
102
|
+
const program = buildProgram(async (options) => {
|
|
103
|
+
await runServer(options);
|
|
104
|
+
});
|
|
105
|
+
await program.parseAsync(normalizeArgv(process.argv.slice(2)), { from: "user" });
|
|
106
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sources":["../src/cli-program.ts","../src/cli.ts"],"sourcesContent":["import { Command, InvalidArgumentError, Option } from 'commander';\nimport { LOG_LEVELS } from './log.js';\nimport pc from 'picocolors';\nimport { OptionsError, type IdpOptions } from './options.js';\n\n/** Long option aliases of the original `saml-idp` CLI. */\nexport const ALIASES: Record<string, string> = {\n '--iss': '--issuer',\n '--acs': '--acsUrl',\n '--slo': '--sloUrl',\n '--aud': '--audience',\n '--spId': '--serviceProviderId',\n '--rs': '--relayState',\n '--static': '--disableRequestAcsUrl',\n '--enc': '--encryptAssertion',\n '--encCert': '--encryptionCert',\n '--encKey': '--encryptionPublicKey',\n '--conf': '--configFile',\n '--acr': '--authnContextClassRef',\n '--acd': '--authnContextDecl',\n};\n\nconst BOOLEAN_OPTIONS = [\n 'disableRequestAcsUrl',\n 'encryptAssertion',\n 'https',\n 'signResponse',\n 'rollSession',\n 'silent',\n];\n\n/**\n * Expands the aliases (`--acs` => `--acsUrl`) and the `--no-<flag>` form of boolean\n * flags (`--no-signResponse` => `--signResponse=false`).\n */\nexport function normalizeArgv(argv: string[]): string[] {\n return argv.map((arg) => {\n if (!arg.startsWith('--')) return arg;\n const [flag, ...rest] = arg.split('=');\n const value = rest.length ? `=${rest.join('=')}` : '';\n const canonical = ALIASES[flag] ?? flag;\n const negated = /^--no-(.+)$/.exec(canonical);\n if (negated && BOOLEAN_OPTIONS.includes(negated[1]) && !value) {\n return `--${negated[1]}=false`;\n }\n return canonical + value;\n });\n}\n\nfunction parseBoolean(value: string): boolean {\n if (['true', 'yes', '1'].includes(value.toLowerCase())) return true;\n if (['false', 'no', '0'].includes(value.toLowerCase())) return false;\n throw new InvalidArgumentError('Expected true or false.');\n}\n\nfunction booleanOption(flag: string, description: string, defaultValue = false): Option {\n return new Option(`${flag} [bool]`, description)\n .argParser(parseBoolean)\n .preset(true)\n .default(defaultValue);\n}\n\nexport function buildProgram(onRun: (options: IdpOptions) => Promise<void>): Command {\n const program = new Command('saml-idp')\n .description(\n 'Simple IdP for SAML 2.0 WebSSO & SLO Profile\\n\\n' +\n 'Launches an IdP web server that mints SAML assertions or logout responses for a Service Provider (SP)',\n )\n .usage('--acsUrl {url} --audience {uri} [options]')\n .showHelpAfterError()\n .configureHelp({ sortOptions: false })\n .addHelpText(\n 'after',\n '\\nAliases: --iss, --acs, --slo, --aud, --spId, --rs, --static, --enc, --encCert, --encKey, --conf, --acr, --acd\\n' +\n '\\nExample:\\n saml-idp --acsUrl http://acme.okta.com/auth/saml20/exampleidp ' +\n '--audience https://www.okta.com/saml2/service-provider/spf5aFRRXFGIMAYXQPNV',\n );\n\n program\n .addOption(new Option('--host <host>', 'IdP Web Server Listener Host').default('localhost'))\n .addOption(\n new Option('-p, --port <port>', 'IdP Web Server Listener Port')\n .default(7000)\n .argParser((v) => {\n const port = Number.parseInt(v, 10);\n if (Number.isNaN(port) || port < 0 || port > 65535) throw new InvalidArgumentError('Invalid port.');\n return port;\n }),\n )\n .option('--cert <cert>', 'IdP Signature PublicKey Certificate (PEM or path) [default: bundled test certificate]')\n .option('--key <key>', 'IdP Signature PrivateKey Certificate (PEM or path) [default: bundled test key]')\n .addOption(new Option('--issuer <uri>', 'IdP Issuer URI').default('urn:example:idp'))\n .option('--acsUrl <url>', 'SP Assertion Consumer URL')\n .option('--sloUrl <url>', 'SP Single Logout URL')\n .option('--audience <uri>', 'SP Audience URI')\n .option('--serviceProviderId <uri>', 'SP Issuer/Entity URI')\n .option('--relayState <state>', 'Default SAML RelayState for SAMLResponse')\n .addOption(\n booleanOption(\n '--disableRequestAcsUrl',\n 'Disables ability for SP AuthnRequest to specify Assertion Consumer URL',\n ),\n )\n .addOption(booleanOption('--encryptAssertion', 'Encrypts assertion with SP Public Key'))\n .option('--encryptionCert <cert>', 'SP Certificate (pem) for Assertion Encryption')\n .option(\n '--encryptionPublicKey <key>',\n 'SP RSA Public Key (pem) for Assertion Encryption (derived from the certificate when omitted)',\n )\n .option('--httpsPrivateKey <key>', 'Web Server TLS/SSL Private Key (pem)')\n .option('--httpsCert <cert>', 'Web Server TLS/SSL Certificate (pem)')\n .addOption(booleanOption('--https', 'Enables HTTPS Listener (requires httpsPrivateKey and httpsCert)'))\n .addOption(booleanOption('--signResponse', 'Enables signing of responses', true))\n .addOption(\n new Option('--configFile <path>', 'Path to a SAML attribute config file').default('saml-idp/config.js'),\n )\n .addOption(\n booleanOption(\n '--rollSession',\n 'Create a new session for every authn request instead of reusing an existing session',\n ),\n )\n .addOption(\n new Option('--authnContextClassRef <uri>', 'Authentication Context Class Reference').default(\n 'urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport',\n ),\n )\n .option('--authnContextDecl <path>', 'Authentication Context Declaration (XML FilePath)')\n .addOption(\n new Option('--logLevel <level>', 'Verbosity of the logs (env: SAML_IDP_LOG_LEVEL)').choices(LOG_LEVELS),\n )\n .addOption(booleanOption('--silent', 'Disables logging (same as --logLevel silent)'))\n .action(async (opts: IdpOptions) => {\n if (!opts.acsUrl) program.error(\"error: required option '--acsUrl <url>' not specified\");\n if (!opts.audience) program.error(\"error: required option '--audience <uri>' not specified\");\n try {\n await onRun({\n ...opts,\n host: process.env.HOST ?? opts.host,\n port: process.env.PORT ? Number.parseInt(process.env.PORT, 10) : opts.port,\n });\n } catch (error) {\n if (error instanceof OptionsError) {\n console.error(pc.red(error.message));\n process.exit(1);\n }\n throw error;\n }\n });\n\n return program;\n}\n","#!/usr/bin/env node\nimport { buildProgram, normalizeArgv } from './cli-program.js';\nimport { runServer } from './index.js';\n\nconst program = buildProgram(async (options) => {\n await runServer(options);\n});\n\nawait program.parseAsync(normalizeArgv(process.argv.slice(2)), { from: 'user' });\n"],"names":["program"],"mappings":"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"}
|