@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
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The half of an OAuth2 sign-in every provider shares.
|
|
3
|
+
*
|
|
4
|
+
* Each provider differs in three places — where you send the user, where the
|
|
5
|
+
* code is exchanged, and what the profile payload is called — and agrees on
|
|
6
|
+
* everything else. This holds the everything else, so a driver is the three
|
|
7
|
+
* URLs, the default scopes, and the mapping from that provider's payload to
|
|
8
|
+
* {@link TransitUser}.
|
|
9
|
+
*
|
|
10
|
+
* Nothing here is stateful: the `state` and, where a provider requires it, the
|
|
11
|
+
* PKCE verifier are passed in and handed back by the caller. Warden does not
|
|
12
|
+
* own your session, so it cannot store them for you — and that is also what
|
|
13
|
+
* lets one driver serve several concurrent sign-ins.
|
|
14
|
+
*/
|
|
15
|
+
import { createHash, randomBytes, randomUUID } from "node:crypto";
|
|
16
|
+
import { assertOAuthState } from "./types.js";
|
|
17
|
+
/**
|
|
18
|
+
* Mint a PKCE code verifier.
|
|
19
|
+
*
|
|
20
|
+
* Store it next to the `state` — the same place, with the same lifetime — and
|
|
21
|
+
* hand it back to `callback()`. A provider that requires PKCE binds the
|
|
22
|
+
* authorization code to this value, so a code intercepted on its way back is
|
|
23
|
+
* useless to whoever intercepted it.
|
|
24
|
+
*/
|
|
25
|
+
export function createCodeVerifier() {
|
|
26
|
+
// 32 bytes of entropy, base64url — 43 characters, the shortest length
|
|
27
|
+
// RFC 7636 allows, and the length every provider accepts.
|
|
28
|
+
return randomBytes(32).toString("base64url");
|
|
29
|
+
}
|
|
30
|
+
export class Oauth2Driver {
|
|
31
|
+
config;
|
|
32
|
+
/**
|
|
33
|
+
* Providers that require PKCE. The redirect then refuses to build without
|
|
34
|
+
* a verifier rather than sending the user to a URL the provider rejects.
|
|
35
|
+
*/
|
|
36
|
+
requiresPkce = false;
|
|
37
|
+
/**
|
|
38
|
+
* Send the client credentials as HTTP Basic on the token request instead of
|
|
39
|
+
* in the body. A handful of providers accept nothing else.
|
|
40
|
+
*/
|
|
41
|
+
tokenAuth = "body";
|
|
42
|
+
constructor(config) {
|
|
43
|
+
this.config = config;
|
|
44
|
+
}
|
|
45
|
+
/** Extra parameters this provider wants on the authorize URL. */
|
|
46
|
+
authorizeParams() {
|
|
47
|
+
return {};
|
|
48
|
+
}
|
|
49
|
+
/** Extra query parameters this provider wants on the profile request. */
|
|
50
|
+
userInfoParams() {
|
|
51
|
+
return {};
|
|
52
|
+
}
|
|
53
|
+
scopes() {
|
|
54
|
+
return [...(this.config.scopes ?? this.defaultScopes)];
|
|
55
|
+
}
|
|
56
|
+
redirectUrl(state, secret) {
|
|
57
|
+
const params = new URLSearchParams({
|
|
58
|
+
client_id: this.config.clientId,
|
|
59
|
+
redirect_uri: this.config.callbackUrl,
|
|
60
|
+
response_type: "code",
|
|
61
|
+
scope: this.scopes().join(" "),
|
|
62
|
+
...this.authorizeParams(),
|
|
63
|
+
...this.config.authorizeParams,
|
|
64
|
+
...(state ? { state } : {}),
|
|
65
|
+
});
|
|
66
|
+
if (this.requiresPkce) {
|
|
67
|
+
if (!secret) {
|
|
68
|
+
throw new Error(`[transit] ${this.provider} requires PKCE: pass the code verifier from createCodeVerifier() to redirectUrl(), store it with the state, and hand it back to callback().`);
|
|
69
|
+
}
|
|
70
|
+
params.set("code_challenge", challengeFor(secret));
|
|
71
|
+
params.set("code_challenge_method", "S256");
|
|
72
|
+
}
|
|
73
|
+
return `${this.authorizeUrl}?${params}`;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Where to send the user, plus what has to be kept until they come back.
|
|
77
|
+
* The state is minted here when the caller has no reason to choose one,
|
|
78
|
+
* and a provider requiring PKCE gets its verifier without the caller
|
|
79
|
+
* having to know it needed one.
|
|
80
|
+
*/
|
|
81
|
+
async begin(state = randomUUID()) {
|
|
82
|
+
if (!this.requiresPkce)
|
|
83
|
+
return { url: this.redirectUrl(state), state };
|
|
84
|
+
const secret = createCodeVerifier();
|
|
85
|
+
return { url: this.redirectUrl(state, secret), state, secret };
|
|
86
|
+
}
|
|
87
|
+
async callback(code, state, expectedState, secret) {
|
|
88
|
+
assertOAuthState(state, expectedState);
|
|
89
|
+
if (this.requiresPkce && !secret) {
|
|
90
|
+
throw new Error(`[transit] ${this.provider} requires PKCE: pass back the code verifier you stored at redirect time.`);
|
|
91
|
+
}
|
|
92
|
+
const tokens = await this.exchange(code, secret);
|
|
93
|
+
const accessToken = String(tokens.access_token);
|
|
94
|
+
const user = await this.fetchUser(accessToken);
|
|
95
|
+
return { user, token: readToken(tokens, accessToken) };
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Read the profile behind a token you already hold, with no code to
|
|
99
|
+
* exchange — a refreshed token, or one a mobile client obtained itself.
|
|
100
|
+
*/
|
|
101
|
+
async userFromToken(accessToken) {
|
|
102
|
+
return this.fetchUser(accessToken);
|
|
103
|
+
}
|
|
104
|
+
/** Trade the authorization code for an access token. */
|
|
105
|
+
async exchange(code, secret) {
|
|
106
|
+
const body = new URLSearchParams({
|
|
107
|
+
code,
|
|
108
|
+
redirect_uri: this.config.callbackUrl,
|
|
109
|
+
grant_type: "authorization_code",
|
|
110
|
+
...(secret ? { code_verifier: secret } : {}),
|
|
111
|
+
});
|
|
112
|
+
const headers = {
|
|
113
|
+
Accept: "application/json",
|
|
114
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
|
115
|
+
};
|
|
116
|
+
if (this.tokenAuth === "basic") {
|
|
117
|
+
const credentials = Buffer.from(`${this.config.clientId}:${this.config.clientSecret}`).toString("base64");
|
|
118
|
+
headers.Authorization = `Basic ${credentials}`;
|
|
119
|
+
// Sending them twice is what makes some providers reject the request
|
|
120
|
+
// outright, so the body carries them only in the other mode.
|
|
121
|
+
body.set("client_id", this.config.clientId);
|
|
122
|
+
}
|
|
123
|
+
else {
|
|
124
|
+
body.set("client_id", this.config.clientId);
|
|
125
|
+
body.set("client_secret", this.config.clientSecret);
|
|
126
|
+
}
|
|
127
|
+
const response = await fetch(this.accessTokenUrl, {
|
|
128
|
+
method: "POST",
|
|
129
|
+
headers,
|
|
130
|
+
body,
|
|
131
|
+
});
|
|
132
|
+
if (!response.ok) {
|
|
133
|
+
throw new Error(`${this.provider} OAuth token exchange failed (HTTP ${response.status})`);
|
|
134
|
+
}
|
|
135
|
+
const tokens = (await response.json());
|
|
136
|
+
if (!tokens.access_token) {
|
|
137
|
+
throw new Error(`${this.provider} OAuth: no access_token in response`);
|
|
138
|
+
}
|
|
139
|
+
return tokens;
|
|
140
|
+
}
|
|
141
|
+
/** Read the profile. Providers needing a second call override this. */
|
|
142
|
+
async fetchUser(accessToken) {
|
|
143
|
+
const raw = await this.get(this.userInfoUrl, accessToken, {
|
|
144
|
+
...this.userInfoParams(),
|
|
145
|
+
});
|
|
146
|
+
return this.mapUser(raw);
|
|
147
|
+
}
|
|
148
|
+
/** An authenticated GET returning a JSON array — some endpoints answer one. */
|
|
149
|
+
async getList(url, accessToken, params = {}) {
|
|
150
|
+
const body = await this.#requestJson(url, accessToken, params);
|
|
151
|
+
return Array.isArray(body) ? body : [];
|
|
152
|
+
}
|
|
153
|
+
/** An authenticated GET returning the decoded JSON body. */
|
|
154
|
+
async get(url, accessToken, params = {}) {
|
|
155
|
+
const body = await this.#requestJson(url, accessToken, params);
|
|
156
|
+
return typeof body === "object" && body !== null && !Array.isArray(body)
|
|
157
|
+
? body
|
|
158
|
+
: {};
|
|
159
|
+
}
|
|
160
|
+
/** The authenticated GET itself, before anything is assumed of the body. */
|
|
161
|
+
async #requestJson(url, accessToken, params = {}) {
|
|
162
|
+
const query = new URLSearchParams(params).toString();
|
|
163
|
+
const response = await fetch(query ? `${url}?${query}` : url, {
|
|
164
|
+
headers: {
|
|
165
|
+
Authorization: `Bearer ${accessToken}`,
|
|
166
|
+
Accept: "application/json",
|
|
167
|
+
},
|
|
168
|
+
});
|
|
169
|
+
if (!response.ok) {
|
|
170
|
+
throw new Error(`${this.provider} profile request failed (HTTP ${response.status})`);
|
|
171
|
+
}
|
|
172
|
+
return await response.json();
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
/** The S256 challenge a provider compares the verifier against. */
|
|
176
|
+
export function challengeFor(secret) {
|
|
177
|
+
return createHash("sha256").update(secret).digest("base64url");
|
|
178
|
+
}
|
|
179
|
+
/** Narrow a provider's token payload to the fields Warden promises. */
|
|
180
|
+
function readToken(tokens, accessToken) {
|
|
181
|
+
return {
|
|
182
|
+
accessToken,
|
|
183
|
+
...(typeof tokens.refresh_token === "string"
|
|
184
|
+
? { refreshToken: tokens.refresh_token }
|
|
185
|
+
: {}),
|
|
186
|
+
...(typeof tokens.expires_in === "number"
|
|
187
|
+
? { expiresIn: tokens.expires_in }
|
|
188
|
+
: {}),
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
/** Read a string field, or `undefined` when it is absent or not a string. */
|
|
192
|
+
export function str(raw, key) {
|
|
193
|
+
const value = raw[key];
|
|
194
|
+
return typeof value === "string" && value !== "" ? value : undefined;
|
|
195
|
+
}
|
|
196
|
+
//# sourceMappingURL=Oauth2Driver.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Oauth2Driver.js","sourceRoot":"","sources":["../src/Oauth2Driver.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The providers an application signs people in through, keyed by the names its
|
|
3
|
+
* config chose.
|
|
4
|
+
*
|
|
5
|
+
* const { url, state, secret } = await transit.begin('google')
|
|
6
|
+
* const { user } = await transit.callback('google', code, state, expected, secret)
|
|
7
|
+
*
|
|
8
|
+
* The key is the caller's: two entries may speak to the same provider with
|
|
9
|
+
* different credentials, and `use(name)` asks for the key, not the protocol.
|
|
10
|
+
*/
|
|
11
|
+
import type { DirectoryDriver, OAuthToken, RedirectRequest, TransitDriver, TransitEntry, TransitUser } from "./types.js";
|
|
12
|
+
export declare class TransitManager {
|
|
13
|
+
#private;
|
|
14
|
+
/** The provider a browser is sent to, under `name`. */
|
|
15
|
+
use(name: string): TransitDriver;
|
|
16
|
+
/** The directory under `name`. */
|
|
17
|
+
directory(name: string): DirectoryDriver;
|
|
18
|
+
/**
|
|
19
|
+
* Verify a login against the directory under `name`.
|
|
20
|
+
*
|
|
21
|
+
* No redirect and no state: the application already holds the credentials,
|
|
22
|
+
* and the directory is the authority that says whether they are right.
|
|
23
|
+
*/
|
|
24
|
+
authenticate(name: string, username: string, password: string): Promise<TransitUser>;
|
|
25
|
+
register(name: string, driver: TransitEntry): void;
|
|
26
|
+
/**
|
|
27
|
+
* Where to send the user, plus what to keep until they come back.
|
|
28
|
+
*
|
|
29
|
+
* This is the path that works for every provider: it mints the state,
|
|
30
|
+
* fetches a request token where the protocol needs one, and returns the
|
|
31
|
+
* secret to store when there is one.
|
|
32
|
+
*/
|
|
33
|
+
begin(name: string, state?: string): Promise<RedirectRequest>;
|
|
34
|
+
/**
|
|
35
|
+
* Where to send the user, when the provider can say so offline. `secret`
|
|
36
|
+
* is only read by providers that require PKCE — mint one with
|
|
37
|
+
* `createCodeVerifier()` and store it beside the state.
|
|
38
|
+
*
|
|
39
|
+
* An OAuth1 provider cannot answer here; call {@link begin} instead.
|
|
40
|
+
*/
|
|
41
|
+
redirect(name: string, state?: string, secret?: string): string;
|
|
42
|
+
/**
|
|
43
|
+
* Handle the OAuth callback. Pass `state` (from the query string) and
|
|
44
|
+
* `expectedState` (from the session, stored at redirect time) for CSRF
|
|
45
|
+
* protection. Omitting `expectedState` logs a security warning.
|
|
46
|
+
*/
|
|
47
|
+
callback(name: string, code: string, state?: string, expectedState?: string, secret?: string): Promise<{
|
|
48
|
+
user: TransitUser;
|
|
49
|
+
token: OAuthToken;
|
|
50
|
+
}>;
|
|
51
|
+
/**
|
|
52
|
+
* Read the profile behind a token already held, through the named provider.
|
|
53
|
+
* Throws when that driver has no way to — rather than answering with a
|
|
54
|
+
* profile it did not fetch.
|
|
55
|
+
*/
|
|
56
|
+
userFromToken(name: string, accessToken: string, tokenSecret?: string): Promise<TransitUser>;
|
|
57
|
+
get registeredDrivers(): string[];
|
|
58
|
+
}
|
|
59
|
+
//# sourceMappingURL=TransitManager.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TransitManager.d.ts","sourceRoot":"","sources":["../src/TransitManager.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EACX,eAAe,EACf,UAAU,EACV,eAAe,EACf,aAAa,EACb,YAAY,EACZ,WAAW,EACX,MAAM,YAAY,CAAC;AAGpB,qBAAa,cAAc;;IAG1B,uDAAuD;IACvD,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,aAAa;IAUhC,kCAAkC;IAClC,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,eAAe;IAUxC;;;;;OAKG;IACG,YAAY,CACjB,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,GACd,OAAO,CAAC,WAAW,CAAC;IAavB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,YAAY,GAAG,IAAI;IAIlD;;;;;;OAMG;IACG,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC;IAUnE;;;;;;OAMG;IACH,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM;IAI/D;;;;OAIG;IACG,QAAQ,CACb,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,KAAK,CAAC,EAAE,MAAM,EACd,aAAa,CAAC,EAAE,MAAM,EACtB,MAAM,CAAC,EAAE,MAAM,GACb,OAAO,CAAC;QAAE,IAAI,EAAE,WAAW,CAAC;QAAC,KAAK,EAAE,UAAU,CAAA;KAAE,CAAC;IAUpD;;;;OAIG;IACG,aAAa,CAClB,IAAI,EAAE,MAAM,EACZ,WAAW,EAAE,MAAM,EACnB,WAAW,CAAC,EAAE,MAAM,GAClB,OAAO,CAAC,WAAW,CAAC;IAUvB,IAAI,iBAAiB,IAAI,MAAM,EAAE,CAEhC;CACD"}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The providers an application signs people in through, keyed by the names its
|
|
3
|
+
* config chose.
|
|
4
|
+
*
|
|
5
|
+
* const { url, state, secret } = await transit.begin('google')
|
|
6
|
+
* const { user } = await transit.callback('google', code, state, expected, secret)
|
|
7
|
+
*
|
|
8
|
+
* The key is the caller's: two entries may speak to the same provider with
|
|
9
|
+
* different credentials, and `use(name)` asks for the key, not the protocol.
|
|
10
|
+
*/
|
|
11
|
+
import { isDirectory } from "./types.js";
|
|
12
|
+
export class TransitManager {
|
|
13
|
+
#drivers = new Map();
|
|
14
|
+
/** The provider a browser is sent to, under `name`. */
|
|
15
|
+
use(name) {
|
|
16
|
+
const entry = this.#entry(name);
|
|
17
|
+
if (isDirectory(entry)) {
|
|
18
|
+
throw new Error(`[transit] '${name}' is a directory: it verifies credentials rather than redirecting. Use authenticate('${name}', …).`);
|
|
19
|
+
}
|
|
20
|
+
return entry;
|
|
21
|
+
}
|
|
22
|
+
/** The directory under `name`. */
|
|
23
|
+
directory(name) {
|
|
24
|
+
const entry = this.#entry(name);
|
|
25
|
+
if (!isDirectory(entry)) {
|
|
26
|
+
throw new Error(`[transit] '${name}' is a redirect provider, not a directory. Use begin('${name}').`);
|
|
27
|
+
}
|
|
28
|
+
return entry;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Verify a login against the directory under `name`.
|
|
32
|
+
*
|
|
33
|
+
* No redirect and no state: the application already holds the credentials,
|
|
34
|
+
* and the directory is the authority that says whether they are right.
|
|
35
|
+
*/
|
|
36
|
+
async authenticate(name, username, password) {
|
|
37
|
+
return this.directory(name).authenticate(username, password);
|
|
38
|
+
}
|
|
39
|
+
#entry(name) {
|
|
40
|
+
const driver = this.#drivers.get(name);
|
|
41
|
+
if (!driver)
|
|
42
|
+
throw new Error(`OAuth driver '${name}' not registered. Available: ${[...this.#drivers.keys()].join(", ")}`);
|
|
43
|
+
return driver;
|
|
44
|
+
}
|
|
45
|
+
register(name, driver) {
|
|
46
|
+
this.#drivers.set(name, driver);
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Where to send the user, plus what to keep until they come back.
|
|
50
|
+
*
|
|
51
|
+
* This is the path that works for every provider: it mints the state,
|
|
52
|
+
* fetches a request token where the protocol needs one, and returns the
|
|
53
|
+
* secret to store when there is one.
|
|
54
|
+
*/
|
|
55
|
+
async begin(name, state) {
|
|
56
|
+
const driver = this.use(name);
|
|
57
|
+
if (typeof driver.begin !== "function") {
|
|
58
|
+
throw new Error(`[transit] The '${name}' driver does not support begin(); use redirect().`);
|
|
59
|
+
}
|
|
60
|
+
return driver.begin(state);
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Where to send the user, when the provider can say so offline. `secret`
|
|
64
|
+
* is only read by providers that require PKCE — mint one with
|
|
65
|
+
* `createCodeVerifier()` and store it beside the state.
|
|
66
|
+
*
|
|
67
|
+
* An OAuth1 provider cannot answer here; call {@link begin} instead.
|
|
68
|
+
*/
|
|
69
|
+
redirect(name, state, secret) {
|
|
70
|
+
return this.use(name).redirectUrl(state, secret);
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Handle the OAuth callback. Pass `state` (from the query string) and
|
|
74
|
+
* `expectedState` (from the session, stored at redirect time) for CSRF
|
|
75
|
+
* protection. Omitting `expectedState` logs a security warning.
|
|
76
|
+
*/
|
|
77
|
+
async callback(name, code, state, expectedState, secret) {
|
|
78
|
+
if (!expectedState) {
|
|
79
|
+
throw new Error(`[transit] OAuth callback for '${name}' requires expectedState for CSRF protection. ` +
|
|
80
|
+
`Store the state from redirect() in the session and pass it here.`);
|
|
81
|
+
}
|
|
82
|
+
return this.use(name).callback(code, state, expectedState, secret);
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Read the profile behind a token already held, through the named provider.
|
|
86
|
+
* Throws when that driver has no way to — rather than answering with a
|
|
87
|
+
* profile it did not fetch.
|
|
88
|
+
*/
|
|
89
|
+
async userFromToken(name, accessToken, tokenSecret) {
|
|
90
|
+
const driver = this.use(name);
|
|
91
|
+
if (typeof driver.userFromToken !== "function") {
|
|
92
|
+
throw new Error(`[transit] The '${name}' driver cannot read a profile from an existing token.`);
|
|
93
|
+
}
|
|
94
|
+
return driver.userFromToken(accessToken, tokenSecret);
|
|
95
|
+
}
|
|
96
|
+
get registeredDrivers() {
|
|
97
|
+
return [...this.#drivers.keys()];
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
//# sourceMappingURL=TransitManager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TransitManager.js","sourceRoot":"","sources":["../src/TransitManager.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Wires `config/transit.ts` into the container.
|
|
3
|
+
*
|
|
4
|
+
* Transit does not import `@c9up/ream`: the slice of the host it needs is
|
|
5
|
+
* duck-typed below, which is what keeps the package publishable on its own and
|
|
6
|
+
* usable from a host that is not Ream.
|
|
7
|
+
*/
|
|
8
|
+
interface TransitContainer {
|
|
9
|
+
singleton(token: unknown, factory: () => unknown): void;
|
|
10
|
+
resolve<T = unknown>(token: unknown): Promise<T>;
|
|
11
|
+
}
|
|
12
|
+
interface TransitConfigStore {
|
|
13
|
+
get<T = unknown>(key: string): T | undefined;
|
|
14
|
+
}
|
|
15
|
+
export interface TransitAppContext {
|
|
16
|
+
container: TransitContainer;
|
|
17
|
+
config: TransitConfigStore;
|
|
18
|
+
}
|
|
19
|
+
export default class TransitProvider {
|
|
20
|
+
protected app: TransitAppContext;
|
|
21
|
+
constructor(app: TransitAppContext);
|
|
22
|
+
register(): void;
|
|
23
|
+
boot(): Promise<void>;
|
|
24
|
+
}
|
|
25
|
+
export {};
|
|
26
|
+
//# sourceMappingURL=TransitProvider.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TransitProvider.d.ts","sourceRoot":"","sources":["../src/TransitProvider.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAMH,UAAU,gBAAgB;IACzB,SAAS,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,OAAO,GAAG,IAAI,CAAC;IACxD,OAAO,CAAC,CAAC,GAAG,OAAO,EAAE,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;CACjD;AAED,UAAU,kBAAkB;IAC3B,GAAG,CAAC,CAAC,GAAG,OAAO,EAAE,GAAG,EAAE,MAAM,GAAG,CAAC,GAAG,SAAS,CAAC;CAC7C;AAED,MAAM,WAAW,iBAAiB;IACjC,SAAS,EAAE,gBAAgB,CAAC;IAC5B,MAAM,EAAE,kBAAkB,CAAC;CAC3B;AAED,MAAM,CAAC,OAAO,OAAO,eAAe;IACvB,SAAS,CAAC,GAAG,EAAE,iBAAiB;gBAAtB,GAAG,EAAE,iBAAiB;IAE5C,QAAQ,IAAI,IAAI;IAgBV,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;CAK3B"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Wires `config/transit.ts` into the container.
|
|
3
|
+
*
|
|
4
|
+
* Transit does not import `@c9up/ream`: the slice of the host it needs is
|
|
5
|
+
* duck-typed below, which is what keeps the package publishable on its own and
|
|
6
|
+
* usable from a host that is not Ream.
|
|
7
|
+
*/
|
|
8
|
+
import { setTransit } from "./services/main.js";
|
|
9
|
+
import { TransitManager } from "./TransitManager.js";
|
|
10
|
+
export default class TransitProvider {
|
|
11
|
+
app;
|
|
12
|
+
constructor(app) {
|
|
13
|
+
this.app = app;
|
|
14
|
+
}
|
|
15
|
+
register() {
|
|
16
|
+
this.app.container.singleton(TransitManager, () => {
|
|
17
|
+
const config = this.app.config.get("transit") ?? {};
|
|
18
|
+
const manager = new TransitManager();
|
|
19
|
+
for (const [name, entry] of Object.entries(config)) {
|
|
20
|
+
manager.register(name, typeof entry === "function" ? entry() : entry);
|
|
21
|
+
}
|
|
22
|
+
return manager;
|
|
23
|
+
});
|
|
24
|
+
// String alias, so a consumer that cannot import Transit still resolves
|
|
25
|
+
// it — the same convention the other providers follow.
|
|
26
|
+
this.app.container.singleton("transit", () => this.app.container.resolve(TransitManager));
|
|
27
|
+
}
|
|
28
|
+
async boot() {
|
|
29
|
+
setTransit(await this.app.container.resolve(TransitManager));
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=TransitProvider.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TransitProvider.js","sourceRoot":"","sources":["../src/TransitProvider.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAChD,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAgBrD,MAAM,CAAC,OAAO,OAAO,eAAe;IACb;IAAtB,YAAsB,GAAsB;QAAtB,QAAG,GAAH,GAAG,CAAmB;IAAG,CAAC;IAEhD,QAAQ;QACP,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,SAAS,CAAC,cAAc,EAAE,GAAG,EAAE;YACjD,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,CAAgB,SAAS,CAAC,IAAI,EAAE,CAAC;YACnE,MAAM,OAAO,GAAG,IAAI,cAAc,EAAE,CAAC;YACrC,KAAK,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;gBACpD,OAAO,CAAC,QAAQ,CAAC,IAAI,EAAE,OAAO,KAAK,KAAK,UAAU,CAAC,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;YACvE,CAAC;YACD,OAAO,OAAO,CAAC;QAChB,CAAC,CAAC,CAAC;QACH,wEAAwE;QACxE,uDAAuD;QACvD,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,SAAS,CAAC,SAAS,EAAE,GAAG,EAAE,CAC5C,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,OAAO,CAAiB,cAAc,CAAC,CAC1D,CAAC;IACH,CAAC;IAED,KAAK,CAAC,IAAI;QACT,UAAU,CACT,MAAM,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,OAAO,CAAiB,cAAc,CAAC,CAChE,CAAC;IACH,CAAC;CACD"}
|
package/dist/apple.d.ts
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The two things Sign in with Apple does differently from every other OpenID
|
|
3
|
+
* Connect provider.
|
|
4
|
+
*
|
|
5
|
+
* Apple has no client secret. It has a private key, and expects a short-lived
|
|
6
|
+
* JWT minted from it on every token request — which is why `clientSecret` is
|
|
7
|
+
* allowed to be a function.
|
|
8
|
+
*
|
|
9
|
+
* And it sends the user's name exactly ONCE, in the body of the first consent
|
|
10
|
+
* callback, never in a token and never again. An application that does not
|
|
11
|
+
* store it then has no way to ask for it later.
|
|
12
|
+
*/
|
|
13
|
+
export interface AppleKey {
|
|
14
|
+
/** The Services ID (web) or App ID this signs for. */
|
|
15
|
+
clientId: string;
|
|
16
|
+
/** The ten-character Team ID from the developer account. */
|
|
17
|
+
teamId: string;
|
|
18
|
+
/** The Key ID of the .p8 this signs with. */
|
|
19
|
+
keyId: string;
|
|
20
|
+
/** The contents of the .p8 file, PEM as Apple issues it. */
|
|
21
|
+
privateKey: string;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Mint the JWT Apple takes in place of a client secret.
|
|
25
|
+
*
|
|
26
|
+
* Signed ES256 with the .p8, naming the team as issuer and the client as
|
|
27
|
+
* subject. Minted per request rather than cached: it costs one signature, and
|
|
28
|
+
* a long-lived one is a credential sitting in memory for no reason.
|
|
29
|
+
*/
|
|
30
|
+
export declare function appleClientSecret(key: AppleKey, now?: number): string;
|
|
31
|
+
/**
|
|
32
|
+
* Read the `user` field of Apple's first consent callback.
|
|
33
|
+
*
|
|
34
|
+
* It arrives as JSON in the POST body, on that one request only. Store what
|
|
35
|
+
* comes out — the id_token never carries a name, and a second sign-in by the
|
|
36
|
+
* same person will not carry one either.
|
|
37
|
+
*
|
|
38
|
+
* Returns `undefined` for anything unreadable rather than throwing: a returning
|
|
39
|
+
* user simply has no `user` field, and that is the normal case.
|
|
40
|
+
*/
|
|
41
|
+
export declare function parseAppleUser(value: unknown): {
|
|
42
|
+
name?: string;
|
|
43
|
+
email?: string;
|
|
44
|
+
} | undefined;
|
|
45
|
+
//# sourceMappingURL=apple.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"apple.d.ts","sourceRoot":"","sources":["../src/apple.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAIH,MAAM,WAAW,QAAQ;IACxB,sDAAsD;IACtD,QAAQ,EAAE,MAAM,CAAC;IACjB,4DAA4D;IAC5D,MAAM,EAAE,MAAM,CAAC;IACf,6CAA6C;IAC7C,KAAK,EAAE,MAAM,CAAC;IACd,4DAA4D;IAC5D,UAAU,EAAE,MAAM,CAAC;CACnB;AAKD;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAChC,GAAG,EAAE,QAAQ,EACb,GAAG,GAAE,MAAsC,GACzC,MAAM,CAkBR;AAED;;;;;;;;;GASG;AACH,wBAAgB,cAAc,CAC7B,KAAK,EAAE,OAAO,GACZ;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,SAAS,CAyB/C"}
|
package/dist/apple.js
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The two things Sign in with Apple does differently from every other OpenID
|
|
3
|
+
* Connect provider.
|
|
4
|
+
*
|
|
5
|
+
* Apple has no client secret. It has a private key, and expects a short-lived
|
|
6
|
+
* JWT minted from it on every token request — which is why `clientSecret` is
|
|
7
|
+
* allowed to be a function.
|
|
8
|
+
*
|
|
9
|
+
* And it sends the user's name exactly ONCE, in the body of the first consent
|
|
10
|
+
* callback, never in a token and never again. An application that does not
|
|
11
|
+
* store it then has no way to ask for it later.
|
|
12
|
+
*/
|
|
13
|
+
import { createPrivateKey, sign as signData } from "node:crypto";
|
|
14
|
+
/** Apple rejects a secret valid for more than six months; minutes are plenty. */
|
|
15
|
+
const LIFETIME_SECONDS = 300;
|
|
16
|
+
/**
|
|
17
|
+
* Mint the JWT Apple takes in place of a client secret.
|
|
18
|
+
*
|
|
19
|
+
* Signed ES256 with the .p8, naming the team as issuer and the client as
|
|
20
|
+
* subject. Minted per request rather than cached: it costs one signature, and
|
|
21
|
+
* a long-lived one is a credential sitting in memory for no reason.
|
|
22
|
+
*/
|
|
23
|
+
export function appleClientSecret(key, now = Math.floor(Date.now() / 1000)) {
|
|
24
|
+
const header = { alg: "ES256", kid: key.keyId, typ: "JWT" };
|
|
25
|
+
const claims = {
|
|
26
|
+
iss: key.teamId,
|
|
27
|
+
iat: now,
|
|
28
|
+
exp: now + LIFETIME_SECONDS,
|
|
29
|
+
aud: "https://appleid.apple.com",
|
|
30
|
+
sub: key.clientId,
|
|
31
|
+
};
|
|
32
|
+
const signingInput = `${b64(header)}.${b64(claims)}`;
|
|
33
|
+
// JOSE wants R concatenated with S; node emits DER unless told, and Apple
|
|
34
|
+
// rejects DER.
|
|
35
|
+
const signature = signData("sha256", Buffer.from(signingInput, "ascii"), {
|
|
36
|
+
key: createPrivateKey(key.privateKey),
|
|
37
|
+
dsaEncoding: "ieee-p1363",
|
|
38
|
+
});
|
|
39
|
+
return `${signingInput}.${signature.toString("base64url")}`;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Read the `user` field of Apple's first consent callback.
|
|
43
|
+
*
|
|
44
|
+
* It arrives as JSON in the POST body, on that one request only. Store what
|
|
45
|
+
* comes out — the id_token never carries a name, and a second sign-in by the
|
|
46
|
+
* same person will not carry one either.
|
|
47
|
+
*
|
|
48
|
+
* Returns `undefined` for anything unreadable rather than throwing: a returning
|
|
49
|
+
* user simply has no `user` field, and that is the normal case.
|
|
50
|
+
*/
|
|
51
|
+
export function parseAppleUser(value) {
|
|
52
|
+
if (typeof value !== "string" || value === "")
|
|
53
|
+
return undefined;
|
|
54
|
+
let parsed;
|
|
55
|
+
try {
|
|
56
|
+
parsed = JSON.parse(value);
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
return undefined;
|
|
60
|
+
}
|
|
61
|
+
if (typeof parsed !== "object" || parsed === null)
|
|
62
|
+
return undefined;
|
|
63
|
+
const email = Reflect.get(parsed, "email");
|
|
64
|
+
const rawName = Reflect.get(parsed, "name");
|
|
65
|
+
const name = typeof rawName === "object" && rawName !== null
|
|
66
|
+
? [Reflect.get(rawName, "firstName"), Reflect.get(rawName, "lastName")]
|
|
67
|
+
.filter((part) => typeof part === "string" && part !== "")
|
|
68
|
+
.join(" ")
|
|
69
|
+
: undefined;
|
|
70
|
+
const out = {};
|
|
71
|
+
if (name)
|
|
72
|
+
out.name = name;
|
|
73
|
+
if (typeof email === "string" && email !== "")
|
|
74
|
+
out.email = email;
|
|
75
|
+
return Object.keys(out).length > 0 ? out : undefined;
|
|
76
|
+
}
|
|
77
|
+
function b64(value) {
|
|
78
|
+
return Buffer.from(JSON.stringify(value)).toString("base64url");
|
|
79
|
+
}
|
|
80
|
+
//# sourceMappingURL=apple.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"apple.js","sourceRoot":"","sources":["../src/apple.ts"],"names":[],"mappings":"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"}
|
package/dist/ber.d.ts
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The encoding LDAP speaks: BER, the subset of it LDAP actually uses.
|
|
3
|
+
*
|
|
4
|
+
* This exists so that nothing an application passes ever becomes part of a
|
|
5
|
+
* string that a server then parses. A search filter here is a structure, not
|
|
6
|
+
* text — its values are written as length-prefixed octets, so a username
|
|
7
|
+
* containing `)(uid=*` is a username containing those characters and cannot
|
|
8
|
+
* become syntax. LDAP injection is not escaped away here; it is unrepresentable.
|
|
9
|
+
*/
|
|
10
|
+
/** Universal tags, and the two LDAP builds everything else from. */
|
|
11
|
+
export declare const BOOLEAN = 1;
|
|
12
|
+
export declare const INTEGER = 2;
|
|
13
|
+
export declare const OCTET_STRING = 4;
|
|
14
|
+
export declare const ENUMERATED = 10;
|
|
15
|
+
export declare const SEQUENCE = 48;
|
|
16
|
+
/** `[APPLICATION n]`, constructed. */
|
|
17
|
+
export declare const application: (n: number) => number;
|
|
18
|
+
/** `[APPLICATION n]`, primitive — which a couple of LDAP operations are. */
|
|
19
|
+
export declare const applicationPrimitive: (n: number) => number;
|
|
20
|
+
/** `[n]` context-specific, constructed. */
|
|
21
|
+
export declare const context: (n: number) => number;
|
|
22
|
+
/** `[n]` context-specific, primitive. */
|
|
23
|
+
export declare const contextPrimitive: (n: number) => number;
|
|
24
|
+
export declare class BerError extends Error {
|
|
25
|
+
constructor(message: string);
|
|
26
|
+
}
|
|
27
|
+
/** One tag-length-value element. */
|
|
28
|
+
export declare function encode(tag: number, value: Buffer): Buffer;
|
|
29
|
+
export declare function encodeInteger(value: number, tag?: number): Buffer;
|
|
30
|
+
export declare function encodeString(value: string, tag?: number): Buffer;
|
|
31
|
+
export declare function encodeBoolean(value: boolean, tag?: number): Buffer;
|
|
32
|
+
export declare function encodeSequence(parts: Buffer[], tag?: number): Buffer;
|
|
33
|
+
/** A decoded element: its tag, its contents, and where it ended. */
|
|
34
|
+
export interface BerElement {
|
|
35
|
+
tag: number;
|
|
36
|
+
value: Buffer;
|
|
37
|
+
end: number;
|
|
38
|
+
}
|
|
39
|
+
/** Read one element at `offset`. */
|
|
40
|
+
export declare function decode(buffer: Buffer, offset?: number): BerElement;
|
|
41
|
+
/** Every element inside a constructed one. */
|
|
42
|
+
export declare function decodeSequence(value: Buffer): BerElement[];
|
|
43
|
+
export declare function decodeInteger(value: Buffer): number;
|
|
44
|
+
export declare function decodeString(value: Buffer): string;
|
|
45
|
+
/**
|
|
46
|
+
* How many bytes a whole message occupies, or `undefined` while it is still
|
|
47
|
+
* arriving. LDAP runs over a stream, so a read can stop anywhere.
|
|
48
|
+
*/
|
|
49
|
+
export declare function messageLength(buffer: Buffer): number | undefined;
|
|
50
|
+
//# sourceMappingURL=ber.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ber.d.ts","sourceRoot":"","sources":["../src/ber.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,oEAAoE;AACpE,eAAO,MAAM,OAAO,IAAO,CAAC;AAC5B,eAAO,MAAM,OAAO,IAAO,CAAC;AAC5B,eAAO,MAAM,YAAY,IAAO,CAAC;AACjC,eAAO,MAAM,UAAU,KAAO,CAAC;AAC/B,eAAO,MAAM,QAAQ,KAAO,CAAC;AAE7B,sCAAsC;AACtC,eAAO,MAAM,WAAW,GAAI,GAAG,MAAM,KAAG,MAAkB,CAAC;AAC3D,4EAA4E;AAC5E,eAAO,MAAM,oBAAoB,GAAI,GAAG,MAAM,KAAG,MAAkB,CAAC;AACpE,2CAA2C;AAC3C,eAAO,MAAM,OAAO,GAAI,GAAG,MAAM,KAAG,MAAkB,CAAC;AACvD,yCAAyC;AACzC,eAAO,MAAM,gBAAgB,GAAI,GAAG,MAAM,KAAG,MAAkB,CAAC;AAEhE,qBAAa,QAAS,SAAQ,KAAK;gBACtB,OAAO,EAAE,MAAM;CAI3B;AAED,oCAAoC;AACpC,wBAAgB,MAAM,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAEzD;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,SAAU,GAAG,MAAM,CAclE;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,SAAe,GAAG,MAAM,CAItE;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,EAAE,GAAG,SAAU,GAAG,MAAM,CAEnE;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,GAAG,SAAW,GAAG,MAAM,CAEtE;AAED,oEAAoE;AACpE,MAAM,WAAW,UAAU;IAC1B,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;CACZ;AAED,oCAAoC;AACpC,wBAAgB,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,SAAI,GAAG,UAAU,CAa7D;AAED,8CAA8C;AAC9C,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,UAAU,EAAE,CAS1D;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CASnD;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAElD;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAchE"}
|