@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/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 Caido Labs Inc.
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
<img width="1000" alt="image" src="https://user-images.githubusercontent.com/6225588/211916659-567751d1-0225-402b-9141-4145c18b0834.png">
|
|
3
|
+
|
|
4
|
+
<br />
|
|
5
|
+
<br />
|
|
6
|
+
<a href="https://caido.io/">Website</a>
|
|
7
|
+
<span> • </span>
|
|
8
|
+
<a href="https://dashboard.caido.io/">Dashboard</a>
|
|
9
|
+
<span> • </span>
|
|
10
|
+
<a href="https://docs.caido.io/" target="_blank">Docs</a>
|
|
11
|
+
<span> • </span>
|
|
12
|
+
<a href="https://links.caido.io/roadmap">Roadmap</a>
|
|
13
|
+
<span> • </span>
|
|
14
|
+
<a href="https://github.com/caido/caido/tree/main/brand">Branding</a>
|
|
15
|
+
<span> • </span>
|
|
16
|
+
<a href="https://links.caido.io/www-discord" target="_blank">Discord</a>
|
|
17
|
+
<br />
|
|
18
|
+
<hr />
|
|
19
|
+
</div>
|
|
20
|
+
|
|
21
|
+
## 👋 Server Auth
|
|
22
|
+
|
|
23
|
+
[](https://www.npmjs.com/package/@caido/server-auth)
|
|
24
|
+
|
|
25
|
+
Authenticate with a Caido instance using device code flow.
|
|
26
|
+
|
|
27
|
+
```typescript
|
|
28
|
+
import { CaidoAuth, BrowserApprover } from "@caido/server-auth";
|
|
29
|
+
|
|
30
|
+
const auth = new CaidoAuth(
|
|
31
|
+
"http://localhost:8080",
|
|
32
|
+
new BrowserApprover((request) => {
|
|
33
|
+
console.log(`Visit ${request.verificationUrl}`);
|
|
34
|
+
console.log(`Enter code: ${request.userCode}`);
|
|
35
|
+
})
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
const token = await auth.startAuthenticationFlow();
|
|
39
|
+
console.log("Access token:", token.accessToken);
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Examples
|
|
43
|
+
|
|
44
|
+
See the [examples](./examples/) directory for complete working examples:
|
|
45
|
+
|
|
46
|
+
- [Browser Authentication](./examples/browser/) - Manual approval via browser
|
|
47
|
+
- [PAT Authentication](./examples/pat/) - Automated approval using Personal Access Token
|
|
48
|
+
|
|
49
|
+
## 💚 Community
|
|
50
|
+
|
|
51
|
+
Come join our [Discord](https://links.caido.io/www-discord) community and connect with other Caido users! We'd love to have you as part of the conversation and help with any questions you may have.
|
package/dist/index.cjs
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
let _urql_core = require("@urql/core");
|
|
2
|
+
let graphql = require("graphql");
|
|
2
3
|
let graphql_ws = require("graphql-ws");
|
|
3
4
|
let graphql_tag = require("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 = graphql_tag.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 = graphql_tag.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 = graphql_tag.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 = graphql_tag.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 _urql_core.Client({
|
|
151
178
|
url: this.graphqlUrl,
|
|
152
|
-
exchanges: [_urql_core.fetchExchange]
|
|
179
|
+
exchanges: [_urql_core.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 = (0, graphql_ws.createClient)({ url: this.websocketUrl });
|
|
201
232
|
const unsubscribe = wsClient.subscribe({
|
|
202
|
-
query:
|
|
203
|
-
createdAuthenticationToken(requestId: $requestId) {
|
|
204
|
-
token { accessToken refreshToken expiresAt }
|
|
205
|
-
error { code message }
|
|
206
|
-
}
|
|
207
|
-
}`,
|
|
233
|
+
query: (0, graphql.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,17 +436,22 @@ 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
|
|
451
|
+
exports.AuthClient = AuthClient;
|
|
392
452
|
exports.AuthenticationError = AuthenticationError;
|
|
393
|
-
exports.AuthenticationFlowError = AuthenticationFlowError;
|
|
394
453
|
exports.BrowserApprover = BrowserApprover;
|
|
395
|
-
exports.
|
|
396
|
-
exports.
|
|
397
|
-
exports.DeviceInformationError = DeviceInformationError;
|
|
454
|
+
exports.CloudError = CloudError;
|
|
455
|
+
exports.InstanceError = InstanceError;
|
|
398
456
|
exports.PATApprover = PATApprover;
|
|
399
|
-
exports.TokenRefreshError = TokenRefreshError;
|
|
400
457
|
//# sourceMappingURL=index.cjs.map
|