@caido/server-auth 0.1.0 → 0.2.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 +51 -0
- package/dist/index.cjs +179 -122
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +62 -71
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +62 -71
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +177 -118
- package/dist/index.mjs.map +1 -1
- package/package.json +6 -5
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Client, fetchExchange } from "@urql/core";
|
|
2
|
+
import { print } from "graphql";
|
|
2
3
|
import { createClient } from "graphql-ws";
|
|
3
4
|
import { gql } from "graphql-tag";
|
|
4
5
|
|
|
@@ -13,51 +14,42 @@ var AuthenticationError = class extends Error {
|
|
|
13
14
|
}
|
|
14
15
|
};
|
|
15
16
|
/**
|
|
16
|
-
* Error thrown
|
|
17
|
+
* Error thrown for errors coming from the Caido cloud API.
|
|
18
|
+
* Used for device approval and device information operations.
|
|
17
19
|
*/
|
|
18
|
-
var
|
|
19
|
-
/** Error code from the API */
|
|
20
|
-
code;
|
|
21
|
-
constructor(code, message) {
|
|
22
|
-
super(`${code}: ${message}`);
|
|
23
|
-
this.name = "AuthenticationFlowError";
|
|
24
|
-
this.code = code;
|
|
25
|
-
}
|
|
26
|
-
};
|
|
27
|
-
/**
|
|
28
|
-
* Error thrown when token refresh fails.
|
|
29
|
-
*/
|
|
30
|
-
var TokenRefreshError = class extends AuthenticationError {
|
|
31
|
-
/** Error code from the API */
|
|
32
|
-
code;
|
|
33
|
-
constructor(code, message) {
|
|
34
|
-
super(`${code}: ${message}`);
|
|
35
|
-
this.name = "TokenRefreshError";
|
|
36
|
-
this.code = code;
|
|
37
|
-
}
|
|
38
|
-
};
|
|
39
|
-
/**
|
|
40
|
-
* Error thrown when device approval fails.
|
|
41
|
-
*/
|
|
42
|
-
var DeviceApprovalError = class extends AuthenticationError {
|
|
20
|
+
var CloudError = class extends AuthenticationError {
|
|
43
21
|
/** HTTP status code if available */
|
|
44
22
|
statusCode;
|
|
45
|
-
|
|
23
|
+
/** Error code from the API if available */
|
|
24
|
+
code;
|
|
25
|
+
/** Reason for the error if available */
|
|
26
|
+
reason;
|
|
27
|
+
constructor(message, options) {
|
|
46
28
|
super(message);
|
|
47
|
-
this.name = "
|
|
48
|
-
this.statusCode = statusCode;
|
|
29
|
+
this.name = "CloudError";
|
|
30
|
+
this.statusCode = options?.statusCode;
|
|
31
|
+
this.code = options?.code;
|
|
32
|
+
this.reason = options?.reason;
|
|
49
33
|
}
|
|
50
34
|
};
|
|
51
35
|
/**
|
|
52
|
-
* Error thrown
|
|
36
|
+
* Error thrown for errors coming from the Caido instance.
|
|
37
|
+
* Used for authentication flow and token refresh operations.
|
|
53
38
|
*/
|
|
54
|
-
var
|
|
55
|
-
/**
|
|
56
|
-
|
|
57
|
-
|
|
39
|
+
var InstanceError = class extends AuthenticationError {
|
|
40
|
+
/** Error code from the API */
|
|
41
|
+
code;
|
|
42
|
+
/** Reason for the error if available */
|
|
43
|
+
reason;
|
|
44
|
+
/** Error message if available */
|
|
45
|
+
errorMessage;
|
|
46
|
+
constructor(code, options) {
|
|
47
|
+
const message = options?.reason ?? options?.message ?? code;
|
|
58
48
|
super(message);
|
|
59
|
-
this.name = "
|
|
60
|
-
this.
|
|
49
|
+
this.name = "InstanceError";
|
|
50
|
+
this.code = code;
|
|
51
|
+
this.reason = options?.reason;
|
|
52
|
+
this.errorMessage = options?.message;
|
|
61
53
|
}
|
|
62
54
|
};
|
|
63
55
|
|
|
@@ -73,8 +65,21 @@ const START_AUTHENTICATION_FLOW = gql`
|
|
|
73
65
|
expiresAt
|
|
74
66
|
}
|
|
75
67
|
error {
|
|
76
|
-
|
|
77
|
-
|
|
68
|
+
... on AuthenticationUserError {
|
|
69
|
+
code
|
|
70
|
+
reason
|
|
71
|
+
}
|
|
72
|
+
... on CloudUserError {
|
|
73
|
+
code
|
|
74
|
+
reason
|
|
75
|
+
}
|
|
76
|
+
... on InternalUserError {
|
|
77
|
+
code
|
|
78
|
+
message
|
|
79
|
+
}
|
|
80
|
+
... on OtherUserError {
|
|
81
|
+
code
|
|
82
|
+
}
|
|
78
83
|
}
|
|
79
84
|
}
|
|
80
85
|
}
|
|
@@ -84,27 +89,51 @@ const CREATED_AUTHENTICATION_TOKEN = gql`
|
|
|
84
89
|
createdAuthenticationToken(requestId: $requestId) {
|
|
85
90
|
token {
|
|
86
91
|
accessToken
|
|
87
|
-
refreshToken
|
|
88
92
|
expiresAt
|
|
93
|
+
refreshToken
|
|
94
|
+
scopes
|
|
89
95
|
}
|
|
90
96
|
error {
|
|
91
|
-
|
|
92
|
-
|
|
97
|
+
... on AuthenticationUserError {
|
|
98
|
+
code
|
|
99
|
+
reason
|
|
100
|
+
}
|
|
101
|
+
... on InternalUserError {
|
|
102
|
+
code
|
|
103
|
+
message
|
|
104
|
+
}
|
|
105
|
+
... on OtherUserError {
|
|
106
|
+
code
|
|
107
|
+
}
|
|
93
108
|
}
|
|
94
109
|
}
|
|
95
110
|
}
|
|
96
111
|
`;
|
|
97
112
|
const REFRESH_AUTHENTICATION_TOKEN = gql`
|
|
98
|
-
mutation RefreshAuthenticationToken($refreshToken:
|
|
113
|
+
mutation RefreshAuthenticationToken($refreshToken: Token!) {
|
|
99
114
|
refreshAuthenticationToken(refreshToken: $refreshToken) {
|
|
100
115
|
token {
|
|
101
116
|
accessToken
|
|
102
|
-
refreshToken
|
|
103
117
|
expiresAt
|
|
118
|
+
refreshToken
|
|
119
|
+
scopes
|
|
104
120
|
}
|
|
105
121
|
error {
|
|
106
|
-
|
|
107
|
-
|
|
122
|
+
... on AuthenticationUserError {
|
|
123
|
+
code
|
|
124
|
+
reason
|
|
125
|
+
}
|
|
126
|
+
... on CloudUserError {
|
|
127
|
+
code
|
|
128
|
+
reason
|
|
129
|
+
}
|
|
130
|
+
... on InternalUserError {
|
|
131
|
+
code
|
|
132
|
+
message
|
|
133
|
+
}
|
|
134
|
+
... on OtherUserError {
|
|
135
|
+
code
|
|
136
|
+
}
|
|
108
137
|
}
|
|
109
138
|
}
|
|
110
139
|
}
|
|
@@ -117,48 +146,54 @@ const REFRESH_AUTHENTICATION_TOKEN = gql`
|
|
|
117
146
|
*
|
|
118
147
|
* @example
|
|
119
148
|
* ```typescript
|
|
120
|
-
* import {
|
|
149
|
+
* import { AuthClient, BrowserApprover } from "@caido/auth";
|
|
121
150
|
*
|
|
122
|
-
* const auth = new
|
|
123
|
-
* "http://localhost:8080",
|
|
124
|
-
* new BrowserApprover((request) => {
|
|
151
|
+
* const auth = new AuthClient({
|
|
152
|
+
* instanceUrl: "http://localhost:8080",
|
|
153
|
+
* approver: new BrowserApprover((request) => {
|
|
125
154
|
* console.log(`Visit ${request.verificationUrl}`);
|
|
126
155
|
* })
|
|
127
|
-
* );
|
|
156
|
+
* });
|
|
128
157
|
*
|
|
129
158
|
* const token = await auth.startAuthenticationFlow();
|
|
130
159
|
* console.log("Access token:", token.accessToken);
|
|
131
160
|
* ```
|
|
132
161
|
*/
|
|
133
|
-
var
|
|
162
|
+
var AuthClient = class {
|
|
134
163
|
instanceUrl;
|
|
135
164
|
graphqlUrl;
|
|
136
165
|
websocketUrl;
|
|
137
166
|
approver;
|
|
138
167
|
client;
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
* @param approver - The approver to use for the authentication flow
|
|
144
|
-
*/
|
|
145
|
-
constructor(instanceUrl, approver) {
|
|
146
|
-
this.instanceUrl = instanceUrl.replace(/\/$/, "");
|
|
168
|
+
fetchFn;
|
|
169
|
+
timeout;
|
|
170
|
+
constructor(options) {
|
|
171
|
+
this.instanceUrl = options.instanceUrl.replace(/\/$/, "");
|
|
147
172
|
this.graphqlUrl = `${this.instanceUrl}/graphql`;
|
|
148
173
|
this.websocketUrl = this.getWebsocketUrl();
|
|
149
|
-
this.approver = approver;
|
|
174
|
+
this.approver = options.approver;
|
|
175
|
+
this.fetchFn = options.fetch;
|
|
176
|
+
this.timeout = options.timeout;
|
|
150
177
|
this.client = new Client({
|
|
151
178
|
url: this.graphqlUrl,
|
|
152
|
-
exchanges: [fetchExchange]
|
|
179
|
+
exchanges: [fetchExchange],
|
|
180
|
+
fetchOptions: () => {
|
|
181
|
+
const fetchOptions = {};
|
|
182
|
+
if (this.timeout !== void 0) fetchOptions.signal = AbortSignal.timeout(this.timeout);
|
|
183
|
+
return fetchOptions;
|
|
184
|
+
},
|
|
185
|
+
fetch: this.fetchFn
|
|
153
186
|
});
|
|
154
187
|
}
|
|
155
|
-
/**
|
|
156
|
-
* Convert HTTP(S) URL to WS(S) URL for subscriptions.
|
|
157
|
-
*/
|
|
158
188
|
getWebsocketUrl() {
|
|
159
189
|
const url = new URL(this.graphqlUrl);
|
|
160
190
|
return `${url.protocol === "https:" ? "wss:" : "ws:"}//${url.host}/ws/graphql`;
|
|
161
191
|
}
|
|
192
|
+
extractErrorDetails(error) {
|
|
193
|
+
if ("reason" in error) return { reason: error.reason };
|
|
194
|
+
if ("message" in error) return { message: error.message };
|
|
195
|
+
return {};
|
|
196
|
+
}
|
|
162
197
|
/**
|
|
163
198
|
* Start the device code authentication flow.
|
|
164
199
|
*
|
|
@@ -169,16 +204,19 @@ var CaidoAuth = class {
|
|
|
169
204
|
* 4. Returns the authentication token once approved
|
|
170
205
|
*
|
|
171
206
|
* @returns The authentication token
|
|
172
|
-
* @throws {
|
|
207
|
+
* @throws {InstanceError} If the flow fails to start
|
|
173
208
|
* @throws {AuthenticationError} If token retrieval fails
|
|
174
209
|
*/
|
|
175
210
|
async startAuthenticationFlow() {
|
|
176
211
|
const result = await this.client.mutation(START_AUTHENTICATION_FLOW, {}).toPromise();
|
|
177
|
-
if (result.error) throw new
|
|
212
|
+
if (result.error) throw new InstanceError("GRAPHQL_ERROR", { message: result.error.message });
|
|
178
213
|
const payload = result.data?.startAuthenticationFlow;
|
|
179
|
-
if (!payload) throw new
|
|
180
|
-
if (payload.error)
|
|
181
|
-
|
|
214
|
+
if (!payload) throw new InstanceError("NO_RESPONSE", { message: "No response from startAuthenticationFlow" });
|
|
215
|
+
if (payload.error) {
|
|
216
|
+
const details = this.extractErrorDetails(payload.error);
|
|
217
|
+
throw new InstanceError(payload.error.code, details);
|
|
218
|
+
}
|
|
219
|
+
if (!payload.request) throw new InstanceError("NO_REQUEST", { message: "No authentication request returned" });
|
|
182
220
|
const authRequest = {
|
|
183
221
|
id: payload.request.id,
|
|
184
222
|
userCode: payload.request.userCode,
|
|
@@ -188,23 +226,11 @@ var CaidoAuth = class {
|
|
|
188
226
|
await this.approver.approve(authRequest);
|
|
189
227
|
return await this.waitForToken(authRequest.id);
|
|
190
228
|
}
|
|
191
|
-
/**
|
|
192
|
-
* Subscribe and wait for the authentication token.
|
|
193
|
-
*
|
|
194
|
-
* @param requestId - The authentication request ID
|
|
195
|
-
* @returns The authentication token once the user authorizes
|
|
196
|
-
* @throws {AuthenticationError} If subscription fails or returns an error
|
|
197
|
-
*/
|
|
198
229
|
async waitForToken(requestId) {
|
|
199
230
|
return new Promise((resolve, reject) => {
|
|
200
231
|
const wsClient = createClient({ url: this.websocketUrl });
|
|
201
232
|
const unsubscribe = wsClient.subscribe({
|
|
202
|
-
query: CREATED_AUTHENTICATION_TOKEN
|
|
203
|
-
createdAuthenticationToken(requestId: $requestId) {
|
|
204
|
-
token { accessToken refreshToken expiresAt }
|
|
205
|
-
error { code message }
|
|
206
|
-
}
|
|
207
|
-
}`,
|
|
233
|
+
query: print(CREATED_AUTHENTICATION_TOKEN),
|
|
208
234
|
variables: { requestId }
|
|
209
235
|
}, {
|
|
210
236
|
next: (result) => {
|
|
@@ -212,7 +238,8 @@ var CaidoAuth = class {
|
|
|
212
238
|
if (payload?.error) {
|
|
213
239
|
unsubscribe();
|
|
214
240
|
wsClient.dispose();
|
|
215
|
-
|
|
241
|
+
const details = this.extractErrorDetails(payload.error);
|
|
242
|
+
reject(new InstanceError(payload.error.code, details));
|
|
216
243
|
return;
|
|
217
244
|
}
|
|
218
245
|
if (payload?.token) {
|
|
@@ -221,17 +248,18 @@ var CaidoAuth = class {
|
|
|
221
248
|
resolve({
|
|
222
249
|
accessToken: payload.token.accessToken,
|
|
223
250
|
refreshToken: payload.token.refreshToken,
|
|
224
|
-
expiresAt: new Date(payload.token.expiresAt)
|
|
251
|
+
expiresAt: new Date(payload.token.expiresAt),
|
|
252
|
+
scopes: payload.token.scopes
|
|
225
253
|
});
|
|
226
254
|
}
|
|
227
255
|
},
|
|
228
256
|
error: (error) => {
|
|
229
257
|
wsClient.dispose();
|
|
230
|
-
reject(new
|
|
258
|
+
reject(new InstanceError("SUBSCRIPTION_ERROR", { message: error instanceof Error ? error.message : String(error) }));
|
|
231
259
|
},
|
|
232
260
|
complete: () => {
|
|
233
261
|
wsClient.dispose();
|
|
234
|
-
reject(new
|
|
262
|
+
reject(new InstanceError("SUBSCRIPTION_COMPLETE", { message: "Subscription ended without receiving token" }));
|
|
235
263
|
}
|
|
236
264
|
});
|
|
237
265
|
});
|
|
@@ -241,19 +269,23 @@ var CaidoAuth = class {
|
|
|
241
269
|
*
|
|
242
270
|
* @param refreshToken - The refresh token from a previous authentication
|
|
243
271
|
* @returns New authentication token with updated access and refresh tokens
|
|
244
|
-
* @throws {
|
|
272
|
+
* @throws {InstanceError} If the refresh fails
|
|
245
273
|
*/
|
|
246
274
|
async refreshToken(refreshToken) {
|
|
247
275
|
const result = await this.client.mutation(REFRESH_AUTHENTICATION_TOKEN, { refreshToken }).toPromise();
|
|
248
|
-
if (result.error) throw new
|
|
276
|
+
if (result.error) throw new InstanceError("GRAPHQL_ERROR", { message: result.error.message });
|
|
249
277
|
const payload = result.data?.refreshAuthenticationToken;
|
|
250
|
-
if (!payload) throw new
|
|
251
|
-
if (payload.error)
|
|
252
|
-
|
|
278
|
+
if (!payload) throw new InstanceError("NO_RESPONSE", { message: "No response from refreshAuthenticationToken" });
|
|
279
|
+
if (payload.error) {
|
|
280
|
+
const details = this.extractErrorDetails(payload.error);
|
|
281
|
+
throw new InstanceError(payload.error.code, details);
|
|
282
|
+
}
|
|
283
|
+
if (!payload.token) throw new InstanceError("NO_TOKEN", { message: "No token returned from refresh" });
|
|
253
284
|
return {
|
|
254
285
|
accessToken: payload.token.accessToken,
|
|
255
286
|
refreshToken: payload.token.refreshToken,
|
|
256
|
-
expiresAt: new Date(payload.token.expiresAt)
|
|
287
|
+
expiresAt: new Date(payload.token.expiresAt),
|
|
288
|
+
scopes: payload.token.scopes
|
|
257
289
|
};
|
|
258
290
|
}
|
|
259
291
|
};
|
|
@@ -316,15 +348,14 @@ var PATApprover = class {
|
|
|
316
348
|
pat;
|
|
317
349
|
allowedScopes;
|
|
318
350
|
apiUrl;
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
*
|
|
322
|
-
* @param options - Configuration options for the approver
|
|
323
|
-
*/
|
|
351
|
+
fetchFn;
|
|
352
|
+
timeout;
|
|
324
353
|
constructor(options) {
|
|
325
354
|
this.pat = options.pat;
|
|
326
355
|
this.allowedScopes = options.allowedScopes;
|
|
327
356
|
this.apiUrl = (options.apiUrl ?? DEFAULT_API_URL).replace(/\/$/, "");
|
|
357
|
+
this.fetchFn = options.fetch ?? globalThis.fetch;
|
|
358
|
+
this.timeout = options.timeout;
|
|
328
359
|
}
|
|
329
360
|
/**
|
|
330
361
|
* Approve the authentication request using the PAT.
|
|
@@ -333,50 +364,71 @@ var PATApprover = class {
|
|
|
333
364
|
* and finally approves the device.
|
|
334
365
|
*
|
|
335
366
|
* @param request - The authentication request
|
|
336
|
-
* @throws {
|
|
337
|
-
* @throws {DeviceApprovalError} If approving the device fails
|
|
367
|
+
* @throws {CloudError} If fetching device information or approving the device fails
|
|
338
368
|
*/
|
|
339
369
|
async approve(request) {
|
|
340
370
|
let scopesToApprove = (await this.getDeviceInformation(request.userCode)).scopes.map((s) => s.name);
|
|
341
371
|
if (this.allowedScopes) scopesToApprove = scopesToApprove.filter((scope) => this.allowedScopes.includes(scope));
|
|
342
372
|
await this.approveDevice(request.userCode, scopesToApprove);
|
|
343
373
|
}
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
374
|
+
async sendRequest(url, options) {
|
|
375
|
+
const fetchOptions = {
|
|
376
|
+
method: options.method,
|
|
377
|
+
headers: options.headers
|
|
378
|
+
};
|
|
379
|
+
if (this.timeout !== void 0) fetchOptions.signal = AbortSignal.timeout(this.timeout);
|
|
380
|
+
return this.fetchFn(url, fetchOptions);
|
|
381
|
+
}
|
|
382
|
+
async parseOAuth2Error(response) {
|
|
383
|
+
let errorText;
|
|
384
|
+
let errorCode;
|
|
385
|
+
let errorDescription;
|
|
386
|
+
try {
|
|
387
|
+
const errorData = await response.json();
|
|
388
|
+
if (typeof errorData === "string") errorText = errorData;
|
|
389
|
+
else {
|
|
390
|
+
errorCode = errorData.error;
|
|
391
|
+
errorDescription = errorData.error_description;
|
|
392
|
+
errorText = errorDescription ?? errorCode ?? "Unknown error";
|
|
393
|
+
}
|
|
394
|
+
} catch {
|
|
395
|
+
errorText = await response.text().catch(() => "Unknown error");
|
|
396
|
+
}
|
|
397
|
+
return {
|
|
398
|
+
errorText,
|
|
399
|
+
errorCode,
|
|
400
|
+
errorDescription
|
|
401
|
+
};
|
|
402
|
+
}
|
|
351
403
|
async getDeviceInformation(userCode) {
|
|
352
404
|
const params = new URLSearchParams();
|
|
353
405
|
params.append("user_code", userCode);
|
|
354
406
|
const url = new URL(`${this.apiUrl}/oauth2/device/information`);
|
|
355
407
|
url.search = params.toString();
|
|
356
|
-
const response = await
|
|
408
|
+
const response = await this.sendRequest(url, {
|
|
357
409
|
method: "GET",
|
|
358
410
|
headers: {
|
|
359
411
|
Authorization: `Bearer ${this.pat}`,
|
|
360
412
|
Accept: "application/json"
|
|
361
413
|
}
|
|
362
414
|
});
|
|
363
|
-
if (!response.ok)
|
|
415
|
+
if (!response.ok) {
|
|
416
|
+
const { errorText, errorCode, errorDescription } = await this.parseOAuth2Error(response);
|
|
417
|
+
throw new CloudError(`Failed to get device information: ${errorText}`, {
|
|
418
|
+
statusCode: response.status,
|
|
419
|
+
code: errorCode,
|
|
420
|
+
reason: errorDescription
|
|
421
|
+
});
|
|
422
|
+
}
|
|
364
423
|
return await response.json();
|
|
365
424
|
}
|
|
366
|
-
/**
|
|
367
|
-
* Approve the device with the specified scopes.
|
|
368
|
-
*
|
|
369
|
-
* @param userCode - The user code from the authentication request
|
|
370
|
-
* @param scopes - The scopes to approve
|
|
371
|
-
* @throws {DeviceApprovalError} If the request fails
|
|
372
|
-
*/
|
|
373
425
|
async approveDevice(userCode, scopes) {
|
|
374
426
|
const params = new URLSearchParams();
|
|
375
427
|
params.append("user_code", userCode);
|
|
376
428
|
params.append("scope", scopes.join(","));
|
|
377
429
|
const url = new URL(`${this.apiUrl}/oauth2/device/approve`);
|
|
378
430
|
url.search = params.toString();
|
|
379
|
-
const response = await
|
|
431
|
+
const response = await this.sendRequest(url, {
|
|
380
432
|
method: "POST",
|
|
381
433
|
headers: {
|
|
382
434
|
Authorization: `Bearer ${this.pat}`,
|
|
@@ -384,10 +436,17 @@ var PATApprover = class {
|
|
|
384
436
|
Accept: "application/json"
|
|
385
437
|
}
|
|
386
438
|
});
|
|
387
|
-
if (!response.ok)
|
|
439
|
+
if (!response.ok) {
|
|
440
|
+
const { errorText, errorCode, errorDescription } = await this.parseOAuth2Error(response);
|
|
441
|
+
throw new CloudError(`Failed to approve device: ${errorText}`, {
|
|
442
|
+
statusCode: response.status,
|
|
443
|
+
code: errorCode,
|
|
444
|
+
reason: errorDescription
|
|
445
|
+
});
|
|
446
|
+
}
|
|
388
447
|
}
|
|
389
448
|
};
|
|
390
449
|
|
|
391
450
|
//#endregion
|
|
392
|
-
export {
|
|
451
|
+
export { AuthClient, AuthenticationError, BrowserApprover, CloudError, InstanceError, PATApprover };
|
|
393
452
|
//# sourceMappingURL=index.mjs.map
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":["createWSClient"],"sources":["../src/errors.ts","../src/queries.ts","../src/client.ts","../src/approvers/browser.ts","../src/approvers/pat.ts"],"sourcesContent":["/**\n * Base error class for authentication-related errors.\n */\nexport class AuthenticationError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"AuthenticationError\";\n }\n}\n\n/**\n * Error thrown when the authentication flow fails to start.\n */\nexport class AuthenticationFlowError extends AuthenticationError {\n /** Error code from the API */\n readonly code: string;\n\n constructor(code: string, message: string) {\n super(`${code}: ${message}`);\n this.name = \"AuthenticationFlowError\";\n this.code = code;\n }\n}\n\n/**\n * Error thrown when token refresh fails.\n */\nexport class TokenRefreshError extends AuthenticationError {\n /** Error code from the API */\n readonly code: string;\n\n constructor(code: string, message: string) {\n super(`${code}: ${message}`);\n this.name = \"TokenRefreshError\";\n this.code = code;\n }\n}\n\n/**\n * Error thrown when device approval fails.\n */\nexport class DeviceApprovalError extends AuthenticationError {\n /** HTTP status code if available */\n readonly statusCode: number | undefined;\n\n constructor(message: string, statusCode?: number) {\n super(message);\n this.name = \"DeviceApprovalError\";\n this.statusCode = statusCode;\n }\n}\n\n/**\n * Error thrown when fetching device information fails.\n */\nexport class DeviceInformationError extends AuthenticationError {\n /** HTTP status code if available */\n readonly statusCode: number | undefined;\n\n constructor(message: string, statusCode?: number) {\n super(message);\n this.name = \"DeviceInformationError\";\n this.statusCode = statusCode;\n }\n}\n","import type { DocumentNode } from \"graphql\";\nimport { gql } from \"graphql-tag\";\n\nexport const START_AUTHENTICATION_FLOW: DocumentNode = gql`\n mutation StartAuthenticationFlow {\n startAuthenticationFlow {\n request {\n id\n userCode\n verificationUrl\n expiresAt\n }\n error {\n code\n message\n }\n }\n }\n`;\n\nexport const CREATED_AUTHENTICATION_TOKEN: DocumentNode = gql`\n subscription CreatedAuthenticationToken($requestId: ID!) {\n createdAuthenticationToken(requestId: $requestId) {\n token {\n accessToken\n refreshToken\n expiresAt\n }\n error {\n code\n message\n }\n }\n }\n`;\n\nexport const REFRESH_AUTHENTICATION_TOKEN: DocumentNode = gql`\n mutation RefreshAuthenticationToken($refreshToken: String!) {\n refreshAuthenticationToken(refreshToken: $refreshToken) {\n token {\n accessToken\n refreshToken\n expiresAt\n }\n error {\n code\n message\n }\n }\n }\n`;\n","import { Client, fetchExchange } from \"@urql/core\";\nimport { createClient as createWSClient } from \"graphql-ws\";\n\nimport type { AuthApprover } from \"./approvers/types.js\";\nimport {\n AuthenticationError,\n AuthenticationFlowError,\n TokenRefreshError,\n} from \"./errors.js\";\nimport {\n CREATED_AUTHENTICATION_TOKEN,\n REFRESH_AUTHENTICATION_TOKEN,\n START_AUTHENTICATION_FLOW,\n} from \"./queries.js\";\nimport type {\n AuthenticationRequest,\n AuthenticationToken,\n CreatedAuthenticationTokenResponse,\n RefreshAuthenticationTokenResponse,\n StartAuthenticationFlowResponse,\n} from \"./types.js\";\n\n/**\n * Client for authenticating with a Caido instance.\n *\n * @example\n * ```typescript\n * import { CaidoAuth, BrowserApprover } from \"@caido/auth\";\n *\n * const auth = new CaidoAuth(\n * \"http://localhost:8080\",\n * new BrowserApprover((request) => {\n * console.log(`Visit ${request.verificationUrl}`);\n * })\n * );\n *\n * const token = await auth.startAuthenticationFlow();\n * console.log(\"Access token:\", token.accessToken);\n * ```\n */\nexport class CaidoAuth {\n private readonly instanceUrl: string;\n private readonly graphqlUrl: string;\n private readonly websocketUrl: string;\n private readonly approver: AuthApprover;\n private readonly client: Client;\n\n /**\n * Create a new CaidoAuth client.\n *\n * @param instanceUrl - Base URL of the Caido instance (e.g., \"http://localhost:8080\")\n * @param approver - The approver to use for the authentication flow\n */\n constructor(instanceUrl: string, approver: AuthApprover) {\n this.instanceUrl = instanceUrl.replace(/\\/$/, \"\");\n this.graphqlUrl = `${this.instanceUrl}/graphql`;\n this.websocketUrl = this.getWebsocketUrl();\n this.approver = approver;\n\n this.client = new Client({\n url: this.graphqlUrl,\n exchanges: [fetchExchange],\n });\n }\n\n /**\n * Convert HTTP(S) URL to WS(S) URL for subscriptions.\n */\n private getWebsocketUrl(): string {\n const url = new URL(this.graphqlUrl);\n const scheme = url.protocol === \"https:\" ? \"wss:\" : \"ws:\";\n return `${scheme}//${url.host}/ws/graphql`;\n }\n\n /**\n * Start the device code authentication flow.\n *\n * This method:\n * 1. Initiates the authentication flow via GraphQL mutation\n * 2. Calls the approver with the authentication request\n * 3. Waits for the user to authorize via WebSocket subscription\n * 4. Returns the authentication token once approved\n *\n * @returns The authentication token\n * @throws {AuthenticationFlowError} If the flow fails to start\n * @throws {AuthenticationError} If token retrieval fails\n */\n async startAuthenticationFlow(): Promise<AuthenticationToken> {\n // Step 1: Start the authentication flow\n const result = await this.client\n .mutation<StartAuthenticationFlowResponse>(START_AUTHENTICATION_FLOW, {})\n .toPromise();\n\n if (result.error) {\n throw new AuthenticationFlowError(\"GRAPHQL_ERROR\", result.error.message);\n }\n\n const payload = result.data?.startAuthenticationFlow;\n if (!payload) {\n throw new AuthenticationFlowError(\n \"NO_RESPONSE\",\n \"No response from startAuthenticationFlow\",\n );\n }\n\n if (payload.error) {\n throw new AuthenticationFlowError(\n payload.error.code,\n payload.error.message,\n );\n }\n\n if (!payload.request) {\n throw new AuthenticationFlowError(\n \"NO_REQUEST\",\n \"No authentication request returned\",\n );\n }\n\n const authRequest: AuthenticationRequest = {\n id: payload.request.id,\n userCode: payload.request.userCode,\n verificationUrl: payload.request.verificationUrl,\n expiresAt: new Date(payload.request.expiresAt),\n };\n\n // Step 2: Call the approver\n await this.approver.approve(authRequest);\n\n // Step 3: Wait for the token via subscription\n const token = await this.waitForToken(authRequest.id);\n return token;\n }\n\n /**\n * Subscribe and wait for the authentication token.\n *\n * @param requestId - The authentication request ID\n * @returns The authentication token once the user authorizes\n * @throws {AuthenticationError} If subscription fails or returns an error\n */\n private async waitForToken(requestId: string): Promise<AuthenticationToken> {\n return new Promise<AuthenticationToken>((resolve, reject) => {\n const wsClient = createWSClient({\n url: this.websocketUrl,\n });\n\n const unsubscribe =\n wsClient.subscribe<CreatedAuthenticationTokenResponse>(\n {\n query:\n CREATED_AUTHENTICATION_TOKEN.loc?.source.body ??\n `subscription CreatedAuthenticationToken($requestId: ID!) {\n createdAuthenticationToken(requestId: $requestId) {\n token { accessToken refreshToken expiresAt }\n error { code message }\n }\n }`,\n variables: { requestId },\n },\n {\n next: (result) => {\n const payload = result.data?.createdAuthenticationToken;\n\n if (payload?.error) {\n unsubscribe();\n wsClient.dispose();\n reject(\n new AuthenticationError(\n `${payload.error.code}: ${payload.error.message}`,\n ),\n );\n return;\n }\n\n if (payload?.token) {\n unsubscribe();\n wsClient.dispose();\n resolve({\n accessToken: payload.token.accessToken,\n refreshToken: payload.token.refreshToken,\n expiresAt: new Date(payload.token.expiresAt),\n });\n }\n },\n error: (error) => {\n wsClient.dispose();\n reject(\n new AuthenticationError(\n error instanceof Error ? error.message : String(error),\n ),\n );\n },\n complete: () => {\n wsClient.dispose();\n reject(\n new AuthenticationError(\n \"Subscription ended without receiving token\",\n ),\n );\n },\n },\n );\n });\n }\n\n /**\n * Refresh an access token using a refresh token.\n *\n * @param refreshToken - The refresh token from a previous authentication\n * @returns New authentication token with updated access and refresh tokens\n * @throws {TokenRefreshError} If the refresh fails\n */\n async refreshToken(refreshToken: string): Promise<AuthenticationToken> {\n const result = await this.client\n .mutation<RefreshAuthenticationTokenResponse>(\n REFRESH_AUTHENTICATION_TOKEN,\n { refreshToken },\n )\n .toPromise();\n\n if (result.error) {\n throw new TokenRefreshError(\"GRAPHQL_ERROR\", result.error.message);\n }\n\n const payload = result.data?.refreshAuthenticationToken;\n if (!payload) {\n throw new TokenRefreshError(\n \"NO_RESPONSE\",\n \"No response from refreshAuthenticationToken\",\n );\n }\n\n if (payload.error) {\n throw new TokenRefreshError(payload.error.code, payload.error.message);\n }\n\n if (!payload.token) {\n throw new TokenRefreshError(\"NO_TOKEN\", \"No token returned from refresh\");\n }\n\n return {\n accessToken: payload.token.accessToken,\n refreshToken: payload.token.refreshToken,\n expiresAt: new Date(payload.token.expiresAt),\n };\n }\n}\n","import type { AuthenticationRequest } from \"../types.js\";\n\nimport type { AuthApprover } from \"./types.js\";\n\n/**\n * Callback function that receives the authentication request details.\n * Used to display the verification URL and user code to the user.\n */\nexport type OnRequestCallback = (\n request: AuthenticationRequest,\n) => Promise<void> | void;\n\n/**\n * Browser-based approver that delegates to a callback function.\n * The callback should display the verification URL and user code to the user,\n * who then manually approves the request in their browser.\n *\n * @example\n * ```typescript\n * const approver = new BrowserApprover((request) => {\n * console.log(`Visit ${request.verificationUrl}`);\n * });\n * ```\n */\nexport class BrowserApprover implements AuthApprover {\n private readonly onRequest: OnRequestCallback;\n\n /**\n * Create a new BrowserApprover.\n *\n * @param onRequest - Callback function that will be called with the authentication request\n */\n constructor(onRequest: OnRequestCallback) {\n this.onRequest = onRequest;\n }\n\n /**\n * Approve the authentication request by calling the callback.\n * The actual approval happens when the user visits the URL and enters the code.\n *\n * @param request - The authentication request\n */\n async approve(request: AuthenticationRequest): Promise<void> {\n await this.onRequest(request);\n }\n}\n","import { DeviceApprovalError, DeviceInformationError } from \"../errors.js\";\nimport type { AuthenticationRequest, DeviceInformation } from \"../types.js\";\n\nimport type { AuthApprover } from \"./types.js\";\n\nconst DEFAULT_API_URL = \"https://api.caido.io\";\n\n/**\n * Options for the PATApprover.\n */\nexport interface PATApproverOptions {\n /** The Personal Access Token to use for approval */\n pat: string;\n /** If provided, only approve these scopes. Others will be filtered out. */\n allowedScopes?: string[];\n /** The API URL to use. Defaults to \"https://api.caido.io\" */\n apiUrl?: string;\n}\n\n/**\n * PAT-based approver that automatically approves device code requests.\n * Uses a Personal Access Token to call the Caido API directly.\n *\n * @example\n * ```typescript\n * // Approve all scopes\n * const approver = new PATApprover({ pat: \"caido_xxxxx\" });\n *\n * // Approve only specific scopes\n * const limitedApprover = new PATApprover({\n * pat: \"caido_xxxxx\",\n * allowedScopes: [\"read:projects\", \"write:requests\"],\n * });\n * ```\n */\nexport class PATApprover implements AuthApprover {\n private readonly pat: string;\n private readonly allowedScopes: string[] | undefined;\n private readonly apiUrl: string;\n\n /**\n * Create a new PATApprover.\n *\n * @param options - Configuration options for the approver\n */\n constructor(options: PATApproverOptions) {\n this.pat = options.pat;\n this.allowedScopes = options.allowedScopes;\n this.apiUrl = (options.apiUrl ?? DEFAULT_API_URL).replace(/\\/$/, \"\");\n }\n\n /**\n * Approve the authentication request using the PAT.\n * First fetches device information to get available scopes,\n * then filters scopes if allowedScopes is set,\n * and finally approves the device.\n *\n * @param request - The authentication request\n * @throws {DeviceInformationError} If fetching device information fails\n * @throws {DeviceApprovalError} If approving the device fails\n */\n async approve(request: AuthenticationRequest): Promise<void> {\n // Step 1: Get device information to retrieve available scopes\n const deviceInfo = await this.getDeviceInformation(request.userCode);\n\n // Step 2: Filter scopes if allowedScopes is provided\n let scopesToApprove = deviceInfo.scopes.map((s) => s.name);\n if (this.allowedScopes) {\n scopesToApprove = scopesToApprove.filter((scope) =>\n this.allowedScopes!.includes(scope),\n );\n }\n\n // Step 3: Approve the device with the filtered scopes\n await this.approveDevice(request.userCode, scopesToApprove);\n }\n\n /**\n * Fetch device information from the API.\n *\n * @param userCode - The user code from the authentication request\n * @returns The device information including available scopes\n * @throws {DeviceInformationError} If the request fails\n */\n private async getDeviceInformation(\n userCode: string,\n ): Promise<DeviceInformation> {\n const params = new URLSearchParams();\n params.append(\"user_code\", userCode);\n const url = new URL(`${this.apiUrl}/oauth2/device/information`);\n url.search = params.toString();\n\n const response = await fetch(url, {\n method: \"GET\",\n headers: {\n Authorization: `Bearer ${this.pat}`,\n Accept: \"application/json\",\n },\n });\n\n if (!response.ok) {\n const errorText = await response.text().catch(() => \"Unknown error\");\n throw new DeviceInformationError(\n `Failed to get device information: ${errorText}`,\n response.status,\n );\n }\n\n const data = (await response.json()) as DeviceInformation;\n return data;\n }\n\n /**\n * Approve the device with the specified scopes.\n *\n * @param userCode - The user code from the authentication request\n * @param scopes - The scopes to approve\n * @throws {DeviceApprovalError} If the request fails\n */\n private async approveDevice(\n userCode: string,\n scopes: string[],\n ): Promise<void> {\n const params = new URLSearchParams();\n params.append(\"user_code\", userCode);\n params.append(\"scope\", scopes.join(\",\"));\n const url = new URL(`${this.apiUrl}/oauth2/device/approve`);\n url.search = params.toString();\n\n const response = await fetch(url, {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${this.pat}`,\n \"Content-Type\": \"application/json\",\n Accept: \"application/json\",\n },\n });\n\n if (!response.ok) {\n const errorText = await response.text().catch(() => \"Unknown error\");\n throw new DeviceApprovalError(\n `Failed to approve device: ${errorText}`,\n response.status,\n );\n }\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.mjs","names":["createWSClient"],"sources":["../src/errors.ts","../src/queries.ts","../src/client.ts","../src/approvers/browser.ts","../src/approvers/pat.ts"],"sourcesContent":["/**\n * Base error class for authentication-related errors.\n */\nexport class AuthenticationError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"AuthenticationError\";\n }\n}\n\n/**\n * Error thrown for errors coming from the Caido cloud API.\n * Used for device approval and device information operations.\n */\nexport class CloudError extends AuthenticationError {\n /** HTTP status code if available */\n readonly statusCode: number | undefined;\n /** Error code from the API if available */\n readonly code: string | undefined;\n /** Reason for the error if available */\n readonly reason: string | undefined;\n\n constructor(\n message: string,\n options?: {\n statusCode?: number;\n code?: string;\n reason?: string;\n },\n ) {\n super(message);\n this.name = \"CloudError\";\n this.statusCode = options?.statusCode;\n this.code = options?.code;\n this.reason = options?.reason;\n }\n}\n\n/**\n * Error thrown for errors coming from the Caido instance.\n * Used for authentication flow and token refresh operations.\n */\nexport class InstanceError extends AuthenticationError {\n /** Error code from the API */\n readonly code: string;\n /** Reason for the error if available */\n readonly reason: string | undefined;\n /** Error message if available */\n readonly errorMessage: string | undefined;\n\n constructor(\n code: string,\n options?: {\n reason?: string;\n message?: string;\n },\n ) {\n const message = options?.reason ?? options?.message ?? code;\n super(message);\n this.name = \"InstanceError\";\n this.code = code;\n this.reason = options?.reason;\n this.errorMessage = options?.message;\n }\n}\n","import type { DocumentNode } from \"graphql\";\nimport { gql } from \"graphql-tag\";\n\nexport const START_AUTHENTICATION_FLOW: DocumentNode = gql`\n mutation StartAuthenticationFlow {\n startAuthenticationFlow {\n request {\n id\n userCode\n verificationUrl\n expiresAt\n }\n error {\n ... on AuthenticationUserError {\n code\n reason\n }\n ... on CloudUserError {\n code\n reason\n }\n ... on InternalUserError {\n code\n message\n }\n ... on OtherUserError {\n code\n }\n }\n }\n }\n`;\n\nexport const CREATED_AUTHENTICATION_TOKEN: DocumentNode = gql`\n subscription CreatedAuthenticationToken($requestId: ID!) {\n createdAuthenticationToken(requestId: $requestId) {\n token {\n accessToken\n expiresAt\n refreshToken\n scopes\n }\n error {\n ... on AuthenticationUserError {\n code\n reason\n }\n ... on InternalUserError {\n code\n message\n }\n ... on OtherUserError {\n code\n }\n }\n }\n }\n`;\n\nexport const REFRESH_AUTHENTICATION_TOKEN: DocumentNode = gql`\n mutation RefreshAuthenticationToken($refreshToken: Token!) {\n refreshAuthenticationToken(refreshToken: $refreshToken) {\n token {\n accessToken\n expiresAt\n refreshToken\n scopes\n }\n error {\n ... on AuthenticationUserError {\n code\n reason\n }\n ... on CloudUserError {\n code\n reason\n }\n ... on InternalUserError {\n code\n message\n }\n ... on OtherUserError {\n code\n }\n }\n }\n }\n`;\n","import { Client, fetchExchange } from \"@urql/core\";\nimport { print } from \"graphql\";\nimport { createClient as createWSClient } from \"graphql-ws\";\n\nimport type { AuthApprover } from \"./approvers/types.js\";\nimport { AuthenticationError, InstanceError } from \"./errors.js\";\nimport {\n CREATED_AUTHENTICATION_TOKEN,\n REFRESH_AUTHENTICATION_TOKEN,\n START_AUTHENTICATION_FLOW,\n} from \"./queries.js\";\nimport type {\n AuthenticationRequest,\n AuthenticationToken,\n CreatedAuthenticationTokenError,\n CreatedAuthenticationTokenResponse,\n RefreshAuthenticationTokenError,\n RefreshAuthenticationTokenResponse,\n StartAuthenticationFlowError,\n StartAuthenticationFlowResponse,\n} from \"./types.js\";\n\n/**\n * Options for configuring the AuthClient.\n */\nexport interface AuthClientOptions {\n /** Base URL of the Caido instance (e.g., \"http://localhost:8080\") */\n instanceUrl: string;\n /** The approver to use for the authentication flow */\n approver: AuthApprover;\n /** Request timeout in milliseconds */\n timeout?: number;\n /** Custom fetch implementation */\n fetch?: typeof globalThis.fetch;\n}\n\ninterface ErrorDetails {\n reason?: string;\n message?: string;\n}\n\n/**\n * Client for authenticating with a Caido instance.\n *\n * @example\n * ```typescript\n * import { AuthClient, BrowserApprover } from \"@caido/auth\";\n *\n * const auth = new AuthClient({\n * instanceUrl: \"http://localhost:8080\",\n * approver: new BrowserApprover((request) => {\n * console.log(`Visit ${request.verificationUrl}`);\n * })\n * });\n *\n * const token = await auth.startAuthenticationFlow();\n * console.log(\"Access token:\", token.accessToken);\n * ```\n */\nexport class AuthClient {\n private readonly instanceUrl: string;\n private readonly graphqlUrl: string;\n private readonly websocketUrl: string;\n private readonly approver: AuthApprover;\n private readonly client: Client;\n private readonly fetchFn: typeof globalThis.fetch | undefined;\n private readonly timeout: number | undefined;\n\n constructor(options: AuthClientOptions) {\n this.instanceUrl = options.instanceUrl.replace(/\\/$/, \"\");\n this.graphqlUrl = `${this.instanceUrl}/graphql`;\n this.websocketUrl = this.getWebsocketUrl();\n this.approver = options.approver;\n this.fetchFn = options.fetch;\n this.timeout = options.timeout;\n\n this.client = new Client({\n url: this.graphqlUrl,\n exchanges: [fetchExchange],\n fetchOptions: () => {\n const fetchOptions: RequestInit = {};\n if (this.timeout !== undefined) {\n fetchOptions.signal = AbortSignal.timeout(this.timeout);\n }\n return fetchOptions;\n },\n fetch: this.fetchFn,\n });\n }\n\n private getWebsocketUrl(): string {\n const url = new URL(this.graphqlUrl);\n const scheme = url.protocol === \"https:\" ? \"wss:\" : \"ws:\";\n return `${scheme}//${url.host}/ws/graphql`;\n }\n\n private extractErrorDetails(\n error:\n | StartAuthenticationFlowError\n | CreatedAuthenticationTokenError\n | RefreshAuthenticationTokenError,\n ): ErrorDetails {\n if (\"reason\" in error) {\n return { reason: error.reason };\n }\n if (\"message\" in error) {\n return { message: error.message };\n }\n return {};\n }\n\n /**\n * Start the device code authentication flow.\n *\n * This method:\n * 1. Initiates the authentication flow via GraphQL mutation\n * 2. Calls the approver with the authentication request\n * 3. Waits for the user to authorize via WebSocket subscription\n * 4. Returns the authentication token once approved\n *\n * @returns The authentication token\n * @throws {InstanceError} If the flow fails to start\n * @throws {AuthenticationError} If token retrieval fails\n */\n async startAuthenticationFlow(): Promise<AuthenticationToken> {\n // Step 1: Start the authentication flow\n const result = await this.client\n .mutation<StartAuthenticationFlowResponse>(START_AUTHENTICATION_FLOW, {})\n .toPromise();\n\n if (result.error) {\n throw new InstanceError(\"GRAPHQL_ERROR\", {\n message: result.error.message,\n });\n }\n\n const payload = result.data?.startAuthenticationFlow;\n if (!payload) {\n throw new InstanceError(\"NO_RESPONSE\", {\n message: \"No response from startAuthenticationFlow\",\n });\n }\n\n if (payload.error) {\n const details = this.extractErrorDetails(payload.error);\n throw new InstanceError(payload.error.code, details);\n }\n\n if (!payload.request) {\n throw new InstanceError(\"NO_REQUEST\", {\n message: \"No authentication request returned\",\n });\n }\n\n const authRequest: AuthenticationRequest = {\n id: payload.request.id,\n userCode: payload.request.userCode,\n verificationUrl: payload.request.verificationUrl,\n expiresAt: new Date(payload.request.expiresAt),\n };\n\n // Step 2: Call the approver\n await this.approver.approve(authRequest);\n\n // Step 3: Wait for the token via subscription\n const token = await this.waitForToken(authRequest.id);\n return token;\n }\n\n private async waitForToken(requestId: string): Promise<AuthenticationToken> {\n return new Promise<AuthenticationToken>((resolve, reject) => {\n const wsClient = createWSClient({\n url: this.websocketUrl,\n });\n\n const unsubscribe =\n wsClient.subscribe<CreatedAuthenticationTokenResponse>(\n {\n query: print(CREATED_AUTHENTICATION_TOKEN),\n variables: { requestId },\n },\n {\n next: (result) => {\n const payload = result.data?.createdAuthenticationToken;\n\n if (payload?.error) {\n unsubscribe();\n wsClient.dispose();\n const details = this.extractErrorDetails(payload.error);\n reject(new InstanceError(payload.error.code, details));\n return;\n }\n\n if (payload?.token) {\n unsubscribe();\n wsClient.dispose();\n resolve({\n accessToken: payload.token.accessToken,\n refreshToken: payload.token.refreshToken,\n expiresAt: new Date(payload.token.expiresAt),\n scopes: payload.token.scopes,\n });\n }\n },\n error: (error) => {\n wsClient.dispose();\n reject(\n new InstanceError(\"SUBSCRIPTION_ERROR\", {\n message:\n error instanceof Error ? error.message : String(error),\n }),\n );\n },\n complete: () => {\n wsClient.dispose();\n reject(\n new InstanceError(\"SUBSCRIPTION_COMPLETE\", {\n message: \"Subscription ended without receiving token\",\n }),\n );\n },\n },\n );\n });\n }\n\n /**\n * Refresh an access token using a refresh token.\n *\n * @param refreshToken - The refresh token from a previous authentication\n * @returns New authentication token with updated access and refresh tokens\n * @throws {InstanceError} If the refresh fails\n */\n async refreshToken(refreshToken: string): Promise<AuthenticationToken> {\n const result = await this.client\n .mutation<RefreshAuthenticationTokenResponse>(\n REFRESH_AUTHENTICATION_TOKEN,\n { refreshToken },\n )\n .toPromise();\n\n if (result.error) {\n throw new InstanceError(\"GRAPHQL_ERROR\", {\n message: result.error.message,\n });\n }\n\n const payload = result.data?.refreshAuthenticationToken;\n if (!payload) {\n throw new InstanceError(\"NO_RESPONSE\", {\n message: \"No response from refreshAuthenticationToken\",\n });\n }\n\n if (payload.error) {\n const details = this.extractErrorDetails(payload.error);\n throw new InstanceError(payload.error.code, details);\n }\n\n if (!payload.token) {\n throw new InstanceError(\"NO_TOKEN\", {\n message: \"No token returned from refresh\",\n });\n }\n\n return {\n accessToken: payload.token.accessToken,\n refreshToken: payload.token.refreshToken,\n expiresAt: new Date(payload.token.expiresAt),\n scopes: payload.token.scopes,\n };\n }\n}\n","import type { AuthenticationRequest } from \"../types.js\";\n\nimport type { AuthApprover } from \"./types.js\";\n\n/**\n * Callback function that receives the authentication request details.\n * Used to display the verification URL and user code to the user.\n */\nexport type OnRequestCallback = (\n request: AuthenticationRequest,\n) => Promise<void> | void;\n\n/**\n * Browser-based approver that delegates to a callback function.\n * The callback should display the verification URL and user code to the user,\n * who then manually approves the request in their browser.\n *\n * @example\n * ```typescript\n * const approver = new BrowserApprover((request) => {\n * console.log(`Visit ${request.verificationUrl}`);\n * });\n * ```\n */\nexport class BrowserApprover implements AuthApprover {\n private readonly onRequest: OnRequestCallback;\n\n /**\n * Create a new BrowserApprover.\n *\n * @param onRequest - Callback function that will be called with the authentication request\n */\n constructor(onRequest: OnRequestCallback) {\n this.onRequest = onRequest;\n }\n\n /**\n * Approve the authentication request by calling the callback.\n * The actual approval happens when the user visits the URL and enters the code.\n *\n * @param request - The authentication request\n */\n async approve(request: AuthenticationRequest): Promise<void> {\n await this.onRequest(request);\n }\n}\n","import { CloudError } from \"../errors.js\";\nimport type { AuthenticationRequest, DeviceInformation } from \"../types.js\";\n\nimport type { AuthApprover } from \"./types.js\";\n\nconst DEFAULT_API_URL = \"https://api.caido.io\";\n\ninterface OAuth2ErrorResponse {\n error?: string;\n error_description?: string;\n}\n\ntype OAuth2ErrorData = OAuth2ErrorResponse | string;\n\ninterface ParsedOAuth2Error {\n errorText: string;\n errorCode: string | undefined;\n errorDescription: string | undefined;\n}\n\ninterface RequestOptions {\n method: string;\n headers: Record<string, string>;\n}\n\n/**\n * Options for the PATApprover.\n */\nexport interface PATApproverOptions {\n /** The Personal Access Token to use for approval */\n pat: string;\n /** If provided, only approve these scopes. Others will be filtered out. */\n allowedScopes?: string[];\n /** The API URL to use. Defaults to \"https://api.caido.io\" */\n apiUrl?: string;\n /** Request timeout in milliseconds */\n timeout?: number;\n /** Custom fetch implementation */\n fetch?: typeof globalThis.fetch;\n}\n\n/**\n * PAT-based approver that automatically approves device code requests.\n * Uses a Personal Access Token to call the Caido API directly.\n *\n * @example\n * ```typescript\n * // Approve all scopes\n * const approver = new PATApprover({ pat: \"caido_xxxxx\" });\n *\n * // Approve only specific scopes\n * const limitedApprover = new PATApprover({\n * pat: \"caido_xxxxx\",\n * allowedScopes: [\"read:projects\", \"write:requests\"],\n * });\n * ```\n */\nexport class PATApprover implements AuthApprover {\n private readonly pat: string;\n private readonly allowedScopes: string[] | undefined;\n private readonly apiUrl: string;\n private readonly fetchFn: typeof globalThis.fetch;\n private readonly timeout: number | undefined;\n\n constructor(options: PATApproverOptions) {\n this.pat = options.pat;\n this.allowedScopes = options.allowedScopes;\n this.apiUrl = (options.apiUrl ?? DEFAULT_API_URL).replace(/\\/$/, \"\");\n this.fetchFn = options.fetch ?? globalThis.fetch;\n this.timeout = options.timeout;\n }\n\n /**\n * Approve the authentication request using the PAT.\n * First fetches device information to get available scopes,\n * then filters scopes if allowedScopes is set,\n * and finally approves the device.\n *\n * @param request - The authentication request\n * @throws {CloudError} If fetching device information or approving the device fails\n */\n async approve(request: AuthenticationRequest): Promise<void> {\n // Step 1: Get device information to retrieve available scopes\n const deviceInfo = await this.getDeviceInformation(request.userCode);\n\n // Step 2: Filter scopes if allowedScopes is provided\n let scopesToApprove = deviceInfo.scopes.map((s) => s.name);\n if (this.allowedScopes) {\n scopesToApprove = scopesToApprove.filter((scope) =>\n this.allowedScopes!.includes(scope),\n );\n }\n\n // Step 3: Approve the device with the filtered scopes\n await this.approveDevice(request.userCode, scopesToApprove);\n }\n\n private async sendRequest(\n url: URL,\n options: RequestOptions,\n ): Promise<Response> {\n const fetchOptions: RequestInit = {\n method: options.method,\n headers: options.headers,\n };\n\n if (this.timeout !== undefined) {\n fetchOptions.signal = AbortSignal.timeout(this.timeout);\n }\n\n return this.fetchFn(url, fetchOptions);\n }\n\n private async parseOAuth2Error(\n response: Response,\n ): Promise<ParsedOAuth2Error> {\n let errorText: string;\n let errorCode: string | undefined;\n let errorDescription: string | undefined;\n\n try {\n const errorData = (await response.json()) as OAuth2ErrorData;\n if (typeof errorData === \"string\") {\n errorText = errorData;\n } else {\n errorCode = errorData.error;\n errorDescription = errorData.error_description;\n errorText = errorDescription ?? errorCode ?? \"Unknown error\";\n }\n } catch {\n errorText = await response.text().catch(() => \"Unknown error\");\n }\n\n return { errorText, errorCode, errorDescription };\n }\n\n private async getDeviceInformation(\n userCode: string,\n ): Promise<DeviceInformation> {\n const params = new URLSearchParams();\n params.append(\"user_code\", userCode);\n const url = new URL(`${this.apiUrl}/oauth2/device/information`);\n url.search = params.toString();\n\n const response = await this.sendRequest(url, {\n method: \"GET\",\n headers: {\n Authorization: `Bearer ${this.pat}`,\n Accept: \"application/json\",\n },\n });\n\n if (!response.ok) {\n const { errorText, errorCode, errorDescription } =\n await this.parseOAuth2Error(response);\n\n throw new CloudError(`Failed to get device information: ${errorText}`, {\n statusCode: response.status,\n code: errorCode,\n reason: errorDescription,\n });\n }\n\n const data = (await response.json()) as DeviceInformation;\n return data;\n }\n\n private async approveDevice(\n userCode: string,\n scopes: string[],\n ): Promise<void> {\n const params = new URLSearchParams();\n params.append(\"user_code\", userCode);\n params.append(\"scope\", scopes.join(\",\"));\n const url = new URL(`${this.apiUrl}/oauth2/device/approve`);\n url.search = params.toString();\n\n const response = await this.sendRequest(url, {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${this.pat}`,\n \"Content-Type\": \"application/json\",\n Accept: \"application/json\",\n },\n });\n\n if (!response.ok) {\n const { errorText, errorCode, errorDescription } =\n await this.parseOAuth2Error(response);\n\n throw new CloudError(`Failed to approve device: ${errorText}`, {\n statusCode: response.status,\n code: errorCode,\n reason: errorDescription,\n });\n }\n }\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@caido/server-auth",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Authenticate with a Caido instance",
|
|
5
5
|
"author": "Caido Labs Inc. <dev@caido.io>",
|
|
6
6
|
"repository": "https://github.com/caido/sdk-js/",
|
|
@@ -24,13 +24,14 @@
|
|
|
24
24
|
"files": [
|
|
25
25
|
"dist/*"
|
|
26
26
|
],
|
|
27
|
-
"scripts": {
|
|
28
|
-
"build": "tsdown"
|
|
29
|
-
},
|
|
30
27
|
"dependencies": {
|
|
31
28
|
"@urql/core": "^6.0.0",
|
|
32
29
|
"graphql": "^16.12.0",
|
|
33
30
|
"graphql-tag": "^2.12.0",
|
|
34
31
|
"graphql-ws": "^6.0.0"
|
|
32
|
+
},
|
|
33
|
+
"scripts": {
|
|
34
|
+
"build": "tsdown",
|
|
35
|
+
"typecheck": "tsc --noEmit"
|
|
35
36
|
}
|
|
36
|
-
}
|
|
37
|
+
}
|