@deque/axe-auth 1.1.0-next.816dc5a1 → 1.1.0-next.8e9934f2

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.
@@ -72,7 +72,9 @@ function isLatestBlob(blob) {
72
72
  (b.refreshToken === undefined || typeof b.refreshToken === "string") &&
73
73
  typeof b.issuerURL === "string" &&
74
74
  typeof b.clientId === "string" &&
75
- typeof b.allowInsecureIssuer === "boolean");
75
+ typeof b.allowInsecureIssuer === "boolean" &&
76
+ typeof b.walnutURL === "string" &&
77
+ b.walnutURL.length > 0);
76
78
  }
77
79
  function blobToEntry(blob) {
78
80
  const tokens = {
@@ -86,6 +88,7 @@ function blobToEntry(blob) {
86
88
  issuerURL: blob.issuerURL,
87
89
  clientId: blob.clientId,
88
90
  allowInsecureIssuer: blob.allowInsecureIssuer,
91
+ walnutURL: blob.walnutURL,
89
92
  };
90
93
  }
91
94
  function entryToBlob(entry) {
@@ -96,6 +99,7 @@ function entryToBlob(entry) {
96
99
  issuerURL: entry.issuerURL,
97
100
  clientId: entry.clientId,
98
101
  allowInsecureIssuer: entry.allowInsecureIssuer,
102
+ walnutURL: entry.walnutURL,
99
103
  };
100
104
  if (entry.tokens.refreshToken)
101
105
  blob.refreshToken = entry.tokens.refreshToken;
@@ -13,9 +13,11 @@ flowchart TB
13
13
  browser[System browser]
14
14
  callback[Loopback callback server<br/>127.0.0.1:ephemeral]
15
15
  keychain[(OS keychain)]
16
+ axe[axe server]
16
17
  keycloak[Customer Keycloak]
17
18
 
18
19
  user -- "axe-auth login / token / logout" --> cli
20
+ cli -- "GET /api/sso-config (login only)" --> axe
19
21
  cli -- "spawns" --> callback
20
22
  cli -- "opens" --> browser
21
23
  browser -- "authorize redirect<br/>(state, PKCE challenge)" --> keycloak
@@ -23,7 +25,7 @@ flowchart TB
23
25
  browser -- "GET /callback?code=...&state=..." --> callback
24
26
  callback -- "code, state" --> cli
25
27
  cli <-- "OIDC discovery, token exchange,<br/>refresh, revoke (HTTPS)" --> keycloak
26
- cli <-- "tokens + issuer/client<br/>(versioned blob)" --> keychain
28
+ cli <-- "tokens + issuer/client/walnutURL<br/>(versioned blob)" --> keychain
27
29
  cli -- "access token (stdout)" --> user
28
30
  ```
29
31
 
@@ -34,7 +36,8 @@ flowchart TB
34
36
  3. **System browser**: the developer's default OS browser (Chrome, Safari, Firefox, etc.). Used only for the user-interactive part of the OAuth Authorization Code flow. Runs on the host, never in a container or sandbox controlled by `axe-auth`.
35
37
  4. **Loopback callback server**: an HTTP listener bound to `127.0.0.1` on an OS-assigned ephemeral port. Spawned by the CLI at the start of `login` and torn down as soon as the OAuth callback fires. Per RFC 8252 §7.3, this is the standard pattern for native-app OAuth.
36
38
  5. **OS keychain**: the platform-native credential store accessed through [`@napi-rs/keyring`](https://www.npmjs.com/package/@napi-rs/keyring) — macOS Keychain, Windows Credential Manager, or Linux Secret Service (GNOME Keyring, KWallet). The CLI writes one entry per machine.
37
- 6. **Customer Keycloak**: the OAuth authorization server for the customer's deployment. Issues access and refresh tokens. Federation between Keycloak and any upstream enterprise IdP (Okta, AAD, etc.) is the customer's concern and out of scope for this document.
39
+ 6. **axe server**: the customer's deployment of the axe API. The CLI hits its `/api/sso-config` endpoint at the start of `login` to discover the Keycloak URL, realm, and OAuth client ID; no other CLI traffic flows through the axe server.
40
+ 7. **Customer Keycloak**: the OAuth authorization server for the customer's deployment. Issues access and refresh tokens. Federation between Keycloak and any upstream enterprise IdP (Okta, AAD, etc.) is the customer's concern and out of scope for this document.
38
41
 
39
42
  `axe-auth` itself does **not** communicate with Deque's API services directly. The access tokens it produces are consumed by downstream tools, most notably the axe MCP server, which presents them to Deque's services as `Authorization: Bearer ...`.
40
43
 
@@ -48,9 +51,12 @@ sequenceDiagram
48
51
  participant CLI as axe-auth CLI
49
52
  participant Browser as System browser
50
53
  participant CB as Loopback callback<br/>(127.0.0.1:port)
54
+ participant Axe as axe server
51
55
  participant KC as Customer Keycloak
52
56
 
53
- User->>CLI: axe-auth login [...]
57
+ User->>CLI: axe-auth login [--server <axe-url>]
58
+ CLI->>Axe: GET /api/sso-config
59
+ Axe-->>CLI: { url, realm, mcpClientId }
54
60
  CLI->>KC: GET /.well-known/openid-configuration
55
61
  KC-->>CLI: { authorization_endpoint, token_endpoint, ... }
56
62
  CLI->>CB: spawn on 127.0.0.1:<ephemeral>
@@ -63,21 +69,22 @@ sequenceDiagram
63
69
  CB-->>CLI: { code, state }
64
70
  CLI->>KC: POST /token (code, code_verifier)
65
71
  KC-->>CLI: { access_token, refresh_token, expires_in }
66
- Note over CLI: save tokens + issuer/client to OS keychain
72
+ Note over CLI: save tokens + issuer/client/walnutURL to OS keychain
67
73
  CLI-->>User: ✓ Authenticated.
68
74
  ```
69
75
 
70
- 1. The developer invokes `axe-auth login` with the authorization-server coordinates (or after a prior login, with no flags the stored entry supplies them).
71
- 2. The CLI fetches the OIDC discovery document at `<issuer>/.well-known/openid-configuration` to learn the authorization, token, and revocation endpoint URLs.
72
- 3. The CLI generates a PKCE `code_verifier` + `code_challenge` (S256) and a random `state` value.
73
- 4. The CLI starts a loopback HTTP listener on `127.0.0.1` at an OS-assigned port.
74
- 5. The CLI opens the developer's system browser to the authorization endpoint with the PKCE challenge, the state, and the loopback `redirect_uri`.
75
- 6. The developer authenticates with Keycloak (typically via the customer's federated SSO).
76
- 7. Keycloak redirects the browser to the loopback `redirect_uri` with an authorization `code` and the original `state`.
77
- 8. The loopback listener validates `state`, captures the `code`, and renders a small success page so the developer knows they can close the tab.
78
- 9. The CLI POSTs `code` + `code_verifier` to Keycloak's token endpoint and receives an `access_token`, `refresh_token`, and `expires_in`.
79
- 10. The CLI persists the resulting `StoredEntry` (tokens plus the issuer/client coordinates that minted them) into the OS keychain.
80
- 11. The CLI prints `✓ Authenticated.` on stdout and exits 0.
76
+ 1. The developer invokes `axe-auth login`, optionally with `--server <axe-url>` (or `AXE_SERVER_URL`); when neither is set the CLI defaults to Deque's SaaS prod axe server.
77
+ 2. The CLI fetches `<axe-server>/api/sso-config` to learn the Keycloak base URL, realm, and OAuth client ID. The axe server returns `mcpClientId: null` when the deployment has not been configured for OAuth-based MCP authentication, and the field is absent on older axe server versions; both cases surface as a clear error before any browser is opened.
78
+ 3. With the discovered coordinates the CLI fetches the OIDC discovery document at `<issuer>/.well-known/openid-configuration` to learn the authorization, token, and revocation endpoint URLs.
79
+ 4. The CLI generates a PKCE `code_verifier` + `code_challenge` (S256) and a random `state` value.
80
+ 5. The CLI starts a loopback HTTP listener on `127.0.0.1` at an OS-assigned port.
81
+ 6. The CLI opens the developer's system browser to the authorization endpoint with the PKCE challenge, the state, and the loopback `redirect_uri`.
82
+ 7. The developer authenticates with Keycloak (typically via the customer's federated SSO).
83
+ 8. Keycloak redirects the browser to the loopback `redirect_uri` with an authorization `code` and the original `state`.
84
+ 9. The loopback listener validates `state`, captures the `code`, and renders a small success page so the developer knows they can close the tab.
85
+ 10. The CLI POSTs `code` + `code_verifier` to Keycloak's token endpoint and receives an `access_token`, `refresh_token`, and `expires_in`.
86
+ 11. The CLI persists the resulting `StoredEntry` (tokens plus the issuer/client coordinates that minted them, plus the originating axe server URL) into the OS keychain.
87
+ 12. The CLI prints `✓ Authenticated.` on stdout and exits 0.
81
88
 
82
89
  ### `axe-auth token`
83
90
 
@@ -173,14 +180,16 @@ sequenceDiagram
173
180
  "accessToken": "...",
174
181
  "refreshToken": "...",
175
182
  "expiresAt": 1714426800000,
176
- "issuerURL": "https://auth.customer.example.com/realms/customer",
177
- "clientId": "axe-auth",
178
- "allowInsecureIssuer": false
183
+ "issuerURL": "https://auth.customer.example.com/auth/realms/customer",
184
+ "clientId": "axe-auth-cli",
185
+ "allowInsecureIssuer": false,
186
+ "walnutURL": "https://axe.customer.example.com"
179
187
  }
180
188
  ```
181
189
 
182
190
  - **Tokens** (`accessToken`, `refreshToken`, `expiresAt`): the OAuth token set returned by Keycloak. `refreshToken` is omitted if the granted scopes did not include `offline_access`.
183
191
  - **Issuer / client coordinates** (`issuerURL`, `clientId`, `allowInsecureIssuer`): the values the tokens were minted against. Persisting them lets `token` and `logout` operate flag-free after first login: the CLI resolves the right discovery URL, token endpoint, and revocation endpoint from the stored values, with no separate "default issuer" pointer to drift out of sync with the tokens themselves.
192
+ - **`walnutURL`**: the originating axe server URL that the SSO discovery used to resolve the OAuth coordinates. Persisted so future verbs can re-discover `/api/sso-config` without user-supplied flags.
184
193
  - **Schema version** (`v`): incremented when the blob shape changes incompatibly. A mismatch surfaces as `version-mismatch` from `KeyringTokenStore.load()`, and the CLI prompts re-authentication rather than guessing at unknown shapes.
185
194
 
186
195
  No other persistent state exists. There is no filesystem cache of OIDC discovery documents (each `login` and `logout` re-fetches), no separate config file, and no logs written to disk by default.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@deque/axe-auth",
3
- "version": "1.1.0-next.816dc5a1",
3
+ "version": "1.1.0-next.8e9934f2",
4
4
  "description": "CLI authentication utility for Deque services",
5
5
  "license": "SEE LICENSE IN LICENSE",
6
6
  "type": "commonjs",
@@ -12,7 +12,8 @@
12
12
  "files": [
13
13
  "dist",
14
14
  "!dist/**/*.test.*",
15
- "docs"
15
+ "docs",
16
+ "credits.json"
16
17
  ],
17
18
  "publishConfig": {
18
19
  "access": "public",