@faable/faable 3.2.0 → 3.3.1

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/dist/api/auth.js CHANGED
@@ -1,8 +1,41 @@
1
- import axios from 'axios';
1
+ import { FaableAuthApi, authBearer } from '@faable/auth-sdk';
2
2
  import os from 'os';
3
+ import { version } from '../config.js';
3
4
 
4
- const api = axios.create({ baseURL: "https://faable.auth.faable.link" });
5
+ // The CLI talks to the Faable tenant's auth server through the same SDK the
6
+ // rest of the platform uses. It used to be a bare axios instance, which meant
7
+ // every call landed in the tenant's traffic as "not identified" — the SDK
8
+ // stamps `x-faable-client`, and the value below makes it say who this really
9
+ // is instead of `auth-sdk`.
10
+ //
11
+ // Two clients, because there are two kinds of call:
12
+ // - anonymous: the device flow and the refresh grant. A public client, no
13
+ // secret, no token yet. POSTs are never retried by the fetcher, which is
14
+ // what the device polling needs — every `authorization_pending` is an
15
+ // error the loop expects, not one to replay.
16
+ // - bearer: `/me` and everything `faable auth` does, with the token the user
17
+ // already holds. The strategy asks for the token on every request, so
18
+ // whatever `loadLiveCredentials` refreshed is what goes on the wire.
19
+ const AUTH_DOMAIN = "https://faable.auth.faable.link";
5
20
  const CLIENT_ID = "c879023b-e34f-4b0c-a262-210e556bc2e4";
21
+ // `<name>/<version>`. No commit: the CLI has no release SHA constant, and a
22
+ // dev build (`0.0.0-development`) is identifiable as such by the version
23
+ // alone. auth caps this segment at 32 chars.
24
+ const CLI_CLIENT = `faable-cli/${version}`;
25
+ // No `x-faable-instance`: the SDK's default is `${HOSTNAME}:${pid}`, which on
26
+ // a laptop is the user's machine name in our audit log. An empty string
27
+ // suppresses the header (the helper drops falsy parts).
28
+ const clientInfo = { client: CLI_CLIENT, instance: "" };
29
+ const createAnonymousAuthApi = ({ domain = AUTH_DOMAIN, fetcher, } = {}) => FaableAuthApi.create({ domain, clientInfo, ...(fetcher ? { fetcher } : {}) });
30
+ const createBearerAuthApi = (token, { domain = AUTH_DOMAIN, account, fetcher } = {}) => FaableAuthApi.create({
31
+ domain,
32
+ ...(account ? { headers: { account_id: account } } : {}),
33
+ authStrategy: authBearer,
34
+ auth: { token },
35
+ clientInfo,
36
+ ...(fetcher ? { fetcher } : {}),
37
+ });
38
+ const anonymous = createAnonymousAuthApi();
6
39
  const PLATFORM_LABELS = {
7
40
  darwin: "macOS",
8
41
  win32: "Windows",
@@ -15,35 +48,33 @@ function deviceName() {
15
48
  return `${os.hostname()} (${platform})`;
16
49
  }
17
50
  async function getDeviceCode() {
18
- const res = await api.post(`/oauth/device/code`, {
51
+ return anonymous.fetcher.post(`/oauth/device/code`, {
19
52
  client_id: CLIENT_ID,
20
53
  scope: "openid email profile offline_access",
21
54
  device_name: deviceName(),
22
55
  });
23
- return res.data;
24
56
  }
25
57
  async function getDeviceToken(device_code) {
26
- const res = await api.post(`/oauth/token`, { device_code, client_id: CLIENT_ID, grant_type: "urn:ietf:params:oauth:grant-type:device_code" });
27
- return res.data;
58
+ return anonymous.fetcher.post(`/oauth/token`, {
59
+ device_code,
60
+ client_id: CLIENT_ID,
61
+ grant_type: "urn:ietf:params:oauth:grant-type:device_code",
62
+ });
28
63
  }
29
64
  // Validate a device-flow access token against the Faable Auth server. The token
30
65
  // is issued by the auth server, so it must be introspected there — NOT against
31
66
  // the deploy API (api.faable.com), which has no /me route and would 404.
32
67
  async function getMe(access_token) {
33
- const res = await api.get(`/me`, {
34
- headers: { Authorization: `Bearer ${access_token}` },
35
- });
36
- return res.data;
68
+ return createBearerAuthApi(access_token).fetcher.get(`/me`);
37
69
  }
38
70
  // Exchange a refresh token for a fresh access token (and a rotated refresh
39
71
  // token). The CLI is a public client, so no client_secret is required.
40
72
  async function refreshToken(refresh_token) {
41
- const res = await api.post(`/oauth/token`, {
73
+ return anonymous.fetcher.post(`/oauth/token`, {
42
74
  grant_type: "refresh_token",
43
75
  client_id: CLIENT_ID,
44
76
  refresh_token,
45
77
  });
46
- return res.data;
47
78
  }
48
79
 
49
- export { getDeviceCode, getDeviceToken, getMe, refreshToken };
80
+ export { AUTH_DOMAIN, CLIENT_ID, CLI_CLIENT, createAnonymousAuthApi, createBearerAuthApi, getDeviceCode, getDeviceToken, getMe, refreshToken };
@@ -1,6 +1,6 @@
1
- import { FaableAuthApi } from '@faable/auth-sdk';
2
1
  import { CredentialsStore } from '../lib/CredentialsStore.js';
3
2
  import { log } from '../log.js';
3
+ import { createBearerAuthApi } from './auth.js';
4
4
  import { loadLiveCredentials } from './session.js';
5
5
 
6
6
  // Default tenant host. `faable auth` is customer-facing: a customer targets
@@ -31,13 +31,10 @@ const requireAuthAdmin = async (opts = {}) => {
31
31
  }
32
32
  const domain = opts.authUrl || process.env.FAABLE_AUTH_URL || DEFAULT_AUTH_URL;
33
33
  const account = opts.account || process.env.FAABLE_AUTH_ACCOUNT;
34
- return new FaableAuthApi({
35
- domain,
36
- ...(account ? { headers: { account_id: account } } : {}),
37
- // Static bearer: passing no `auth` keeps sdk-base from attaching any token
38
- // strategy, so this header is used verbatim on every request.
39
- fetcher: { headers: { Authorization: `Bearer ${token}` } }
40
- });
34
+ // The session's bearer as a strategy — with the CLI's own identity on the
35
+ // wire instead of `auth-sdk` — scoped to the target tenant when one was
36
+ // named. The server decides whether this token may manage that tenant.
37
+ return createBearerAuthApi(token, { domain, account });
41
38
  };
42
39
  // Translate raw management-API failures into actionable CLI errors. Everything
43
40
  // else is rethrown untouched (FaableApiError messages already carry
@@ -49,7 +49,10 @@ const loadLiveCredentials = async (store = new CredentialsStore()) => {
49
49
  // token endpoint's RFC 6749 error body. "Run `faable login` again"
50
50
  // would be a lie here — login is denied too — so say what actually
51
51
  // happened and stop instead of letting a generic 401 mislead.
52
- const body = e?.response?.data;
52
+ // The SDK's error keeps the HTTP body under `response.data`, like the
53
+ // axios error it replaced.
54
+ const body = e
55
+ ?.response?.data;
53
56
  if (body?.error_code === "user_suspended") {
54
57
  log.error("❌ Your account has been suspended. Contact support@faable.com.");
55
58
  process.exit(1);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@faable/faable",
3
- "version": "3.2.0",
3
+ "version": "3.3.1",
4
4
  "main": "dist/index.js",
5
5
  "license": "MIT",
6
6
  "author": "Marc Pomar <marc@faable.com>",
@@ -27,7 +27,7 @@
27
27
  ],
28
28
  "dependencies": {
29
29
  "@actions/core": "^3.0.0",
30
- "@faable/auth-sdk": "^2.5.39",
30
+ "@faable/auth-sdk": "^2.6.3",
31
31
  "axios": "^1.18.1",
32
32
  "fs-extra": "^11.3.2",
33
33
  "open": "^11.0.0",