@vigilbase/cli 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 +138 -0
- package/dist/auth/device-auth.d.ts +58 -0
- package/dist/auth/device-auth.d.ts.map +1 -0
- package/dist/auth/device-auth.js +229 -0
- package/dist/auth/device-auth.js.map +1 -0
- package/dist/auth/index.d.ts +4 -0
- package/dist/auth/index.d.ts.map +1 -0
- package/dist/auth/index.js +3 -0
- package/dist/auth/index.js.map +1 -0
- package/dist/auth/store.d.ts +12 -0
- package/dist/auth/store.d.ts.map +1 -0
- package/dist/auth/store.js +85 -0
- package/dist/auth/store.js.map +1 -0
- package/dist/auth/types.d.ts +46 -0
- package/dist/auth/types.d.ts.map +1 -0
- package/dist/auth/types.js +8 -0
- package/dist/auth/types.js.map +1 -0
- package/dist/bin.d.ts +3 -0
- package/dist/bin.d.ts.map +1 -0
- package/dist/bin.js +5 -0
- package/dist/bin.js.map +1 -0
- package/dist/cli.d.ts +13 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +510 -0
- package/dist/cli.js.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/banner.d.ts +29 -0
- package/dist/lib/banner.d.ts.map +1 -0
- package/dist/lib/banner.js +68 -0
- package/dist/lib/banner.js.map +1 -0
- package/dist/lib/client.d.ts +27 -0
- package/dist/lib/client.d.ts.map +1 -0
- package/dist/lib/client.js +146 -0
- package/dist/lib/client.js.map +1 -0
- package/dist/lib/config.d.ts +31 -0
- package/dist/lib/config.d.ts.map +1 -0
- package/dist/lib/config.js +57 -0
- package/dist/lib/config.js.map +1 -0
- package/dist/lib/format.d.ts +14 -0
- package/dist/lib/format.d.ts.map +1 -0
- package/dist/lib/format.js +289 -0
- package/dist/lib/format.js.map +1 -0
- package/dist/lib/help.d.ts +4 -0
- package/dist/lib/help.d.ts.map +1 -0
- package/dist/lib/help.js +58 -0
- package/dist/lib/help.js.map +1 -0
- package/dist/lib/one-client.d.ts +43 -0
- package/dist/lib/one-client.d.ts.map +1 -0
- package/dist/lib/one-client.js +147 -0
- package/dist/lib/one-client.js.map +1 -0
- package/dist/lib/open-url.d.ts +25 -0
- package/dist/lib/open-url.d.ts.map +1 -0
- package/dist/lib/open-url.js +112 -0
- package/dist/lib/open-url.js.map +1 -0
- package/dist/lib/progress.d.ts +20 -0
- package/dist/lib/progress.d.ts.map +1 -0
- package/dist/lib/progress.js +78 -0
- package/dist/lib/progress.js.map +1 -0
- package/dist/types.d.ts +45 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +7 -0
- package/dist/types.js.map +1 -0
- package/package.json +70 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Vigilbase
|
|
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,138 @@
|
|
|
1
|
+
# @vigilbase/cli
|
|
2
|
+
|
|
3
|
+
Terminal client for Vigilbase — public scanners/tools without login, plus Vigilbase One after [device authorization](https://better-auth.com/docs/plugins/device-authorization).
|
|
4
|
+
|
|
5
|
+
The binary name is **`vigilbase`** — after a global install you run:
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
vigilbase help
|
|
9
|
+
vigilbase run security-headers https://vigilbase.com
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
## Install
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
npm install -g @vigilbase/cli
|
|
16
|
+
# or
|
|
17
|
+
pnpm add -g @vigilbase/cli
|
|
18
|
+
|
|
19
|
+
vigilbase help
|
|
20
|
+
vigilbase list --json
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Without a global install:
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
npx @vigilbase/cli help
|
|
27
|
+
npx @vigilbase/cli run email-security vigilbase.com --json
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
### From this monorepo
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
pnpm install
|
|
34
|
+
pnpm link:vigilbase
|
|
35
|
+
# builds dist/ and symlinks `vigilbase` into PATH
|
|
36
|
+
|
|
37
|
+
vigilbase help
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Unlink later with `pnpm unlink:vigilbase`.
|
|
41
|
+
|
|
42
|
+
## Commands
|
|
43
|
+
|
|
44
|
+
### Public tools (no auth)
|
|
45
|
+
|
|
46
|
+
| Command | Purpose |
|
|
47
|
+
| --- | --- |
|
|
48
|
+
| `vigilbase list` | Catalog from `GET /api/agent/tools` |
|
|
49
|
+
| `vigilbase run <tool> <target>` | Execute a scan |
|
|
50
|
+
| `vigilbase headers <url>` | Shortcut for `security-headers` |
|
|
51
|
+
| `vigilbase email <domain>` | Shortcut for `email-security` |
|
|
52
|
+
| `vigilbase describe` / `skill` | Tool metadata + skill markdown |
|
|
53
|
+
| `vigilbase badge <domain>` | Public badge URL |
|
|
54
|
+
| `vigilbase doctor` | Connectivity + auth status |
|
|
55
|
+
|
|
56
|
+
Currently allowlisted for autonomous execution: `security-headers`, `email-security`.
|
|
57
|
+
|
|
58
|
+
### Auth (Vigilbase One)
|
|
59
|
+
|
|
60
|
+
| Command | Purpose |
|
|
61
|
+
| --- | --- |
|
|
62
|
+
| `vigilbase login` | Device-code sign-in (RFC 8628) at `one.vigilbase.com/device` |
|
|
63
|
+
| `vigilbase logout` | Clear `~/.config/vigilbase/credentials.json` |
|
|
64
|
+
| `vigilbase whoami` | Show local / live One session |
|
|
65
|
+
|
|
66
|
+
Sensitive tools unlocked after login:
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
vigilbase login
|
|
70
|
+
vigilbase run website-security vigilbase.com
|
|
71
|
+
vigilbase run react2shell https://app.example.com
|
|
72
|
+
vigilbase run fortibleed-check example.com
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Public tools stay usable while signed out. Sensitive tools route through Vigilbase One after `vigilbase login`.
|
|
76
|
+
|
|
77
|
+
## Agent-friendly mode
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
vigilbase list --json
|
|
81
|
+
vigilbase run security-headers https://vigilbase.com --json
|
|
82
|
+
vigilbase email vigilbase.com --json
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
| Exit code | Meaning |
|
|
86
|
+
| --- | --- |
|
|
87
|
+
| `0` | Success |
|
|
88
|
+
| `1` | Error |
|
|
89
|
+
| `2` | Rate limited (`HTTP 429`) |
|
|
90
|
+
|
|
91
|
+
## Local development
|
|
92
|
+
|
|
93
|
+
For pointing the CLI at local servers (not shown in `vigilbase help`):
|
|
94
|
+
|
|
95
|
+
| Env / flag | Description |
|
|
96
|
+
| --- | --- |
|
|
97
|
+
| `VIGILBASE_BASE_URL` / `--base-url` | Public API (default `https://vigilbase.com`) |
|
|
98
|
+
| `VIGILBASE_ONE_URL` / `--one-url` | One API (default `https://one.vigilbase.com`) |
|
|
99
|
+
| `VIGILBASE_CLIENT_ID` / `--client-id` | OAuth client id for device auth |
|
|
100
|
+
| `VIGILBASE_CONFIG_DIR` | Override `~/.config/vigilbase` |
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
vigilbase doctor --base-url http://localhost:3001
|
|
104
|
+
vigilbase login --one-url http://localhost:3000
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
## Architecture notes
|
|
108
|
+
|
|
109
|
+
- **Public surface** (`VigilbaseClient`) talks only to vigilbase.com agent/tool endpoints — no credentials.
|
|
110
|
+
- **One surface** stores tokens from Better Auth `POST /api/auth/device/code` + `/device/token` under `~/.config/vigilbase/credentials.json`.
|
|
111
|
+
- Headless / CI: `vigilbase login --token "$VIGILBASE_ONE_TOKEN"` or `--no-open` and approve the printed URL elsewhere.
|
|
112
|
+
|
|
113
|
+
## Smoke test
|
|
114
|
+
|
|
115
|
+
```bash
|
|
116
|
+
VIGILBASE_BASE_URL=https://vigilbase.com pnpm --filter @vigilbase/cli smoke:e2e
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
## Publishing
|
|
120
|
+
|
|
121
|
+
See [PUBLISH.md](./PUBLISH.md) for npm org setup and `npm publish` steps.
|
|
122
|
+
|
|
123
|
+
## Library API
|
|
124
|
+
|
|
125
|
+
```ts
|
|
126
|
+
import { runCli, VigilbaseClient, createConfig } from '@vigilbase/cli'
|
|
127
|
+
|
|
128
|
+
await runCli({ argv: ['list', '--json'] })
|
|
129
|
+
|
|
130
|
+
const client = new VigilbaseClient(createConfig())
|
|
131
|
+
await client.listTools()
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
## Related
|
|
135
|
+
|
|
136
|
+
- OpenAPI: https://vigilbase.com/openapi.json
|
|
137
|
+
- Skills index: https://vigilbase.com/.well-known/agent-skills/index.json
|
|
138
|
+
- Device auth docs: https://better-auth.com/docs/plugins/device-authorization
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Better Auth device authorization (RFC 8628) client for Vigilbase One.
|
|
3
|
+
*
|
|
4
|
+
* Server plugin: https://better-auth.com/docs/plugins/device-authorization
|
|
5
|
+
* Endpoints (relative to One's Better Auth base, typically `/api/auth`):
|
|
6
|
+
* POST /device/code
|
|
7
|
+
* POST /device/token
|
|
8
|
+
*
|
|
9
|
+
* Public tool commands do not use this module. One-scoped commands will.
|
|
10
|
+
*/
|
|
11
|
+
import type { CliConfig } from '../lib/config.js';
|
|
12
|
+
import type { DeviceCodeResponse, DeviceTokenPendingError, DeviceTokenSuccess } from './types.js';
|
|
13
|
+
export declare class DeviceAuthError extends Error {
|
|
14
|
+
readonly code?: string;
|
|
15
|
+
readonly status: number;
|
|
16
|
+
readonly body: unknown;
|
|
17
|
+
constructor(message: string, options: {
|
|
18
|
+
status: number;
|
|
19
|
+
code?: string;
|
|
20
|
+
body?: unknown;
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
export declare function requestDeviceCode(config: CliConfig, options?: {
|
|
24
|
+
scope?: string;
|
|
25
|
+
}): Promise<DeviceCodeResponse>;
|
|
26
|
+
export type PollResult = {
|
|
27
|
+
status: 'authorized';
|
|
28
|
+
token: DeviceTokenSuccess;
|
|
29
|
+
} | {
|
|
30
|
+
status: 'pending';
|
|
31
|
+
error: DeviceTokenPendingError;
|
|
32
|
+
} | {
|
|
33
|
+
status: 'denied';
|
|
34
|
+
error: DeviceTokenPendingError;
|
|
35
|
+
} | {
|
|
36
|
+
status: 'expired';
|
|
37
|
+
error: DeviceTokenPendingError;
|
|
38
|
+
} | {
|
|
39
|
+
status: 'transient';
|
|
40
|
+
httpStatus: number;
|
|
41
|
+
error: DeviceTokenPendingError;
|
|
42
|
+
};
|
|
43
|
+
export declare function pollDeviceToken(config: CliConfig, deviceCode: string): Promise<PollResult>;
|
|
44
|
+
export declare function sleep(ms: number): Promise<void>;
|
|
45
|
+
/**
|
|
46
|
+
* Run the full device-code poll loop until authorized, denied, or expired.
|
|
47
|
+
* Returns the token payload on success.
|
|
48
|
+
*/
|
|
49
|
+
export declare function waitForDeviceAuthorization(config: CliConfig, device: DeviceCodeResponse, options?: {
|
|
50
|
+
onPending?: (info: {
|
|
51
|
+
attempt: number;
|
|
52
|
+
intervalSec: number;
|
|
53
|
+
kind: 'pending' | 'transient';
|
|
54
|
+
}) => void;
|
|
55
|
+
signal?: AbortSignal;
|
|
56
|
+
maxConsecutiveTransient?: number;
|
|
57
|
+
}): Promise<DeviceTokenSuccess>;
|
|
58
|
+
//# sourceMappingURL=device-auth.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"device-auth.d.ts","sourceRoot":"","sources":["../../src/auth/device-auth.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAA;AACjD,OAAO,KAAK,EACV,kBAAkB,EAClB,uBAAuB,EACvB,kBAAkB,EACnB,MAAM,YAAY,CAAA;AAOnB,qBAAa,eAAgB,SAAQ,KAAK;IACxC,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAA;gBAGpB,OAAO,EAAE,MAAM,EACf,OAAO,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,OAAO,CAAA;KAAE;CAQ7D;AAkDD,wBAAsB,iBAAiB,CACrC,MAAM,EAAE,SAAS,EACjB,OAAO,CAAC,EAAE;IAAE,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,GAC3B,OAAO,CAAC,kBAAkB,CAAC,CAyC7B;AAED,MAAM,MAAM,UAAU,GAClB;IAAE,MAAM,EAAE,YAAY,CAAC;IAAC,KAAK,EAAE,kBAAkB,CAAA;CAAE,GACnD;IAAE,MAAM,EAAE,SAAS,CAAC;IAAC,KAAK,EAAE,uBAAuB,CAAA;CAAE,GACrD;IAAE,MAAM,EAAE,QAAQ,CAAC;IAAC,KAAK,EAAE,uBAAuB,CAAA;CAAE,GACpD;IAAE,MAAM,EAAE,SAAS,CAAC;IAAC,KAAK,EAAE,uBAAuB,CAAA;CAAE,GACrD;IACE,MAAM,EAAE,WAAW,CAAA;IACnB,UAAU,EAAE,MAAM,CAAA;IAClB,KAAK,EAAE,uBAAuB,CAAA;CAC/B,CAAA;AAEL,wBAAsB,eAAe,CACnC,MAAM,EAAE,SAAS,EACjB,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,UAAU,CAAC,CAoErB;AAED,wBAAsB,KAAK,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAErD;AAED;;;GAGG;AACH,wBAAsB,0BAA0B,CAC9C,MAAM,EAAE,SAAS,EACjB,MAAM,EAAE,kBAAkB,EAC1B,OAAO,CAAC,EAAE;IACR,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE;QACjB,OAAO,EAAE,MAAM,CAAA;QACf,WAAW,EAAE,MAAM,CAAA;QACnB,IAAI,EAAE,SAAS,GAAG,WAAW,CAAA;KAC9B,KAAK,IAAI,CAAA;IACV,MAAM,CAAC,EAAE,WAAW,CAAA;IACpB,uBAAuB,CAAC,EAAE,MAAM,CAAA;CACjC,GACA,OAAO,CAAC,kBAAkB,CAAC,CAiF7B"}
|
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Better Auth device authorization (RFC 8628) client for Vigilbase One.
|
|
3
|
+
*
|
|
4
|
+
* Server plugin: https://better-auth.com/docs/plugins/device-authorization
|
|
5
|
+
* Endpoints (relative to One's Better Auth base, typically `/api/auth`):
|
|
6
|
+
* POST /device/code
|
|
7
|
+
* POST /device/token
|
|
8
|
+
*
|
|
9
|
+
* Public tool commands do not use this module. One-scoped commands will.
|
|
10
|
+
*/
|
|
11
|
+
const DEVICE_GRANT = 'urn:ietf:params:oauth:grant-type:device_code';
|
|
12
|
+
/** How many consecutive unreachable token polls before aborting a live login. */
|
|
13
|
+
const MAX_CONSECUTIVE_TRANSIENT = 8;
|
|
14
|
+
export class DeviceAuthError extends Error {
|
|
15
|
+
code;
|
|
16
|
+
status;
|
|
17
|
+
body;
|
|
18
|
+
constructor(message, options) {
|
|
19
|
+
super(message);
|
|
20
|
+
this.name = 'DeviceAuthError';
|
|
21
|
+
this.status = options.status;
|
|
22
|
+
this.code = options.code;
|
|
23
|
+
this.body = options.body;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
function authBase(config) {
|
|
27
|
+
return `${config.oneBaseUrl}/api/auth`;
|
|
28
|
+
}
|
|
29
|
+
function isRetryableTokenStatus(status) {
|
|
30
|
+
return (status === 404 ||
|
|
31
|
+
status === 408 ||
|
|
32
|
+
status === 425 ||
|
|
33
|
+
status === 429 ||
|
|
34
|
+
status >= 500);
|
|
35
|
+
}
|
|
36
|
+
async function postJson(config, path, body) {
|
|
37
|
+
const url = `${authBase(config)}${path}`;
|
|
38
|
+
const controller = new AbortController();
|
|
39
|
+
const timer = setTimeout(() => controller.abort(), config.timeoutMs);
|
|
40
|
+
try {
|
|
41
|
+
const response = await fetch(url, {
|
|
42
|
+
method: 'POST',
|
|
43
|
+
signal: controller.signal,
|
|
44
|
+
headers: {
|
|
45
|
+
Accept: 'application/json',
|
|
46
|
+
'Content-Type': 'application/json',
|
|
47
|
+
'User-Agent': config.userAgent,
|
|
48
|
+
},
|
|
49
|
+
body: JSON.stringify(body),
|
|
50
|
+
});
|
|
51
|
+
const text = await response.text();
|
|
52
|
+
let json = null;
|
|
53
|
+
if (text) {
|
|
54
|
+
try {
|
|
55
|
+
json = JSON.parse(text);
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
json = { raw: text };
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return { status: response.status, json };
|
|
62
|
+
}
|
|
63
|
+
finally {
|
|
64
|
+
clearTimeout(timer);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
export async function requestDeviceCode(config, options) {
|
|
68
|
+
const { status, json } = await postJson(config, '/device/code', {
|
|
69
|
+
client_id: config.clientId,
|
|
70
|
+
...(options?.scope ? { scope: options.scope } : {}),
|
|
71
|
+
});
|
|
72
|
+
if (status === 404) {
|
|
73
|
+
throw new DeviceAuthError(`Device authorization is not enabled on ${config.oneBaseUrl} yet. ` +
|
|
74
|
+
'Enable Better Auth\'s deviceAuthorization plugin on Vigilbase One, then retry `vigilbase login`.', { status, body: json, code: 'not_enabled' });
|
|
75
|
+
}
|
|
76
|
+
if (status < 200 || status >= 300) {
|
|
77
|
+
const message = json &&
|
|
78
|
+
typeof json === 'object' &&
|
|
79
|
+
'message' in json &&
|
|
80
|
+
typeof json.message === 'string'
|
|
81
|
+
? json.message
|
|
82
|
+
: `Device code request failed (HTTP ${status})`;
|
|
83
|
+
throw new DeviceAuthError(message, { status, body: json });
|
|
84
|
+
}
|
|
85
|
+
const data = json;
|
|
86
|
+
if (!data.device_code || !data.user_code || !data.verification_uri) {
|
|
87
|
+
throw new DeviceAuthError('Device code response was missing required fields.', { status, body: json });
|
|
88
|
+
}
|
|
89
|
+
return {
|
|
90
|
+
device_code: data.device_code,
|
|
91
|
+
user_code: data.user_code,
|
|
92
|
+
verification_uri: data.verification_uri,
|
|
93
|
+
verification_uri_complete: data.verification_uri_complete,
|
|
94
|
+
expires_in: data.expires_in ?? 600,
|
|
95
|
+
interval: data.interval ?? 5,
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
export async function pollDeviceToken(config, deviceCode) {
|
|
99
|
+
let status;
|
|
100
|
+
let json;
|
|
101
|
+
try {
|
|
102
|
+
;
|
|
103
|
+
({ status, json } = await postJson(config, '/device/token', {
|
|
104
|
+
grant_type: DEVICE_GRANT,
|
|
105
|
+
device_code: deviceCode,
|
|
106
|
+
client_id: config.clientId,
|
|
107
|
+
}));
|
|
108
|
+
}
|
|
109
|
+
catch (error) {
|
|
110
|
+
const description = error instanceof Error ? error.message : 'Network error while polling';
|
|
111
|
+
return {
|
|
112
|
+
status: 'transient',
|
|
113
|
+
httpStatus: 0,
|
|
114
|
+
error: {
|
|
115
|
+
error: 'authorization_pending',
|
|
116
|
+
error_description: description,
|
|
117
|
+
},
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
if (json &&
|
|
121
|
+
typeof json === 'object' &&
|
|
122
|
+
'access_token' in json &&
|
|
123
|
+
typeof json.access_token === 'string') {
|
|
124
|
+
return { status: 'authorized', token: json };
|
|
125
|
+
}
|
|
126
|
+
const errorBody = (json ?? {});
|
|
127
|
+
const code = errorBody.error || `http_${status}`;
|
|
128
|
+
switch (code) {
|
|
129
|
+
case 'authorization_pending':
|
|
130
|
+
case 'slow_down':
|
|
131
|
+
return { status: 'pending', error: errorBody };
|
|
132
|
+
case 'access_denied':
|
|
133
|
+
return { status: 'denied', error: errorBody };
|
|
134
|
+
case 'expired_token':
|
|
135
|
+
return { status: 'expired', error: errorBody };
|
|
136
|
+
default:
|
|
137
|
+
break;
|
|
138
|
+
}
|
|
139
|
+
// After a successful device/code, brief 404/5xx (deploys, rollbacks, blips)
|
|
140
|
+
// must not abort login as "not enabled".
|
|
141
|
+
if (isRetryableTokenStatus(status)) {
|
|
142
|
+
return {
|
|
143
|
+
status: 'transient',
|
|
144
|
+
httpStatus: status,
|
|
145
|
+
error: {
|
|
146
|
+
error: 'authorization_pending',
|
|
147
|
+
error_description: errorBody.error_description ||
|
|
148
|
+
errorBody.error ||
|
|
149
|
+
`Temporary device token error (HTTP ${status})`,
|
|
150
|
+
},
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
throw new DeviceAuthError(errorBody.error_description ||
|
|
154
|
+
errorBody.error ||
|
|
155
|
+
`Device token request failed (HTTP ${status})`, { status, body: json, code });
|
|
156
|
+
}
|
|
157
|
+
export async function sleep(ms) {
|
|
158
|
+
await new Promise((resolve) => setTimeout(resolve, ms));
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Run the full device-code poll loop until authorized, denied, or expired.
|
|
162
|
+
* Returns the token payload on success.
|
|
163
|
+
*/
|
|
164
|
+
export async function waitForDeviceAuthorization(config, device, options) {
|
|
165
|
+
let intervalSec = device.interval && device.interval > 0 ? device.interval : 5;
|
|
166
|
+
const deadline = Date.now() + device.expires_in * 1000;
|
|
167
|
+
let attempt = 0;
|
|
168
|
+
let consecutiveTransient = 0;
|
|
169
|
+
const maxTransient = options?.maxConsecutiveTransient ?? MAX_CONSECUTIVE_TRANSIENT;
|
|
170
|
+
let lastTransientStatus = 0;
|
|
171
|
+
while (Date.now() < deadline) {
|
|
172
|
+
if (options?.signal?.aborted) {
|
|
173
|
+
throw new DeviceAuthError('Login cancelled.', {
|
|
174
|
+
status: 0,
|
|
175
|
+
code: 'cancelled',
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
attempt += 1;
|
|
179
|
+
options?.onPending?.({
|
|
180
|
+
attempt,
|
|
181
|
+
intervalSec,
|
|
182
|
+
kind: consecutiveTransient > 0 ? 'transient' : 'pending',
|
|
183
|
+
});
|
|
184
|
+
await sleep(intervalSec * 1000);
|
|
185
|
+
const result = await pollDeviceToken(config, device.device_code);
|
|
186
|
+
switch (result.status) {
|
|
187
|
+
case 'authorized':
|
|
188
|
+
return result.token;
|
|
189
|
+
case 'pending':
|
|
190
|
+
consecutiveTransient = 0;
|
|
191
|
+
if (result.error.error === 'slow_down') {
|
|
192
|
+
intervalSec += 5;
|
|
193
|
+
}
|
|
194
|
+
break;
|
|
195
|
+
case 'transient':
|
|
196
|
+
consecutiveTransient += 1;
|
|
197
|
+
lastTransientStatus = result.httpStatus;
|
|
198
|
+
if (consecutiveTransient >= maxTransient) {
|
|
199
|
+
throw new DeviceAuthError(`Lost contact with device authorization on ${config.oneBaseUrl} while waiting for approval` +
|
|
200
|
+
(lastTransientStatus
|
|
201
|
+
? ` (HTTP ${lastTransientStatus})`
|
|
202
|
+
: '') +
|
|
203
|
+
'. Retry `vigilbase login` once One is healthy.', {
|
|
204
|
+
status: lastTransientStatus,
|
|
205
|
+
code: 'unreachable',
|
|
206
|
+
body: result.error,
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
break;
|
|
210
|
+
case 'denied':
|
|
211
|
+
throw new DeviceAuthError('Access was denied in the browser.', {
|
|
212
|
+
status: 403,
|
|
213
|
+
code: 'access_denied',
|
|
214
|
+
body: result.error,
|
|
215
|
+
});
|
|
216
|
+
case 'expired':
|
|
217
|
+
throw new DeviceAuthError('The device code expired. Run `vigilbase login` again.', { status: 400, code: 'expired_token', body: result.error });
|
|
218
|
+
default: {
|
|
219
|
+
const _exhaustive = result;
|
|
220
|
+
throw new DeviceAuthError(`Unexpected poll result: ${JSON.stringify(_exhaustive)}`, {
|
|
221
|
+
status: 0,
|
|
222
|
+
code: 'unexpected',
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
throw new DeviceAuthError('Timed out waiting for browser authorization. Run `vigilbase login` again.', { status: 0, code: 'timeout' });
|
|
228
|
+
}
|
|
229
|
+
//# sourceMappingURL=device-auth.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"device-auth.js","sourceRoot":"","sources":["../../src/auth/device-auth.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AASH,MAAM,YAAY,GAAG,8CAA8C,CAAA;AAEnE,iFAAiF;AACjF,MAAM,yBAAyB,GAAG,CAAC,CAAA;AAEnC,MAAM,OAAO,eAAgB,SAAQ,KAAK;IAC/B,IAAI,CAAS;IACb,MAAM,CAAQ;IACd,IAAI,CAAS;IAEtB,YACE,OAAe,EACf,OAA0D;QAE1D,KAAK,CAAC,OAAO,CAAC,CAAA;QACd,IAAI,CAAC,IAAI,GAAG,iBAAiB,CAAA;QAC7B,IAAI,CAAC,MAAM,GAAG,OAAO,CAAC,MAAM,CAAA;QAC5B,IAAI,CAAC,IAAI,GAAG,OAAO,CAAC,IAAI,CAAA;QACxB,IAAI,CAAC,IAAI,GAAG,OAAO,CAAC,IAAI,CAAA;IAC1B,CAAC;CACF;AAED,SAAS,QAAQ,CAAC,MAAiB;IACjC,OAAO,GAAG,MAAM,CAAC,UAAU,WAAW,CAAA;AACxC,CAAC;AAED,SAAS,sBAAsB,CAAC,MAAc;IAC5C,OAAO,CACL,MAAM,KAAK,GAAG;QACd,MAAM,KAAK,GAAG;QACd,MAAM,KAAK,GAAG;QACd,MAAM,KAAK,GAAG;QACd,MAAM,IAAI,GAAG,CACd,CAAA;AACH,CAAC;AAED,KAAK,UAAU,QAAQ,CACrB,MAAiB,EACjB,IAAY,EACZ,IAA6B;IAE7B,MAAM,GAAG,GAAG,GAAG,QAAQ,CAAC,MAAM,CAAC,GAAG,IAAI,EAAE,CAAA;IACxC,MAAM,UAAU,GAAG,IAAI,eAAe,EAAE,CAAA;IACxC,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,UAAU,CAAC,KAAK,EAAE,EAAE,MAAM,CAAC,SAAS,CAAC,CAAA;IACpE,IAAI,CAAC;QACH,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE;YAChC,MAAM,EAAE,MAAM;YACd,MAAM,EAAE,UAAU,CAAC,MAAM;YACzB,OAAO,EAAE;gBACP,MAAM,EAAE,kBAAkB;gBAC1B,cAAc,EAAE,kBAAkB;gBAClC,YAAY,EAAE,MAAM,CAAC,SAAS;aAC/B;YACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC;SAC3B,CAAC,CAAA;QACF,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAA;QAClC,IAAI,IAAI,GAAY,IAAI,CAAA;QACxB,IAAI,IAAI,EAAE,CAAC;YACT,IAAI,CAAC;gBACH,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAY,CAAA;YACpC,CAAC;YAAC,MAAM,CAAC;gBACP,IAAI,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,CAAA;YACtB,CAAC;QACH,CAAC;QACD,OAAO,EAAE,MAAM,EAAE,QAAQ,CAAC,MAAM,EAAE,IAAI,EAAE,CAAA;IAC1C,CAAC;YAAS,CAAC;QACT,YAAY,CAAC,KAAK,CAAC,CAAA;IACrB,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,MAAiB,EACjB,OAA4B;IAE5B,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,QAAQ,CAAC,MAAM,EAAE,cAAc,EAAE;QAC9D,SAAS,EAAE,MAAM,CAAC,QAAQ;QAC1B,GAAG,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KACpD,CAAC,CAAA;IAEF,IAAI,MAAM,KAAK,GAAG,EAAE,CAAC;QACnB,MAAM,IAAI,eAAe,CACvB,0CAA0C,MAAM,CAAC,UAAU,QAAQ;YACjE,kGAAkG,EACpG,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,aAAa,EAAE,CAC5C,CAAA;IACH,CAAC;IAED,IAAI,MAAM,GAAG,GAAG,IAAI,MAAM,IAAI,GAAG,EAAE,CAAC;QAClC,MAAM,OAAO,GACX,IAAI;YACJ,OAAO,IAAI,KAAK,QAAQ;YACxB,SAAS,IAAI,IAAI;YACjB,OAAQ,IAA6B,CAAC,OAAO,KAAK,QAAQ;YACxD,CAAC,CAAE,IAA4B,CAAC,OAAO;YACvC,CAAC,CAAC,oCAAoC,MAAM,GAAG,CAAA;QACnD,MAAM,IAAI,eAAe,CAAC,OAAO,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAA;IAC5D,CAAC;IAED,MAAM,IAAI,GAAG,IAAmC,CAAA;IAChD,IAAI,CAAC,IAAI,CAAC,WAAW,IAAI,CAAC,IAAI,CAAC,SAAS,IAAI,CAAC,IAAI,CAAC,gBAAgB,EAAE,CAAC;QACnE,MAAM,IAAI,eAAe,CACvB,mDAAmD,EACnD,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,CACvB,CAAA;IACH,CAAC;IAED,OAAO;QACL,WAAW,EAAE,IAAI,CAAC,WAAW;QAC7B,SAAS,EAAE,IAAI,CAAC,SAAS;QACzB,gBAAgB,EAAE,IAAI,CAAC,gBAAgB;QACvC,yBAAyB,EAAE,IAAI,CAAC,yBAAyB;QACzD,UAAU,EAAE,IAAI,CAAC,UAAU,IAAI,GAAG;QAClC,QAAQ,EAAE,IAAI,CAAC,QAAQ,IAAI,CAAC;KAC7B,CAAA;AACH,CAAC;AAaD,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,MAAiB,EACjB,UAAkB;IAElB,IAAI,MAAc,CAAA;IAClB,IAAI,IAAa,CAAA;IACjB,IAAI,CAAC;QACH,CAAC;QAAA,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,QAAQ,CAAC,MAAM,EAAE,eAAe,EAAE;YAC3D,UAAU,EAAE,YAAY;YACxB,WAAW,EAAE,UAAU;YACvB,SAAS,EAAE,MAAM,CAAC,QAAQ;SAC3B,CAAC,CAAC,CAAA;IACL,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,WAAW,GACf,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,6BAA6B,CAAA;QACxE,OAAO;YACL,MAAM,EAAE,WAAW;YACnB,UAAU,EAAE,CAAC;YACb,KAAK,EAAE;gBACL,KAAK,EAAE,uBAAuB;gBAC9B,iBAAiB,EAAE,WAAW;aAC/B;SACF,CAAA;IACH,CAAC;IAED,IACE,IAAI;QACJ,OAAO,IAAI,KAAK,QAAQ;QACxB,cAAc,IAAI,IAAI;QACtB,OAAQ,IAAkC,CAAC,YAAY,KAAK,QAAQ,EACpE,CAAC;QACD,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,KAAK,EAAE,IAA0B,EAAE,CAAA;IACpE,CAAC;IAED,MAAM,SAAS,GAAG,CAAC,IAAI,IAAI,EAAE,CAA4B,CAAA;IACzD,MAAM,IAAI,GAAG,SAAS,CAAC,KAAK,IAAI,QAAQ,MAAM,EAAE,CAAA;IAEhD,QAAQ,IAAI,EAAE,CAAC;QACb,KAAK,uBAAuB,CAAC;QAC7B,KAAK,WAAW;YACd,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,KAAK,EAAE,SAAS,EAAE,CAAA;QAChD,KAAK,eAAe;YAClB,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,SAAS,EAAE,CAAA;QAC/C,KAAK,eAAe;YAClB,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,KAAK,EAAE,SAAS,EAAE,CAAA;QAChD;YACE,MAAK;IACT,CAAC;IAED,4EAA4E;IAC5E,yCAAyC;IACzC,IAAI,sBAAsB,CAAC,MAAM,CAAC,EAAE,CAAC;QACnC,OAAO;YACL,MAAM,EAAE,WAAW;YACnB,UAAU,EAAE,MAAM;YAClB,KAAK,EAAE;gBACL,KAAK,EAAE,uBAAuB;gBAC9B,iBAAiB,EACf,SAAS,CAAC,iBAAiB;oBAC3B,SAAS,CAAC,KAAK;oBACf,sCAAsC,MAAM,GAAG;aAClD;SACF,CAAA;IACH,CAAC;IAED,MAAM,IAAI,eAAe,CACvB,SAAS,CAAC,iBAAiB;QACzB,SAAS,CAAC,KAAK;QACf,qCAAqC,MAAM,GAAG,EAChD,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,CAC7B,CAAA;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,KAAK,CAAC,EAAU;IACpC,MAAM,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,CAAA;AACzD,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,0BAA0B,CAC9C,MAAiB,EACjB,MAA0B,EAC1B,OAQC;IAED,IAAI,WAAW,GAAG,MAAM,CAAC,QAAQ,IAAI,MAAM,CAAC,QAAQ,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAA;IAC9E,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,MAAM,CAAC,UAAU,GAAG,IAAI,CAAA;IACtD,IAAI,OAAO,GAAG,CAAC,CAAA;IACf,IAAI,oBAAoB,GAAG,CAAC,CAAA;IAC5B,MAAM,YAAY,GAChB,OAAO,EAAE,uBAAuB,IAAI,yBAAyB,CAAA;IAC/D,IAAI,mBAAmB,GAAG,CAAC,CAAA;IAE3B,OAAO,IAAI,CAAC,GAAG,EAAE,GAAG,QAAQ,EAAE,CAAC;QAC7B,IAAI,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC;YAC7B,MAAM,IAAI,eAAe,CAAC,kBAAkB,EAAE;gBAC5C,MAAM,EAAE,CAAC;gBACT,IAAI,EAAE,WAAW;aAClB,CAAC,CAAA;QACJ,CAAC;QAED,OAAO,IAAI,CAAC,CAAA;QACZ,OAAO,EAAE,SAAS,EAAE,CAAC;YACnB,OAAO;YACP,WAAW;YACX,IAAI,EAAE,oBAAoB,GAAG,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,SAAS;SACzD,CAAC,CAAA;QACF,MAAM,KAAK,CAAC,WAAW,GAAG,IAAI,CAAC,CAAA;QAE/B,MAAM,MAAM,GAAG,MAAM,eAAe,CAAC,MAAM,EAAE,MAAM,CAAC,WAAW,CAAC,CAAA;QAChE,QAAQ,MAAM,CAAC,MAAM,EAAE,CAAC;YACtB,KAAK,YAAY;gBACf,OAAO,MAAM,CAAC,KAAK,CAAA;YACrB,KAAK,SAAS;gBACZ,oBAAoB,GAAG,CAAC,CAAA;gBACxB,IAAI,MAAM,CAAC,KAAK,CAAC,KAAK,KAAK,WAAW,EAAE,CAAC;oBACvC,WAAW,IAAI,CAAC,CAAA;gBAClB,CAAC;gBACD,MAAK;YACP,KAAK,WAAW;gBACd,oBAAoB,IAAI,CAAC,CAAA;gBACzB,mBAAmB,GAAG,MAAM,CAAC,UAAU,CAAA;gBACvC,IAAI,oBAAoB,IAAI,YAAY,EAAE,CAAC;oBACzC,MAAM,IAAI,eAAe,CACvB,6CAA6C,MAAM,CAAC,UAAU,6BAA6B;wBACzF,CAAC,mBAAmB;4BAClB,CAAC,CAAC,UAAU,mBAAmB,GAAG;4BAClC,CAAC,CAAC,EAAE,CAAC;wBACP,gDAAgD,EAClD;wBACE,MAAM,EAAE,mBAAmB;wBAC3B,IAAI,EAAE,aAAa;wBACnB,IAAI,EAAE,MAAM,CAAC,KAAK;qBACnB,CACF,CAAA;gBACH,CAAC;gBACD,MAAK;YACP,KAAK,QAAQ;gBACX,MAAM,IAAI,eAAe,CAAC,mCAAmC,EAAE;oBAC7D,MAAM,EAAE,GAAG;oBACX,IAAI,EAAE,eAAe;oBACrB,IAAI,EAAE,MAAM,CAAC,KAAK;iBACnB,CAAC,CAAA;YACJ,KAAK,SAAS;gBACZ,MAAM,IAAI,eAAe,CACvB,uDAAuD,EACvD,EAAE,MAAM,EAAE,GAAG,EAAE,IAAI,EAAE,eAAe,EAAE,IAAI,EAAE,MAAM,CAAC,KAAK,EAAE,CAC3D,CAAA;YACH,OAAO,CAAC,CAAC,CAAC;gBACR,MAAM,WAAW,GAAU,MAAM,CAAA;gBACjC,MAAM,IAAI,eAAe,CACvB,2BAA2B,IAAI,CAAC,SAAS,CAAC,WAAW,CAAC,EAAE,EACxD;oBACE,MAAM,EAAE,CAAC;oBACT,IAAI,EAAE,YAAY;iBACnB,CACF,CAAA;YACH,CAAC;QACH,CAAC;IACH,CAAC;IAED,MAAM,IAAI,eAAe,CACvB,2EAA2E,EAC3E,EAAE,MAAM,EAAE,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,CAC/B,CAAA;AACH,CAAC"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { clearOneCredentials, credentialsPath, getOneAccessToken, readCredentials, writeOneCredentials, } from './store.js';
|
|
2
|
+
export { DeviceAuthError, pollDeviceToken, requestDeviceCode, waitForDeviceAuthorization, } from './device-auth.js';
|
|
3
|
+
export type { CredentialsFile, DeviceCodeResponse, DeviceTokenSuccess, OneCredentials, StoredUser, } from './types.js';
|
|
4
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/auth/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,mBAAmB,EACnB,eAAe,EACf,iBAAiB,EACjB,eAAe,EACf,mBAAmB,GACpB,MAAM,YAAY,CAAA;AACnB,OAAO,EACL,eAAe,EACf,eAAe,EACf,iBAAiB,EACjB,0BAA0B,GAC3B,MAAM,kBAAkB,CAAA;AACzB,YAAY,EACV,eAAe,EACf,kBAAkB,EAClB,kBAAkB,EAClB,cAAc,EACd,UAAU,GACX,MAAM,YAAY,CAAA"}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export { clearOneCredentials, credentialsPath, getOneAccessToken, readCredentials, writeOneCredentials, } from './store.js';
|
|
2
|
+
export { DeviceAuthError, pollDeviceToken, requestDeviceCode, waitForDeviceAuthorization, } from './device-auth.js';
|
|
3
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/auth/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,mBAAmB,EACnB,eAAe,EACf,iBAAiB,EACjB,eAAe,EACf,mBAAmB,GACpB,MAAM,YAAY,CAAA;AACnB,OAAO,EACL,eAAe,EACf,eAAe,EACf,iBAAiB,EACjB,0BAA0B,GAC3B,MAAM,kBAAkB,CAAA"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { CliConfig } from '../lib/config.js';
|
|
2
|
+
import type { CredentialsFile, OneCredentials } from './types.js';
|
|
3
|
+
/**
|
|
4
|
+
* Resolve the credentials file path and reject path traversal outside configDir.
|
|
5
|
+
* Filename is a fixed constant — only configDir varies (from env / XDG).
|
|
6
|
+
*/
|
|
7
|
+
export declare function credentialsPath(config: Pick<CliConfig, 'configDir'>): string;
|
|
8
|
+
export declare function readCredentials(config: Pick<CliConfig, 'configDir'>): Promise<CredentialsFile>;
|
|
9
|
+
export declare function writeOneCredentials(config: Pick<CliConfig, 'configDir'>, one: OneCredentials): Promise<void>;
|
|
10
|
+
export declare function clearOneCredentials(config: Pick<CliConfig, 'configDir'>): Promise<boolean>;
|
|
11
|
+
export declare function getOneAccessToken(config: Pick<CliConfig, 'configDir'>): Promise<string | null>;
|
|
12
|
+
//# sourceMappingURL=store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../src/auth/store.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAA;AACjD,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,YAAY,CAAA;AAMjE;;;GAGG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,IAAI,CAAC,SAAS,EAAE,WAAW,CAAC,GAAG,MAAM,CAQ5E;AAED,wBAAsB,eAAe,CACnC,MAAM,EAAE,IAAI,CAAC,SAAS,EAAE,WAAW,CAAC,GACnC,OAAO,CAAC,eAAe,CAAC,CAsB1B;AAED,wBAAsB,mBAAmB,CACvC,MAAM,EAAE,IAAI,CAAC,SAAS,EAAE,WAAW,CAAC,EACpC,GAAG,EAAE,cAAc,GAClB,OAAO,CAAC,IAAI,CAAC,CAUf;AAED,wBAAsB,mBAAmB,CACvC,MAAM,EAAE,IAAI,CAAC,SAAS,EAAE,WAAW,CAAC,GACnC,OAAO,CAAC,OAAO,CAAC,CAmBlB;AAED,wBAAsB,iBAAiB,CACrC,MAAM,EAAE,IAAI,CAAC,SAAS,EAAE,WAAW,CAAC,GACnC,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAWxB"}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { mkdir, readFile, rm, writeFile } from 'node:fs/promises';
|
|
2
|
+
import { basename, resolve, sep } from 'node:path';
|
|
3
|
+
const FILE_NAME = 'credentials.json';
|
|
4
|
+
const MODE_DIR = 0o700;
|
|
5
|
+
const MODE_FILE = 0o600;
|
|
6
|
+
/**
|
|
7
|
+
* Resolve the credentials file path and reject path traversal outside configDir.
|
|
8
|
+
* Filename is a fixed constant — only configDir varies (from env / XDG).
|
|
9
|
+
*/
|
|
10
|
+
export function credentialsPath(config) {
|
|
11
|
+
const configDir = resolve(config.configDir);
|
|
12
|
+
const path = resolve(configDir, FILE_NAME);
|
|
13
|
+
const allowedPrefix = configDir.endsWith(sep) ? configDir : `${configDir}${sep}`;
|
|
14
|
+
if (!path.startsWith(allowedPrefix) || basename(path) !== FILE_NAME) {
|
|
15
|
+
throw new Error('Refusing to read credentials outside the config directory.');
|
|
16
|
+
}
|
|
17
|
+
return path;
|
|
18
|
+
}
|
|
19
|
+
export async function readCredentials(config) {
|
|
20
|
+
const path = credentialsPath(config);
|
|
21
|
+
try {
|
|
22
|
+
// Path is confined to configDir + fixed FILE_NAME by credentialsPath().
|
|
23
|
+
// no-dd-sa:typescript-node-security/detect-non-literal-fs-filename — validated credentials path under configDir
|
|
24
|
+
const raw = await readFile(path, 'utf8');
|
|
25
|
+
const parsed = JSON.parse(raw);
|
|
26
|
+
if (parsed?.version !== 1) {
|
|
27
|
+
return { version: 1, one: null };
|
|
28
|
+
}
|
|
29
|
+
return parsed;
|
|
30
|
+
}
|
|
31
|
+
catch (error) {
|
|
32
|
+
if (error &&
|
|
33
|
+
typeof error === 'object' &&
|
|
34
|
+
'code' in error &&
|
|
35
|
+
error.code === 'ENOENT') {
|
|
36
|
+
return { version: 1, one: null };
|
|
37
|
+
}
|
|
38
|
+
throw error;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
export async function writeOneCredentials(config, one) {
|
|
42
|
+
const configDir = resolve(config.configDir);
|
|
43
|
+
await mkdir(configDir, { recursive: true, mode: MODE_DIR });
|
|
44
|
+
const next = { version: 1, one };
|
|
45
|
+
const path = credentialsPath(config);
|
|
46
|
+
// no-dd-sa:typescript-node-security/detect-non-literal-fs-filename — validated credentials path under configDir
|
|
47
|
+
await writeFile(path, `${JSON.stringify(next, null, 2)}\n`, {
|
|
48
|
+
encoding: 'utf8',
|
|
49
|
+
mode: MODE_FILE,
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
export async function clearOneCredentials(config) {
|
|
53
|
+
const current = await readCredentials(config);
|
|
54
|
+
if (!current.one)
|
|
55
|
+
return false;
|
|
56
|
+
const path = credentialsPath(config);
|
|
57
|
+
try {
|
|
58
|
+
// no-dd-sa:typescript-node-security/detect-non-literal-fs-filename — validated credentials path under configDir
|
|
59
|
+
await rm(path);
|
|
60
|
+
}
|
|
61
|
+
catch (error) {
|
|
62
|
+
if (error &&
|
|
63
|
+
typeof error === 'object' &&
|
|
64
|
+
'code' in error &&
|
|
65
|
+
error.code === 'ENOENT') {
|
|
66
|
+
return false;
|
|
67
|
+
}
|
|
68
|
+
throw error;
|
|
69
|
+
}
|
|
70
|
+
return true;
|
|
71
|
+
}
|
|
72
|
+
export async function getOneAccessToken(config) {
|
|
73
|
+
const credentials = await readCredentials(config);
|
|
74
|
+
const token = credentials.one?.accessToken;
|
|
75
|
+
if (!token)
|
|
76
|
+
return null;
|
|
77
|
+
if (credentials.one?.expiresAt) {
|
|
78
|
+
const expires = Date.parse(credentials.one.expiresAt);
|
|
79
|
+
if (Number.isFinite(expires) && expires <= Date.now()) {
|
|
80
|
+
return null;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return token;
|
|
84
|
+
}
|
|
85
|
+
//# sourceMappingURL=store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"store.js","sourceRoot":"","sources":["../../src/auth/store.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Credential shapes for Vigilbase One CLI auth.
|
|
3
|
+
*
|
|
4
|
+
* Intended to store tokens from Better Auth's device authorization grant
|
|
5
|
+
* (RFC 8628): https://better-auth.com/docs/plugins/device-authorization
|
|
6
|
+
*/
|
|
7
|
+
export type StoredUser = {
|
|
8
|
+
id?: string;
|
|
9
|
+
email?: string;
|
|
10
|
+
name?: string;
|
|
11
|
+
};
|
|
12
|
+
export type OneCredentials = {
|
|
13
|
+
accessToken: string;
|
|
14
|
+
refreshToken?: string;
|
|
15
|
+
tokenType?: string;
|
|
16
|
+
scope?: string;
|
|
17
|
+
/** ISO timestamp when the access token expires, if known. */
|
|
18
|
+
expiresAt?: string;
|
|
19
|
+
user?: StoredUser;
|
|
20
|
+
/** When these credentials were written. */
|
|
21
|
+
obtainedAt: string;
|
|
22
|
+
};
|
|
23
|
+
export type CredentialsFile = {
|
|
24
|
+
version: 1;
|
|
25
|
+
one?: OneCredentials | null;
|
|
26
|
+
};
|
|
27
|
+
export type DeviceCodeResponse = {
|
|
28
|
+
device_code: string;
|
|
29
|
+
user_code: string;
|
|
30
|
+
verification_uri: string;
|
|
31
|
+
verification_uri_complete?: string;
|
|
32
|
+
expires_in: number;
|
|
33
|
+
interval?: number;
|
|
34
|
+
};
|
|
35
|
+
export type DeviceTokenSuccess = {
|
|
36
|
+
access_token: string;
|
|
37
|
+
token_type?: string;
|
|
38
|
+
expires_in?: number;
|
|
39
|
+
refresh_token?: string;
|
|
40
|
+
scope?: string;
|
|
41
|
+
};
|
|
42
|
+
export type DeviceTokenPendingError = {
|
|
43
|
+
error: 'authorization_pending' | 'slow_down' | 'access_denied' | 'expired_token' | string;
|
|
44
|
+
error_description?: string;
|
|
45
|
+
};
|
|
46
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/auth/types.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,MAAM,MAAM,UAAU,GAAG;IACvB,EAAE,CAAC,EAAE,MAAM,CAAA;IACX,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,IAAI,CAAC,EAAE,MAAM,CAAA;CACd,CAAA;AAED,MAAM,MAAM,cAAc,GAAG;IAC3B,WAAW,EAAE,MAAM,CAAA;IACnB,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,6DAA6D;IAC7D,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,IAAI,CAAC,EAAE,UAAU,CAAA;IACjB,2CAA2C;IAC3C,UAAU,EAAE,MAAM,CAAA;CACnB,CAAA;AAED,MAAM,MAAM,eAAe,GAAG;IAC5B,OAAO,EAAE,CAAC,CAAA;IACV,GAAG,CAAC,EAAE,cAAc,GAAG,IAAI,CAAA;CAC5B,CAAA;AAED,MAAM,MAAM,kBAAkB,GAAG;IAC/B,WAAW,EAAE,MAAM,CAAA;IACnB,SAAS,EAAE,MAAM,CAAA;IACjB,gBAAgB,EAAE,MAAM,CAAA;IACxB,yBAAyB,CAAC,EAAE,MAAM,CAAA;IAClC,UAAU,EAAE,MAAM,CAAA;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAA;CAClB,CAAA;AAED,MAAM,MAAM,kBAAkB,GAAG;IAC/B,YAAY,EAAE,MAAM,CAAA;IACpB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,KAAK,CAAC,EAAE,MAAM,CAAA;CACf,CAAA;AAED,MAAM,MAAM,uBAAuB,GAAG;IACpC,KAAK,EACD,uBAAuB,GACvB,WAAW,GACX,eAAe,GACf,eAAe,GACf,MAAM,CAAA;IACV,iBAAiB,CAAC,EAAE,MAAM,CAAA;CAC3B,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/auth/types.ts"],"names":[],"mappings":"AAAA;;;;;GAKG"}
|
package/dist/bin.d.ts
ADDED