@c9up/transit 0.1.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 +21 -0
- package/README.md +191 -0
- package/dist/Oauth1Driver.d.ts +68 -0
- package/dist/Oauth1Driver.d.ts.map +1 -0
- package/dist/Oauth1Driver.js +148 -0
- package/dist/Oauth1Driver.js.map +1 -0
- package/dist/Oauth2Driver.d.ts +89 -0
- package/dist/Oauth2Driver.d.ts.map +1 -0
- package/dist/Oauth2Driver.js +196 -0
- package/dist/Oauth2Driver.js.map +1 -0
- package/dist/TransitManager.d.ts +59 -0
- package/dist/TransitManager.d.ts.map +1 -0
- package/dist/TransitManager.js +100 -0
- package/dist/TransitManager.js.map +1 -0
- package/dist/TransitProvider.d.ts +26 -0
- package/dist/TransitProvider.d.ts.map +1 -0
- package/dist/TransitProvider.js +32 -0
- package/dist/TransitProvider.js.map +1 -0
- package/dist/apple.d.ts +45 -0
- package/dist/apple.d.ts.map +1 -0
- package/dist/apple.js +80 -0
- package/dist/apple.js.map +1 -0
- package/dist/ber.d.ts +50 -0
- package/dist/ber.d.ts.map +1 -0
- package/dist/ber.js +156 -0
- package/dist/ber.js.map +1 -0
- package/dist/c14n.d.ts +38 -0
- package/dist/c14n.d.ts.map +1 -0
- package/dist/c14n.js +129 -0
- package/dist/c14n.js.map +1 -0
- package/dist/config.d.ts +115 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +146 -0
- package/dist/config.js.map +1 -0
- package/dist/configure.d.ts +18 -0
- package/dist/configure.d.ts.map +1 -0
- package/dist/configure.js +33 -0
- package/dist/configure.js.map +1 -0
- package/dist/drivers/AppleDriver.d.ts +36 -0
- package/dist/drivers/AppleDriver.d.ts.map +1 -0
- package/dist/drivers/AppleDriver.js +58 -0
- package/dist/drivers/AppleDriver.js.map +1 -0
- package/dist/drivers/DiscordDriver.d.ts +15 -0
- package/dist/drivers/DiscordDriver.d.ts.map +1 -0
- package/dist/drivers/DiscordDriver.js +45 -0
- package/dist/drivers/DiscordDriver.js.map +1 -0
- package/dist/drivers/FacebookDriver.d.ts +15 -0
- package/dist/drivers/FacebookDriver.d.ts.map +1 -0
- package/dist/drivers/FacebookDriver.js +47 -0
- package/dist/drivers/FacebookDriver.js.map +1 -0
- package/dist/drivers/GitHubDriver.d.ts +33 -0
- package/dist/drivers/GitHubDriver.d.ts.map +1 -0
- package/dist/drivers/GitHubDriver.js +72 -0
- package/dist/drivers/GitHubDriver.js.map +1 -0
- package/dist/drivers/GoogleDriver.d.ts +16 -0
- package/dist/drivers/GoogleDriver.d.ts.map +1 -0
- package/dist/drivers/GoogleDriver.js +31 -0
- package/dist/drivers/GoogleDriver.js.map +1 -0
- package/dist/drivers/LdapDirectory.d.ts +51 -0
- package/dist/drivers/LdapDirectory.d.ts.map +1 -0
- package/dist/drivers/LdapDirectory.js +127 -0
- package/dist/drivers/LdapDirectory.js.map +1 -0
- package/dist/drivers/LinkedInDriver.d.ts +29 -0
- package/dist/drivers/LinkedInDriver.d.ts.map +1 -0
- package/dist/drivers/LinkedInDriver.js +97 -0
- package/dist/drivers/LinkedInDriver.js.map +1 -0
- package/dist/drivers/LinkedInOpenidConnectDriver.d.ts +17 -0
- package/dist/drivers/LinkedInOpenidConnectDriver.d.ts.map +1 -0
- package/dist/drivers/LinkedInOpenidConnectDriver.js +27 -0
- package/dist/drivers/LinkedInOpenidConnectDriver.js.map +1 -0
- package/dist/drivers/OidcDriver.d.ts +64 -0
- package/dist/drivers/OidcDriver.d.ts.map +1 -0
- package/dist/drivers/OidcDriver.js +259 -0
- package/dist/drivers/OidcDriver.js.map +1 -0
- package/dist/drivers/SamlDriver.d.ts +66 -0
- package/dist/drivers/SamlDriver.d.ts.map +1 -0
- package/dist/drivers/SamlDriver.js +228 -0
- package/dist/drivers/SamlDriver.js.map +1 -0
- package/dist/drivers/SpotifyDriver.d.ts +16 -0
- package/dist/drivers/SpotifyDriver.d.ts.map +1 -0
- package/dist/drivers/SpotifyDriver.js +34 -0
- package/dist/drivers/SpotifyDriver.js.map +1 -0
- package/dist/drivers/TwitterDriver.d.ts +21 -0
- package/dist/drivers/TwitterDriver.d.ts.map +1 -0
- package/dist/drivers/TwitterDriver.js +46 -0
- package/dist/drivers/TwitterDriver.js.map +1 -0
- package/dist/drivers/TwitterXDriver.d.ts +22 -0
- package/dist/drivers/TwitterXDriver.d.ts.map +1 -0
- package/dist/drivers/TwitterXDriver.js +47 -0
- package/dist/drivers/TwitterXDriver.js.map +1 -0
- package/dist/dsig.d.ts +51 -0
- package/dist/dsig.d.ts.map +1 -0
- package/dist/dsig.js +273 -0
- package/dist/dsig.js.map +1 -0
- package/dist/index.d.ts +38 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +33 -0
- package/dist/index.js.map +1 -0
- package/dist/jwt.d.ts +135 -0
- package/dist/jwt.d.ts.map +1 -0
- package/dist/jwt.js +167 -0
- package/dist/jwt.js.map +1 -0
- package/dist/ldap.d.ts +82 -0
- package/dist/ldap.d.ts.map +1 -0
- package/dist/ldap.js +271 -0
- package/dist/ldap.js.map +1 -0
- package/dist/nodeEnv.d.ts +17 -0
- package/dist/nodeEnv.d.ts.map +1 -0
- package/dist/nodeEnv.js +33 -0
- package/dist/nodeEnv.js.map +1 -0
- package/dist/oauth1.d.ts +68 -0
- package/dist/oauth1.d.ts.map +1 -0
- package/dist/oauth1.js +106 -0
- package/dist/oauth1.js.map +1 -0
- package/dist/oidc.d.ts +61 -0
- package/dist/oidc.d.ts.map +1 -0
- package/dist/oidc.js +151 -0
- package/dist/oidc.js.map +1 -0
- package/dist/quasar.d.ts +14 -0
- package/dist/quasar.d.ts.map +1 -0
- package/dist/quasar.js +42 -0
- package/dist/quasar.js.map +1 -0
- package/dist/replay.d.ts +70 -0
- package/dist/replay.d.ts.map +1 -0
- package/dist/replay.js +82 -0
- package/dist/replay.js.map +1 -0
- package/dist/saml.d.ts +68 -0
- package/dist/saml.d.ts.map +1 -0
- package/dist/saml.js +181 -0
- package/dist/saml.js.map +1 -0
- package/dist/services/main.d.ts +22 -0
- package/dist/services/main.d.ts.map +1 -0
- package/dist/services/main.js +39 -0
- package/dist/services/main.js.map +1 -0
- package/dist/testing/FakeTransit.d.ts +82 -0
- package/dist/testing/FakeTransit.d.ts.map +1 -0
- package/dist/testing/FakeTransit.js +160 -0
- package/dist/testing/FakeTransit.js.map +1 -0
- package/dist/types.d.ts +118 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +27 -0
- package/dist/types.js.map +1 -0
- package/dist/xml.d.ts +72 -0
- package/dist/xml.d.ts.map +1 -0
- package/dist/xml.js +353 -0
- package/dist/xml.js.map +1 -0
- package/package.json +73 -0
- package/src/Oauth1Driver.ts +216 -0
- package/src/Oauth2Driver.ts +271 -0
- package/src/TransitManager.ts +145 -0
- package/src/TransitProvider.ts +51 -0
- package/src/apple.ts +101 -0
- package/src/ber.ts +175 -0
- package/src/c14n.ts +187 -0
- package/src/config.ts +160 -0
- package/src/configure.ts +47 -0
- package/src/drivers/AppleDriver.ts +69 -0
- package/src/drivers/DiscordDriver.ts +50 -0
- package/src/drivers/FacebookDriver.ts +53 -0
- package/src/drivers/GitHubDriver.ts +91 -0
- package/src/drivers/GoogleDriver.ts +38 -0
- package/src/drivers/LdapDirectory.ts +185 -0
- package/src/drivers/LinkedInDriver.ts +105 -0
- package/src/drivers/LinkedInOpenidConnectDriver.ts +33 -0
- package/src/drivers/OidcDriver.ts +379 -0
- package/src/drivers/SamlDriver.ts +326 -0
- package/src/drivers/SpotifyDriver.ts +38 -0
- package/src/drivers/TwitterDriver.ts +55 -0
- package/src/drivers/TwitterXDriver.ts +53 -0
- package/src/dsig.ts +405 -0
- package/src/index.ts +85 -0
- package/src/jwt.ts +263 -0
- package/src/ldap.ts +374 -0
- package/src/nodeEnv.ts +31 -0
- package/src/oauth1.ts +144 -0
- package/src/oidc.ts +208 -0
- package/src/quasar.ts +64 -0
- package/src/replay.ts +136 -0
- package/src/saml.ts +309 -0
- package/src/services/main.ts +47 -0
- package/src/testing/FakeTransit.ts +233 -0
- package/src/types.ts +150 -0
- package/src/xml.ts +438 -0
package/dist/jwt.js
ADDED
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Verifying an OpenID Connect `id_token`.
|
|
3
|
+
*
|
|
4
|
+
* This is the file that decides whether a sign-in is genuine, so it is written
|
|
5
|
+
* to refuse rather than to accept. Three rules shape it:
|
|
6
|
+
*
|
|
7
|
+
* - The algorithm is chosen by US, never read from the token. A verifier that
|
|
8
|
+
* trusts the header's `alg` accepts `none`, and accepts an RS256 public key
|
|
9
|
+
* replayed as an HS256 shared secret — the two oldest JWT forgeries there
|
|
10
|
+
* are.
|
|
11
|
+
* - Every claim that binds the token to this exchange is checked: the issuer,
|
|
12
|
+
* the audience, the expiry, and the nonce. A token that is perfectly valid
|
|
13
|
+
* for somebody else is not valid here.
|
|
14
|
+
* - Nothing is decoded before the signature holds.
|
|
15
|
+
*
|
|
16
|
+
* No dependency: Node verifies RSA and EC against a JWK on its own.
|
|
17
|
+
*/
|
|
18
|
+
import { constants, createPublicKey, createVerify, verify as verifySignature, } from "node:crypto";
|
|
19
|
+
/**
|
|
20
|
+
* The algorithms this verifies. Asymmetric only, and deliberately so: the
|
|
21
|
+
* symmetric family signs with the client secret, so anyone holding it — every
|
|
22
|
+
* copy of the config, every log line that leaked it — can mint a token for any
|
|
23
|
+
* user. `none` is not an algorithm.
|
|
24
|
+
*/
|
|
25
|
+
const SUPPORTED = {
|
|
26
|
+
RS256: { hash: "sha256", kind: "rsa", kty: "RSA" },
|
|
27
|
+
RS384: { hash: "sha384", kind: "rsa", kty: "RSA" },
|
|
28
|
+
RS512: { hash: "sha512", kind: "rsa", kty: "RSA" },
|
|
29
|
+
PS256: { hash: "sha256", kind: "rsa-pss", kty: "RSA" },
|
|
30
|
+
PS384: { hash: "sha384", kind: "rsa-pss", kty: "RSA" },
|
|
31
|
+
PS512: { hash: "sha512", kind: "rsa-pss", kty: "RSA" },
|
|
32
|
+
ES256: { hash: "sha256", kind: "ec", kty: "EC", crv: "P-256" },
|
|
33
|
+
ES384: { hash: "sha384", kind: "ec", kty: "EC", crv: "P-384" },
|
|
34
|
+
ES512: { hash: "sha512", kind: "ec", kty: "EC", crv: "P-521" },
|
|
35
|
+
};
|
|
36
|
+
export function isSupportedAlg(value) {
|
|
37
|
+
return typeof value === "string" && value in SUPPORTED;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Split and decode a compact JWS. This says nothing about whether the token is
|
|
41
|
+
* genuine — it is what the caller needs to find the right key.
|
|
42
|
+
*/
|
|
43
|
+
export function decodeJws(token) {
|
|
44
|
+
const segments = token.split(".");
|
|
45
|
+
if (segments.length !== 3) {
|
|
46
|
+
throw new Error("[transit] id_token is not a compact JWS (three segments)");
|
|
47
|
+
}
|
|
48
|
+
const [encodedHeader, encodedPayload, encodedSignature] = segments;
|
|
49
|
+
return {
|
|
50
|
+
header: decodeSegment(encodedHeader, "header"),
|
|
51
|
+
payload: decodeSegment(encodedPayload, "payload"),
|
|
52
|
+
signingInput: `${encodedHeader}.${encodedPayload}`,
|
|
53
|
+
signature: Buffer.from(encodedSignature, "base64url"),
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Check a token's signature against one key.
|
|
58
|
+
*
|
|
59
|
+
* `alg` is the caller's choice, not the token's. Passing the header's own
|
|
60
|
+
* value would be the vulnerability this exists to avoid.
|
|
61
|
+
*/
|
|
62
|
+
export function verifyJwsSignature(parts, jwk, alg) {
|
|
63
|
+
const spec = SUPPORTED[alg];
|
|
64
|
+
assertKeyMatchesAlg(jwk, alg);
|
|
65
|
+
const key = createPublicKey({ key: jwk, format: "jwk" });
|
|
66
|
+
const data = Buffer.from(parts.signingInput, "ascii");
|
|
67
|
+
if (spec.kind === "ec") {
|
|
68
|
+
// JOSE concatenates R and S; Node reads DER unless told otherwise, and
|
|
69
|
+
// would reject every valid signature without this.
|
|
70
|
+
return verifySignature(spec.hash, data, { key, dsaEncoding: "ieee-p1363" }, parts.signature);
|
|
71
|
+
}
|
|
72
|
+
if (spec.kind === "rsa-pss") {
|
|
73
|
+
return verifySignature(spec.hash, data, {
|
|
74
|
+
key,
|
|
75
|
+
padding: constants.RSA_PKCS1_PSS_PADDING,
|
|
76
|
+
saltLength: constants.RSA_PSS_SALTLEN_DIGEST,
|
|
77
|
+
}, parts.signature);
|
|
78
|
+
}
|
|
79
|
+
const verifier = createVerify(spec.hash);
|
|
80
|
+
verifier.update(data);
|
|
81
|
+
verifier.end();
|
|
82
|
+
return verifier.verify(key, parts.signature);
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Check every claim that binds this token to this exchange.
|
|
86
|
+
*
|
|
87
|
+
* Each of these has been a real vulnerability somewhere: a token from another
|
|
88
|
+
* issuer, a token minted for another client of the same issuer, an expired one
|
|
89
|
+
* replayed, and — the one people forget — a token from an older sign-in of the
|
|
90
|
+
* same user, which is what the nonce catches.
|
|
91
|
+
*/
|
|
92
|
+
export function assertIdTokenClaims(payload, expected) {
|
|
93
|
+
const leeway = expected.leewaySeconds ?? 60;
|
|
94
|
+
const now = expected.now ?? Math.floor(Date.now() / 1000);
|
|
95
|
+
const iss = payload.iss;
|
|
96
|
+
if (iss !== expected.issuer) {
|
|
97
|
+
throw new Error(`[transit] id_token issuer mismatch: expected '${expected.issuer}'`);
|
|
98
|
+
}
|
|
99
|
+
const sub = payload.sub;
|
|
100
|
+
if (typeof sub !== "string" || sub === "") {
|
|
101
|
+
throw new Error("[transit] id_token carries no subject");
|
|
102
|
+
}
|
|
103
|
+
const aud = payload.aud;
|
|
104
|
+
const audiences = Array.isArray(aud) ? aud : [aud];
|
|
105
|
+
if (!audiences.includes(expected.audience)) {
|
|
106
|
+
throw new Error("[transit] id_token was not issued for this client — check the audience");
|
|
107
|
+
}
|
|
108
|
+
if (audiences.length > 1 && payload.azp !== expected.audience) {
|
|
109
|
+
// With several audiences the spec requires `azp`, and it has to name us:
|
|
110
|
+
// otherwise a token minted for a sibling client would pass the check
|
|
111
|
+
// above.
|
|
112
|
+
throw new Error("[transit] id_token has several audiences and no azp naming this client");
|
|
113
|
+
}
|
|
114
|
+
const exp = payload.exp;
|
|
115
|
+
if (typeof exp !== "number") {
|
|
116
|
+
throw new Error("[transit] id_token carries no expiry");
|
|
117
|
+
}
|
|
118
|
+
if (exp + leeway < now) {
|
|
119
|
+
throw new Error("[transit] id_token has expired");
|
|
120
|
+
}
|
|
121
|
+
const iat = payload.iat;
|
|
122
|
+
if (typeof iat === "number" && iat - leeway > now) {
|
|
123
|
+
// A token issued in the future is either a clock problem or a forgery;
|
|
124
|
+
// neither should sign anybody in.
|
|
125
|
+
throw new Error("[transit] id_token is issued in the future");
|
|
126
|
+
}
|
|
127
|
+
if (payload.nonce !== expected.nonce) {
|
|
128
|
+
// Without this, a token captured from an earlier sign-in of the same user
|
|
129
|
+
// at the same provider can be replayed into a fresh session.
|
|
130
|
+
throw new Error("[transit] id_token nonce does not match this sign-in");
|
|
131
|
+
}
|
|
132
|
+
return payload;
|
|
133
|
+
}
|
|
134
|
+
function decodeSegment(segment, what) {
|
|
135
|
+
let parsed;
|
|
136
|
+
try {
|
|
137
|
+
parsed = JSON.parse(Buffer.from(segment, "base64url").toString("utf8"));
|
|
138
|
+
}
|
|
139
|
+
catch {
|
|
140
|
+
throw new Error(`[transit] id_token ${what} is not valid JSON`);
|
|
141
|
+
}
|
|
142
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
143
|
+
throw new Error(`[transit] id_token ${what} is not an object`);
|
|
144
|
+
}
|
|
145
|
+
return parsed;
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Refuse a key that is not what the algorithm is defined over.
|
|
149
|
+
*
|
|
150
|
+
* Node ignores the EC-specific options when handed an RSA key, so verifying an
|
|
151
|
+
* "ES256" token against an RSA key quietly becomes an RSA verification. That
|
|
152
|
+
* is not a forgery on its own — it still needs a real signature — but a
|
|
153
|
+
* verifier that reports success for a path it did not take is not one worth
|
|
154
|
+
* trusting. The curve is checked for the same reason: ES256 is defined over
|
|
155
|
+
* P-256 and nothing else.
|
|
156
|
+
*/
|
|
157
|
+
function assertKeyMatchesAlg(jwk, alg) {
|
|
158
|
+
const spec = SUPPORTED[alg];
|
|
159
|
+
if (jwk.kty !== spec.kty) {
|
|
160
|
+
throw new Error(`[transit] ${alg} needs a ${spec.kty} key, and the provider published a ${String(jwk.kty)} one`);
|
|
161
|
+
}
|
|
162
|
+
const curve = "crv" in spec ? spec.crv : undefined;
|
|
163
|
+
if (curve !== undefined && jwk.crv !== curve) {
|
|
164
|
+
throw new Error(`[transit] ${alg} is defined over ${curve}, and the key is on ${String(jwk.crv)}`);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
//# sourceMappingURL=jwt.js.map
|
package/dist/jwt.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jwt.js","sourceRoot":"","sources":["../src/jwt.ts"],"names":[],"mappings":"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"}
|
package/dist/ldap.d.ts
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The slice of LDAP an application needs to sign somebody in: connect, bind,
|
|
3
|
+
* search, close.
|
|
4
|
+
*
|
|
5
|
+
* Three things here are the difference between an LDAP sign-in that works and
|
|
6
|
+
* one that lets anybody through:
|
|
7
|
+
*
|
|
8
|
+
* - **An empty password is refused before the socket is touched.** A simple
|
|
9
|
+
* bind with no password is an ANONYMOUS bind, and the server answers
|
|
10
|
+
* success. A login form that passes a blank password straight through
|
|
11
|
+
* therefore authenticates as whoever was named. It is the oldest bug in
|
|
12
|
+
* LDAP authentication and it is one `if`.
|
|
13
|
+
* - **Filters are structures, not text.** Values are written as
|
|
14
|
+
* length-prefixed octets, so a username cannot become syntax.
|
|
15
|
+
* - **TLS unless told otherwise.** A simple bind sends the password as it
|
|
16
|
+
* was typed.
|
|
17
|
+
*/
|
|
18
|
+
export declare class LdapError extends Error {
|
|
19
|
+
readonly code: number | undefined;
|
|
20
|
+
constructor(message: string, code?: number);
|
|
21
|
+
}
|
|
22
|
+
/** A filter, as a shape. There is no filter string, on purpose. */
|
|
23
|
+
export type LdapFilter = {
|
|
24
|
+
equals: [attribute: string, value: string];
|
|
25
|
+
} | {
|
|
26
|
+
present: string;
|
|
27
|
+
} | {
|
|
28
|
+
and: LdapFilter[];
|
|
29
|
+
} | {
|
|
30
|
+
or: LdapFilter[];
|
|
31
|
+
} | {
|
|
32
|
+
not: LdapFilter;
|
|
33
|
+
};
|
|
34
|
+
export interface LdapEntry {
|
|
35
|
+
dn: string;
|
|
36
|
+
attributes: Record<string, string[]>;
|
|
37
|
+
}
|
|
38
|
+
export interface LdapOptions {
|
|
39
|
+
/** `ldaps://host:636` or `ldap://host:389`. */
|
|
40
|
+
url: string;
|
|
41
|
+
/**
|
|
42
|
+
* Allow a plaintext connection. A simple bind sends the password as typed,
|
|
43
|
+
* so this has to be asked for.
|
|
44
|
+
*/
|
|
45
|
+
allowInsecure?: boolean;
|
|
46
|
+
/** How long to wait for the server, in milliseconds. Default 10 000. */
|
|
47
|
+
timeoutMs?: number;
|
|
48
|
+
/** Refuse a server certificate that does not check out. Default true. */
|
|
49
|
+
rejectUnauthorized?: boolean;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* One connection, for one exchange.
|
|
53
|
+
*
|
|
54
|
+
* A connection carries the identity of whatever last bound on it, so this is
|
|
55
|
+
* deliberately short-lived: verifying a password means binding as that person,
|
|
56
|
+
* and nothing else may run on the connection afterwards.
|
|
57
|
+
*/
|
|
58
|
+
export declare class LdapConnection {
|
|
59
|
+
#private;
|
|
60
|
+
constructor(options: LdapOptions);
|
|
61
|
+
connect(): Promise<void>;
|
|
62
|
+
/**
|
|
63
|
+
* Bind as `dn` with `password`.
|
|
64
|
+
*
|
|
65
|
+
* An empty password is refused here, before anything is sent. The server
|
|
66
|
+
* would answer success: a simple bind with no password is an anonymous
|
|
67
|
+
* bind, and a login form that passes one through authenticates as whoever
|
|
68
|
+
* was named.
|
|
69
|
+
*/
|
|
70
|
+
bind(dn: string, password: string): Promise<void>;
|
|
71
|
+
/** Search under `base`, and read the entries it answers with. */
|
|
72
|
+
search(options: {
|
|
73
|
+
base: string;
|
|
74
|
+
filter: LdapFilter;
|
|
75
|
+
attributes?: string[];
|
|
76
|
+
scope?: "base" | "one" | "sub";
|
|
77
|
+
sizeLimit?: number;
|
|
78
|
+
}): Promise<LdapEntry[]>;
|
|
79
|
+
/** Say goodbye and close. Never throws: this runs in a finally. */
|
|
80
|
+
close(): Promise<void>;
|
|
81
|
+
}
|
|
82
|
+
//# sourceMappingURL=ldap.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ldap.d.ts","sourceRoot":"","sources":["../src/ldap.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AA8BH,qBAAa,SAAU,SAAQ,KAAK;IACnC,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,CAAC;gBACtB,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM;CAK1C;AAED,mEAAmE;AACnE,MAAM,MAAM,UAAU,GACnB;IAAE,MAAM,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,CAAC,CAAA;CAAE,GAC9C;IAAE,OAAO,EAAE,MAAM,CAAA;CAAE,GACnB;IAAE,GAAG,EAAE,UAAU,EAAE,CAAA;CAAE,GACrB;IAAE,EAAE,EAAE,UAAU,EAAE,CAAA;CAAE,GACpB;IAAE,GAAG,EAAE,UAAU,CAAA;CAAE,CAAC;AAEvB,MAAM,WAAW,SAAS;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;CACrC;AAED,MAAM,WAAW,WAAW;IAC3B,+CAA+C;IAC/C,GAAG,EAAE,MAAM,CAAC;IACZ;;;OAGG;IACH,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,wEAAwE;IACxE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,yEAAyE;IACzE,kBAAkB,CAAC,EAAE,OAAO,CAAC;CAC7B;AAED;;;;;;GAMG;AACH,qBAAa,cAAc;;gBAcd,OAAO,EAAE,WAAW;IAI1B,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IA2C9B;;;;;;;OAOG;IACG,IAAI,CAAC,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAmBvD,iEAAiE;IAC3D,MAAM,CAAC,OAAO,EAAE;QACrB,IAAI,EAAE,MAAM,CAAC;QACb,MAAM,EAAE,UAAU,CAAC;QACnB,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;QACtB,KAAK,CAAC,EAAE,MAAM,GAAG,KAAK,GAAG,KAAK,CAAC;QAC/B,SAAS,CAAC,EAAE,MAAM,CAAC;KACnB,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC;IAsBxB,mEAAmE;IAC7D,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CA0F5B"}
|
package/dist/ldap.js
ADDED
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The slice of LDAP an application needs to sign somebody in: connect, bind,
|
|
3
|
+
* search, close.
|
|
4
|
+
*
|
|
5
|
+
* Three things here are the difference between an LDAP sign-in that works and
|
|
6
|
+
* one that lets anybody through:
|
|
7
|
+
*
|
|
8
|
+
* - **An empty password is refused before the socket is touched.** A simple
|
|
9
|
+
* bind with no password is an ANONYMOUS bind, and the server answers
|
|
10
|
+
* success. A login form that passes a blank password straight through
|
|
11
|
+
* therefore authenticates as whoever was named. It is the oldest bug in
|
|
12
|
+
* LDAP authentication and it is one `if`.
|
|
13
|
+
* - **Filters are structures, not text.** Values are written as
|
|
14
|
+
* length-prefixed octets, so a username cannot become syntax.
|
|
15
|
+
* - **TLS unless told otherwise.** A simple bind sends the password as it
|
|
16
|
+
* was typed.
|
|
17
|
+
*/
|
|
18
|
+
import { connect as connectTcp } from "node:net";
|
|
19
|
+
import { connect as connectTls } from "node:tls";
|
|
20
|
+
import { application, applicationPrimitive, BOOLEAN, context, contextPrimitive, decode, decodeInteger, decodeSequence, decodeString, ENUMERATED, encodeBoolean, encodeInteger, encodeSequence, encodeString, messageLength, OCTET_STRING, } from "./ber.js";
|
|
21
|
+
const BIND_REQUEST = application(0);
|
|
22
|
+
const BIND_RESPONSE = application(1);
|
|
23
|
+
const UNBIND_REQUEST = applicationPrimitive(2);
|
|
24
|
+
const SEARCH_REQUEST = application(3);
|
|
25
|
+
const SEARCH_ENTRY = application(4);
|
|
26
|
+
const SEARCH_DONE = application(5);
|
|
27
|
+
export class LdapError extends Error {
|
|
28
|
+
code;
|
|
29
|
+
constructor(message, code) {
|
|
30
|
+
super(`[transit] ${message}`);
|
|
31
|
+
this.name = "LdapError";
|
|
32
|
+
this.code = code;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* One connection, for one exchange.
|
|
37
|
+
*
|
|
38
|
+
* A connection carries the identity of whatever last bound on it, so this is
|
|
39
|
+
* deliberately short-lived: verifying a password means binding as that person,
|
|
40
|
+
* and nothing else may run on the connection afterwards.
|
|
41
|
+
*/
|
|
42
|
+
export class LdapConnection {
|
|
43
|
+
#socket;
|
|
44
|
+
#buffer = Buffer.alloc(0);
|
|
45
|
+
#nextMessage = 1;
|
|
46
|
+
#pending = new Map();
|
|
47
|
+
#options;
|
|
48
|
+
constructor(options) {
|
|
49
|
+
this.#options = options;
|
|
50
|
+
}
|
|
51
|
+
async connect() {
|
|
52
|
+
const url = new URL(this.#options.url);
|
|
53
|
+
const secure = url.protocol === "ldaps:";
|
|
54
|
+
if (!secure && this.#options.allowInsecure !== true) {
|
|
55
|
+
throw new LdapError(`'${this.#options.url}' is not encrypted, and a simple bind sends the password as it was typed. Use ldaps://, or set allowInsecure when the link is already protected.`);
|
|
56
|
+
}
|
|
57
|
+
const port = Number(url.port || (secure ? 636 : 389));
|
|
58
|
+
const host = url.hostname;
|
|
59
|
+
await new Promise((resolve, reject) => {
|
|
60
|
+
const socket = secure
|
|
61
|
+
? connectTls({
|
|
62
|
+
host,
|
|
63
|
+
port,
|
|
64
|
+
servername: host,
|
|
65
|
+
rejectUnauthorized: this.#options.rejectUnauthorized !== false,
|
|
66
|
+
})
|
|
67
|
+
: connectTcp({ host, port });
|
|
68
|
+
const onError = (error) => {
|
|
69
|
+
this.#failAll(error);
|
|
70
|
+
reject(new LdapError(`could not reach ${host}:${port} — ${error.message}`));
|
|
71
|
+
};
|
|
72
|
+
socket.once("error", onError);
|
|
73
|
+
socket.once(secure ? "secureConnect" : "connect", () => {
|
|
74
|
+
socket.setTimeout(this.#options.timeoutMs ?? 10_000);
|
|
75
|
+
socket.on("data", (chunk) => this.#receive(chunk));
|
|
76
|
+
socket.on("timeout", () => this.#failAll(new LdapError("the directory stopped answering")));
|
|
77
|
+
socket.on("close", () => this.#failAll(new LdapError("the directory closed the connection")));
|
|
78
|
+
this.#socket = socket;
|
|
79
|
+
resolve();
|
|
80
|
+
});
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Bind as `dn` with `password`.
|
|
85
|
+
*
|
|
86
|
+
* An empty password is refused here, before anything is sent. The server
|
|
87
|
+
* would answer success: a simple bind with no password is an anonymous
|
|
88
|
+
* bind, and a login form that passes one through authenticates as whoever
|
|
89
|
+
* was named.
|
|
90
|
+
*/
|
|
91
|
+
async bind(dn, password) {
|
|
92
|
+
if (password === "") {
|
|
93
|
+
throw new LdapError("an empty password is an anonymous bind, which the directory accepts — refused here instead");
|
|
94
|
+
}
|
|
95
|
+
const body = encodeSequence([
|
|
96
|
+
encodeInteger(3),
|
|
97
|
+
encodeString(dn),
|
|
98
|
+
encodeString(password, contextPrimitive(0)),
|
|
99
|
+
], BIND_REQUEST);
|
|
100
|
+
const [response] = await this.#exchange(body, [BIND_RESPONSE]);
|
|
101
|
+
if (!response)
|
|
102
|
+
throw new LdapError("the directory sent no bind response");
|
|
103
|
+
assertSuccess(response, "bind");
|
|
104
|
+
}
|
|
105
|
+
/** Search under `base`, and read the entries it answers with. */
|
|
106
|
+
async search(options) {
|
|
107
|
+
const scope = { base: 0, one: 1, sub: 2 }[options.scope ?? "sub"];
|
|
108
|
+
const body = encodeSequence([
|
|
109
|
+
encodeString(options.base),
|
|
110
|
+
encodeInteger(scope, ENUMERATED),
|
|
111
|
+
encodeInteger(0, ENUMERATED),
|
|
112
|
+
encodeInteger(options.sizeLimit ?? 2),
|
|
113
|
+
encodeInteger(0),
|
|
114
|
+
encodeBoolean(false, BOOLEAN),
|
|
115
|
+
encodeFilter(options.filter),
|
|
116
|
+
encodeSequence((options.attributes ?? []).map((a) => encodeString(a))),
|
|
117
|
+
], SEARCH_REQUEST);
|
|
118
|
+
const parts = await this.#exchange(body, [SEARCH_ENTRY], SEARCH_DONE);
|
|
119
|
+
const done = parts.at(-1);
|
|
120
|
+
if (done)
|
|
121
|
+
assertSuccess(done, "search");
|
|
122
|
+
return parts.slice(0, -1).map(readEntry);
|
|
123
|
+
}
|
|
124
|
+
/** Say goodbye and close. Never throws: this runs in a finally. */
|
|
125
|
+
async close() {
|
|
126
|
+
const socket = this.#socket;
|
|
127
|
+
if (!socket)
|
|
128
|
+
return;
|
|
129
|
+
this.#socket = undefined;
|
|
130
|
+
try {
|
|
131
|
+
socket.write(encodeSequence([
|
|
132
|
+
encodeInteger(this.#nextMessage),
|
|
133
|
+
encodeSequence([], UNBIND_REQUEST),
|
|
134
|
+
]));
|
|
135
|
+
}
|
|
136
|
+
catch {
|
|
137
|
+
// The point is to end the connection; failing to be polite about it
|
|
138
|
+
// changes nothing.
|
|
139
|
+
}
|
|
140
|
+
socket.destroy();
|
|
141
|
+
this.#failAll(new LdapError("the connection was closed"));
|
|
142
|
+
}
|
|
143
|
+
/** Send one request and collect the replies that carry its message id. */
|
|
144
|
+
async #exchange(body, collect, until) {
|
|
145
|
+
const socket = this.#socket;
|
|
146
|
+
if (!socket)
|
|
147
|
+
throw new LdapError("the connection is not open");
|
|
148
|
+
const id = this.#nextMessage++;
|
|
149
|
+
return new Promise((resolve, reject) => {
|
|
150
|
+
this.#pending.set(id, { resolve, reject, parts: [] });
|
|
151
|
+
this.#expect.set(id, { collect, until });
|
|
152
|
+
socket.write(encodeSequence([encodeInteger(id), body]), (error) => {
|
|
153
|
+
if (error) {
|
|
154
|
+
this.#pending.delete(id);
|
|
155
|
+
reject(new LdapError(`the request could not be sent — ${error.message}`));
|
|
156
|
+
}
|
|
157
|
+
});
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
#expect = new Map();
|
|
161
|
+
#receive(chunk) {
|
|
162
|
+
this.#buffer = Buffer.concat([this.#buffer, chunk]);
|
|
163
|
+
for (;;) {
|
|
164
|
+
const length = messageLength(this.#buffer);
|
|
165
|
+
if (length === undefined || this.#buffer.length < length)
|
|
166
|
+
return;
|
|
167
|
+
const message = this.#buffer.subarray(0, length);
|
|
168
|
+
this.#buffer = this.#buffer.subarray(length);
|
|
169
|
+
this.#dispatch(message);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
#dispatch(message) {
|
|
173
|
+
const [idElement, op] = decodeSequence(decode(message).value);
|
|
174
|
+
if (!idElement || !op)
|
|
175
|
+
return;
|
|
176
|
+
const id = decodeInteger(idElement.value);
|
|
177
|
+
const waiting = this.#pending.get(id);
|
|
178
|
+
const expected = this.#expect.get(id);
|
|
179
|
+
if (!waiting || !expected)
|
|
180
|
+
return;
|
|
181
|
+
if (expected.until === undefined) {
|
|
182
|
+
// A single-reply operation: this is the answer.
|
|
183
|
+
this.#finish(id, [op.value]);
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
waiting.parts.push(op.value);
|
|
187
|
+
if (op.tag === expected.until)
|
|
188
|
+
this.#finish(id, waiting.parts);
|
|
189
|
+
}
|
|
190
|
+
#finish(id, parts) {
|
|
191
|
+
const waiting = this.#pending.get(id);
|
|
192
|
+
this.#pending.delete(id);
|
|
193
|
+
this.#expect.delete(id);
|
|
194
|
+
waiting?.resolve(parts);
|
|
195
|
+
}
|
|
196
|
+
#failAll(error) {
|
|
197
|
+
for (const [id, waiting] of this.#pending) {
|
|
198
|
+
this.#pending.delete(id);
|
|
199
|
+
this.#expect.delete(id);
|
|
200
|
+
waiting.reject(error);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
/** A filter, encoded. Values become octets, never syntax. */
|
|
205
|
+
function encodeFilter(filter) {
|
|
206
|
+
if ("equals" in filter) {
|
|
207
|
+
const [attribute, value] = filter.equals;
|
|
208
|
+
return encodeSequence([encodeString(attribute), encodeString(value)], context(3));
|
|
209
|
+
}
|
|
210
|
+
if ("present" in filter) {
|
|
211
|
+
return encodeString(filter.present, contextPrimitive(7));
|
|
212
|
+
}
|
|
213
|
+
if ("and" in filter) {
|
|
214
|
+
return encodeSequence(filter.and.map(encodeFilter), context(0));
|
|
215
|
+
}
|
|
216
|
+
if ("or" in filter) {
|
|
217
|
+
return encodeSequence(filter.or.map(encodeFilter), context(1));
|
|
218
|
+
}
|
|
219
|
+
return encodeSequence([encodeFilter(filter.not)], context(2));
|
|
220
|
+
}
|
|
221
|
+
function readEntry(value) {
|
|
222
|
+
const [dnElement, attributesElement] = decodeSequence(value);
|
|
223
|
+
const attributes = {};
|
|
224
|
+
for (const attribute of attributesElement
|
|
225
|
+
? decodeSequence(attributesElement.value)
|
|
226
|
+
: []) {
|
|
227
|
+
const [nameElement, valuesElement] = decodeSequence(attribute.value);
|
|
228
|
+
if (!nameElement)
|
|
229
|
+
continue;
|
|
230
|
+
attributes[decodeString(nameElement.value)] = valuesElement
|
|
231
|
+
? decodeSequence(valuesElement.value).map((v) => decodeString(v.value))
|
|
232
|
+
: [];
|
|
233
|
+
}
|
|
234
|
+
return {
|
|
235
|
+
dn: dnElement ? decodeString(dnElement.value) : "",
|
|
236
|
+
attributes,
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
/** LDAP answers a code; anything but zero is a refusal worth reporting. */
|
|
240
|
+
function assertSuccess(response, what) {
|
|
241
|
+
const [codeElement, , messageElement] = decodeSequence(response);
|
|
242
|
+
if (!codeElement)
|
|
243
|
+
throw new LdapError(`the ${what} response has no result code`);
|
|
244
|
+
const code = decodeInteger(codeElement.value);
|
|
245
|
+
if (code === 0)
|
|
246
|
+
return;
|
|
247
|
+
const detail = messageElement && messageElement.tag === OCTET_STRING
|
|
248
|
+
? decodeString(messageElement.value)
|
|
249
|
+
: "";
|
|
250
|
+
throw new LdapError(`the directory refused the ${what}: ${describe(code)}${detail ? ` — ${detail}` : ""}`, code);
|
|
251
|
+
}
|
|
252
|
+
/** The result codes worth naming; the rest are reported by number. */
|
|
253
|
+
function describe(code) {
|
|
254
|
+
switch (code) {
|
|
255
|
+
case 32:
|
|
256
|
+
return "no such object";
|
|
257
|
+
case 34:
|
|
258
|
+
return "invalid DN syntax";
|
|
259
|
+
case 48:
|
|
260
|
+
return "authentication is not allowed";
|
|
261
|
+
case 49:
|
|
262
|
+
return "invalid credentials";
|
|
263
|
+
case 50:
|
|
264
|
+
return "insufficient rights";
|
|
265
|
+
case 53:
|
|
266
|
+
return "the server is unwilling to perform this";
|
|
267
|
+
default:
|
|
268
|
+
return `result code ${code}`;
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
//# sourceMappingURL=ldap.js.map
|
package/dist/ldap.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ldap.js","sourceRoot":"","sources":["../src/ldap.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,EAAE,OAAO,IAAI,UAAU,EAAe,MAAM,UAAU,CAAC;AAC9D,OAAO,EAAE,OAAO,IAAI,UAAU,EAAE,MAAM,UAAU,CAAC;AACjD,OAAO,EACN,WAAW,EACX,oBAAoB,EACpB,OAAO,EACP,OAAO,EACP,gBAAgB,EAChB,MAAM,EACN,aAAa,EACb,cAAc,EACd,YAAY,EACZ,UAAU,EACV,aAAa,EACb,aAAa,EACb,cAAc,EACd,YAAY,EACZ,aAAa,EACb,YAAY,GACZ,MAAM,UAAU,CAAC;AAElB,MAAM,YAAY,GAAG,WAAW,CAAC,CAAC,CAAC,CAAC;AACpC,MAAM,aAAa,GAAG,WAAW,CAAC,CAAC,CAAC,CAAC;AACrC,MAAM,cAAc,GAAG,oBAAoB,CAAC,CAAC,CAAC,CAAC;AAC/C,MAAM,cAAc,GAAG,WAAW,CAAC,CAAC,CAAC,CAAC;AACtC,MAAM,YAAY,GAAG,WAAW,CAAC,CAAC,CAAC,CAAC;AACpC,MAAM,WAAW,GAAG,WAAW,CAAC,CAAC,CAAC,CAAC;AAEnC,MAAM,OAAO,SAAU,SAAQ,KAAK;IAC1B,IAAI,CAAqB;IAClC,YAAY,OAAe,EAAE,IAAa;QACzC,KAAK,CAAC,aAAa,OAAO,EAAE,CAAC,CAAC;QAC9B,IAAI,CAAC,IAAI,GAAG,WAAW,CAAC;QACxB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IAClB,CAAC;CACD;AA6BD;;;;;;GAMG;AACH,MAAM,OAAO,cAAc;IAC1B,OAAO,CAAqB;IAC5B,OAAO,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IAC1B,YAAY,GAAG,CAAC,CAAC;IACjB,QAAQ,GAAG,IAAI,GAAG,EAOf,CAAC;IACK,QAAQ,CAAc;IAE/B,YAAY,OAAoB;QAC/B,IAAI,CAAC,QAAQ,GAAG,OAAO,CAAC;IACzB,CAAC;IAED,KAAK,CAAC,OAAO;QACZ,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;QACvC,MAAM,MAAM,GAAG,GAAG,CAAC,QAAQ,KAAK,QAAQ,CAAC;QACzC,IAAI,CAAC,MAAM,IAAI,IAAI,CAAC,QAAQ,CAAC,aAAa,KAAK,IAAI,EAAE,CAAC;YACrD,MAAM,IAAI,SAAS,CAClB,IAAI,IAAI,CAAC,QAAQ,CAAC,GAAG,kJAAkJ,CACvK,CAAC;QACH,CAAC;QACD,MAAM,IAAI,GAAG,MAAM,CAAC,GAAG,CAAC,IAAI,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;QACtD,MAAM,IAAI,GAAG,GAAG,CAAC,QAAQ,CAAC;QAE1B,MAAM,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YAC3C,MAAM,MAAM,GAAG,MAAM;gBACpB,CAAC,CAAC,UAAU,CAAC;oBACX,IAAI;oBACJ,IAAI;oBACJ,UAAU,EAAE,IAAI;oBAChB,kBAAkB,EAAE,IAAI,CAAC,QAAQ,CAAC,kBAAkB,KAAK,KAAK;iBAC9D,CAAC;gBACH,CAAC,CAAC,UAAU,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;YAE9B,MAAM,OAAO,GAAG,CAAC,KAAY,EAAE,EAAE;gBAChC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;gBACrB,MAAM,CACL,IAAI,SAAS,CAAC,mBAAmB,IAAI,IAAI,IAAI,MAAM,KAAK,CAAC,OAAO,EAAE,CAAC,CACnE,CAAC;YACH,CAAC,CAAC;YACF,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;YAC9B,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,SAAS,EAAE,GAAG,EAAE;gBACtD,MAAM,CAAC,UAAU,CAAC,IAAI,CAAC,QAAQ,CAAC,SAAS,IAAI,MAAM,CAAC,CAAC;gBACrD,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC;gBACnD,MAAM,CAAC,EAAE,CAAC,SAAS,EAAE,GAAG,EAAE,CACzB,IAAI,CAAC,QAAQ,CAAC,IAAI,SAAS,CAAC,iCAAiC,CAAC,CAAC,CAC/D,CAAC;gBACF,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE,CACvB,IAAI,CAAC,QAAQ,CAAC,IAAI,SAAS,CAAC,qCAAqC,CAAC,CAAC,CACnE,CAAC;gBACF,IAAI,CAAC,OAAO,GAAG,MAAM,CAAC;gBACtB,OAAO,EAAE,CAAC;YACX,CAAC,CAAC,CAAC;QACJ,CAAC,CAAC,CAAC;IACJ,CAAC;IAED;;;;;;;OAOG;IACH,KAAK,CAAC,IAAI,CAAC,EAAU,EAAE,QAAgB;QACtC,IAAI,QAAQ,KAAK,EAAE,EAAE,CAAC;YACrB,MAAM,IAAI,SAAS,CAClB,4FAA4F,CAC5F,CAAC;QACH,CAAC;QACD,MAAM,IAAI,GAAG,cAAc,CAC1B;YACC,aAAa,CAAC,CAAC,CAAC;YAChB,YAAY,CAAC,EAAE,CAAC;YAChB,YAAY,CAAC,QAAQ,EAAE,gBAAgB,CAAC,CAAC,CAAC,CAAC;SAC3C,EACD,YAAY,CACZ,CAAC;QACF,MAAM,CAAC,QAAQ,CAAC,GAAG,MAAM,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,CAAC,aAAa,CAAC,CAAC,CAAC;QAC/D,IAAI,CAAC,QAAQ;YAAE,MAAM,IAAI,SAAS,CAAC,qCAAqC,CAAC,CAAC;QAC1E,aAAa,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;IACjC,CAAC;IAED,iEAAiE;IACjE,KAAK,CAAC,MAAM,CAAC,OAMZ;QACA,MAAM,KAAK,GAAG,EAAE,IAAI,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,CAAC,OAAO,CAAC,KAAK,IAAI,KAAK,CAAC,CAAC;QAClE,MAAM,IAAI,GAAG,cAAc,CAC1B;YACC,YAAY,CAAC,OAAO,CAAC,IAAI,CAAC;YAC1B,aAAa,CAAC,KAAK,EAAE,UAAU,CAAC;YAChC,aAAa,CAAC,CAAC,EAAE,UAAU,CAAC;YAC5B,aAAa,CAAC,OAAO,CAAC,SAAS,IAAI,CAAC,CAAC;YACrC,aAAa,CAAC,CAAC,CAAC;YAChB,aAAa,CAAC,KAAK,EAAE,OAAO,CAAC;YAC7B,YAAY,CAAC,OAAO,CAAC,MAAM,CAAC;YAC5B,cAAc,CAAC,CAAC,OAAO,CAAC,UAAU,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC;SACtE,EACD,cAAc,CACd,CAAC;QAEF,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,CAAC,YAAY,CAAC,EAAE,WAAW,CAAC,CAAC;QACtE,MAAM,IAAI,GAAG,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;QAC1B,IAAI,IAAI;YAAE,aAAa,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;QACxC,OAAO,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;IAC1C,CAAC;IAED,mEAAmE;IACnE,KAAK,CAAC,KAAK;QACV,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC;QAC5B,IAAI,CAAC,MAAM;YAAE,OAAO;QACpB,IAAI,CAAC,OAAO,GAAG,SAAS,CAAC;QACzB,IAAI,CAAC;YACJ,MAAM,CAAC,KAAK,CACX,cAAc,CAAC;gBACd,aAAa,CAAC,IAAI,CAAC,YAAY,CAAC;gBAChC,cAAc,CAAC,EAAE,EAAE,cAAc,CAAC;aAClC,CAAC,CACF,CAAC;QACH,CAAC;QAAC,MAAM,CAAC;YACR,oEAAoE;YACpE,mBAAmB;QACpB,CAAC;QACD,MAAM,CAAC,OAAO,EAAE,CAAC;QACjB,IAAI,CAAC,QAAQ,CAAC,IAAI,SAAS,CAAC,2BAA2B,CAAC,CAAC,CAAC;IAC3D,CAAC;IAED,0EAA0E;IAC1E,KAAK,CAAC,SAAS,CACd,IAAY,EACZ,OAAiB,EACjB,KAAc;QAEd,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC;QAC5B,IAAI,CAAC,MAAM;YAAE,MAAM,IAAI,SAAS,CAAC,4BAA4B,CAAC,CAAC;QAC/D,MAAM,EAAE,GAAG,IAAI,CAAC,YAAY,EAAE,CAAC;QAE/B,OAAO,IAAI,OAAO,CAAW,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YAChD,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC,CAAC;YACtD,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC,CAAC;YACzC,MAAM,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC,aAAa,CAAC,EAAE,CAAC,EAAE,IAAI,CAAC,CAAC,EAAE,CAAC,KAAK,EAAE,EAAE;gBACjE,IAAI,KAAK,EAAE,CAAC;oBACX,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;oBACzB,MAAM,CACL,IAAI,SAAS,CAAC,mCAAmC,KAAK,CAAC,OAAO,EAAE,CAAC,CACjE,CAAC;gBACH,CAAC;YACF,CAAC,CAAC,CAAC;QACJ,CAAC,CAAC,CAAC;IACJ,CAAC;IAEQ,OAAO,GAAG,IAAI,GAAG,EAGvB,CAAC;IAEJ,QAAQ,CAAC,KAAa;QACrB,IAAI,CAAC,OAAO,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC,CAAC;QACpD,SAAS,CAAC;YACT,MAAM,MAAM,GAAG,aAAa,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;YAC3C,IAAI,MAAM,KAAK,SAAS,IAAI,IAAI,CAAC,OAAO,CAAC,MAAM,GAAG,MAAM;gBAAE,OAAO;YACjE,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC;YACjD,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;YAC7C,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;QACzB,CAAC;IACF,CAAC;IAED,SAAS,CAAC,OAAe;QACxB,MAAM,CAAC,SAAS,EAAE,EAAE,CAAC,GAAG,cAAc,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,KAAK,CAAC,CAAC;QAC9D,IAAI,CAAC,SAAS,IAAI,CAAC,EAAE;YAAE,OAAO;QAC9B,MAAM,EAAE,GAAG,aAAa,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;QAC1C,MAAM,OAAO,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QACtC,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QACtC,IAAI,CAAC,OAAO,IAAI,CAAC,QAAQ;YAAE,OAAO;QAElC,IAAI,QAAQ,CAAC,KAAK,KAAK,SAAS,EAAE,CAAC;YAClC,gDAAgD;YAChD,IAAI,CAAC,OAAO,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC;YAC7B,OAAO;QACR,CAAC;QACD,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC;QAC7B,IAAI,EAAE,CAAC,GAAG,KAAK,QAAQ,CAAC,KAAK;YAAE,IAAI,CAAC,OAAO,CAAC,EAAE,EAAE,OAAO,CAAC,KAAK,CAAC,CAAC;IAChE,CAAC;IAED,OAAO,CAAC,EAAU,EAAE,KAAe;QAClC,MAAM,OAAO,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QACtC,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QACzB,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QACxB,OAAO,EAAE,OAAO,CAAC,KAAK,CAAC,CAAC;IACzB,CAAC;IAED,QAAQ,CAAC,KAAY;QACpB,KAAK,MAAM,CAAC,EAAE,EAAE,OAAO,CAAC,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;YAC3C,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;YACzB,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;YACxB,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QACvB,CAAC;IACF,CAAC;CACD;AAED,6DAA6D;AAC7D,SAAS,YAAY,CAAC,MAAkB;IACvC,IAAI,QAAQ,IAAI,MAAM,EAAE,CAAC;QACxB,MAAM,CAAC,SAAS,EAAE,KAAK,CAAC,GAAG,MAAM,CAAC,MAAM,CAAC;QACzC,OAAO,cAAc,CACpB,CAAC,YAAY,CAAC,SAAS,CAAC,EAAE,YAAY,CAAC,KAAK,CAAC,CAAC,EAC9C,OAAO,CAAC,CAAC,CAAC,CACV,CAAC;IACH,CAAC;IACD,IAAI,SAAS,IAAI,MAAM,EAAE,CAAC;QACzB,OAAO,YAAY,CAAC,MAAM,CAAC,OAAO,EAAE,gBAAgB,CAAC,CAAC,CAAC,CAAC,CAAC;IAC1D,CAAC;IACD,IAAI,KAAK,IAAI,MAAM,EAAE,CAAC;QACrB,OAAO,cAAc,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,YAAY,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC;IACjE,CAAC;IACD,IAAI,IAAI,IAAI,MAAM,EAAE,CAAC;QACpB,OAAO,cAAc,CAAC,MAAM,CAAC,EAAE,CAAC,GAAG,CAAC,YAAY,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC;IAChE,CAAC;IACD,OAAO,cAAc,CAAC,CAAC,YAAY,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC;AAC/D,CAAC;AAED,SAAS,SAAS,CAAC,KAAa;IAC/B,MAAM,CAAC,SAAS,EAAE,iBAAiB,CAAC,GAAG,cAAc,CAAC,KAAK,CAAC,CAAC;IAC7D,MAAM,UAAU,GAA6B,EAAE,CAAC;IAChD,KAAK,MAAM,SAAS,IAAI,iBAAiB;QACxC,CAAC,CAAC,cAAc,CAAC,iBAAiB,CAAC,KAAK,CAAC;QACzC,CAAC,CAAC,EAAE,EAAE,CAAC;QACP,MAAM,CAAC,WAAW,EAAE,aAAa,CAAC,GAAG,cAAc,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;QACrE,IAAI,CAAC,WAAW;YAAE,SAAS;QAC3B,UAAU,CAAC,YAAY,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC,GAAG,aAAa;YAC1D,CAAC,CAAC,cAAc,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,YAAY,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;YACvE,CAAC,CAAC,EAAE,CAAC;IACP,CAAC;IACD,OAAO;QACN,EAAE,EAAE,SAAS,CAAC,CAAC,CAAC,YAAY,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE;QAClD,UAAU;KACV,CAAC;AACH,CAAC;AAED,2EAA2E;AAC3E,SAAS,aAAa,CAAC,QAAgB,EAAE,IAAY;IACpD,MAAM,CAAC,WAAW,EAAE,AAAD,EAAG,cAAc,CAAC,GAAG,cAAc,CAAC,QAAQ,CAAC,CAAC;IACjE,IAAI,CAAC,WAAW;QACf,MAAM,IAAI,SAAS,CAAC,OAAO,IAAI,8BAA8B,CAAC,CAAC;IAChE,MAAM,IAAI,GAAG,aAAa,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC;IAC9C,IAAI,IAAI,KAAK,CAAC;QAAE,OAAO;IACvB,MAAM,MAAM,GACX,cAAc,IAAI,cAAc,CAAC,GAAG,KAAK,YAAY;QACpD,CAAC,CAAC,YAAY,CAAC,cAAc,CAAC,KAAK,CAAC;QACpC,CAAC,CAAC,EAAE,CAAC;IACP,MAAM,IAAI,SAAS,CAClB,6BAA6B,IAAI,KAAK,QAAQ,CAAC,IAAI,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,MAAM,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,EACrF,IAAI,CACJ,CAAC;AACH,CAAC;AAED,sEAAsE;AACtE,SAAS,QAAQ,CAAC,IAAY;IAC7B,QAAQ,IAAI,EAAE,CAAC;QACd,KAAK,EAAE;YACN,OAAO,gBAAgB,CAAC;QACzB,KAAK,EAAE;YACN,OAAO,mBAAmB,CAAC;QAC5B,KAAK,EAAE;YACN,OAAO,+BAA+B,CAAC;QACxC,KAAK,EAAE;YACN,OAAO,qBAAqB,CAAC;QAC9B,KAAK,EAAE;YACN,OAAO,qBAAqB,CAAC;QAC9B,KAAK,EAAE;YACN,OAAO,yCAAyC,CAAC;QAClD;YACC,OAAO,eAAe,IAAI,EAAE,CAAC;IAC/B,CAAC;AACF,CAAC"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reading `NODE_ENV`, with the aliases people actually set.
|
|
3
|
+
*
|
|
4
|
+
* `NODE_ENV=prod` is ordinary in a Dockerfile or a platform dashboard. Read
|
|
5
|
+
* verbatim it answers "not production" — and here that means the in-process
|
|
6
|
+
* SAML replay store is accepted on a production deployment, where it bounds
|
|
7
|
+
* replay within one replica and not beyond it.
|
|
8
|
+
*
|
|
9
|
+
* Duplicated rather than imported: transit depends on no other package in this
|
|
10
|
+
* workspace, and a security decision that only holds when an optional peer is
|
|
11
|
+
* installed is not a decision.
|
|
12
|
+
*/
|
|
13
|
+
/** The canonical name for whatever `NODE_ENV` holds. */
|
|
14
|
+
export declare function normalizeNodeEnv(value: string | undefined): string;
|
|
15
|
+
/** Whether this process is running in production, under any spelling. */
|
|
16
|
+
export declare function inProduction(): boolean;
|
|
17
|
+
//# sourceMappingURL=nodeEnv.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"nodeEnv.d.ts","sourceRoot":"","sources":["../src/nodeEnv.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAMH,wDAAwD;AACxD,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,CAOlE;AAED,yEAAyE;AACzE,wBAAgB,YAAY,IAAI,OAAO,CAEtC"}
|
package/dist/nodeEnv.js
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reading `NODE_ENV`, with the aliases people actually set.
|
|
3
|
+
*
|
|
4
|
+
* `NODE_ENV=prod` is ordinary in a Dockerfile or a platform dashboard. Read
|
|
5
|
+
* verbatim it answers "not production" — and here that means the in-process
|
|
6
|
+
* SAML replay store is accepted on a production deployment, where it bounds
|
|
7
|
+
* replay within one replica and not beyond it.
|
|
8
|
+
*
|
|
9
|
+
* Duplicated rather than imported: transit depends on no other package in this
|
|
10
|
+
* workspace, and a security decision that only holds when an optional peer is
|
|
11
|
+
* installed is not a decision.
|
|
12
|
+
*/
|
|
13
|
+
const DEV_ENVS = ["dev", "develop", "development"];
|
|
14
|
+
const PROD_ENVS = ["prod", "production"];
|
|
15
|
+
const TEST_ENVS = ["test", "testing"];
|
|
16
|
+
/** The canonical name for whatever `NODE_ENV` holds. */
|
|
17
|
+
export function normalizeNodeEnv(value) {
|
|
18
|
+
if (!value || typeof value !== "string")
|
|
19
|
+
return "unknown";
|
|
20
|
+
const env = value.toLowerCase();
|
|
21
|
+
if (DEV_ENVS.includes(env))
|
|
22
|
+
return "development";
|
|
23
|
+
if (PROD_ENVS.includes(env))
|
|
24
|
+
return "production";
|
|
25
|
+
if (TEST_ENVS.includes(env))
|
|
26
|
+
return "test";
|
|
27
|
+
return env;
|
|
28
|
+
}
|
|
29
|
+
/** Whether this process is running in production, under any spelling. */
|
|
30
|
+
export function inProduction() {
|
|
31
|
+
return normalizeNodeEnv(process.env.NODE_ENV) === "production";
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=nodeEnv.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"nodeEnv.js","sourceRoot":"","sources":["../src/nodeEnv.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,MAAM,QAAQ,GAAG,CAAC,KAAK,EAAE,SAAS,EAAE,aAAa,CAAC,CAAC;AACnD,MAAM,SAAS,GAAG,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;AACzC,MAAM,SAAS,GAAG,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;AAEtC,wDAAwD;AACxD,MAAM,UAAU,gBAAgB,CAAC,KAAyB;IACzD,IAAI,CAAC,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,SAAS,CAAC;IAC1D,MAAM,GAAG,GAAG,KAAK,CAAC,WAAW,EAAE,CAAC;IAChC,IAAI,QAAQ,CAAC,QAAQ,CAAC,GAAG,CAAC;QAAE,OAAO,aAAa,CAAC;IACjD,IAAI,SAAS,CAAC,QAAQ,CAAC,GAAG,CAAC;QAAE,OAAO,YAAY,CAAC;IACjD,IAAI,SAAS,CAAC,QAAQ,CAAC,GAAG,CAAC;QAAE,OAAO,MAAM,CAAC;IAC3C,OAAO,GAAG,CAAC;AACZ,CAAC;AAED,yEAAyE;AACzE,MAAM,UAAU,YAAY;IAC3B,OAAO,gBAAgB,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,KAAK,YAAY,CAAC;AAChE,CAAC"}
|