@mycadet/cli 0.12.6-dev.202610091338.ee7f3e3 → 0.12.7

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.
@@ -5,7 +5,7 @@
5
5
  import crypto from "node:crypto";
6
6
 
7
7
  import { loopbackSafeFetch } from "./loopback-fetch.js";
8
- import { isJsonObject } from "./json";
8
+ import { z } from "zod";
9
9
 
10
10
  const WORKOS_API_BASE_URL = "https://api.workos.com";
11
11
  const PROVIDER_ID = "workos";
@@ -51,13 +51,39 @@ export function buildAuthorizeUrl(options: AuthorizeUrlOptions): string {
51
51
  return url.toString();
52
52
  }
53
53
 
54
- interface HeadlessProviderEntry {
55
- id: string;
56
- name?: string;
57
- client_id?: string;
58
- flows?: string[];
59
- openid_configuration_url?: string;
60
- }
54
+ const HeadlessProviderEntrySchema = z.object({
55
+ id: z.string().optional(),
56
+ name: z.string().optional(),
57
+ client_id: z.string().optional(),
58
+ flows: z.array(z.string()).optional(),
59
+ openid_configuration_url: z.string().optional(),
60
+ });
61
+
62
+ type HeadlessProviderEntry = z.infer<typeof HeadlessProviderEntrySchema>;
63
+
64
+ const WorkosAuthConfigSchema = z.object({
65
+ data: z
66
+ .object({
67
+ socialaccount: z
68
+ .object({
69
+ providers: z.array(HeadlessProviderEntrySchema).optional(),
70
+ })
71
+ .optional(),
72
+ })
73
+ .optional(),
74
+ });
75
+
76
+ const WorkosAccessTokenResponseSchema = z.object({
77
+ access_token: z.string(),
78
+ });
79
+
80
+ const WorkosSessionTokenResponseSchema = z.object({
81
+ meta: z
82
+ .object({
83
+ session_token: z.string(),
84
+ })
85
+ .optional(),
86
+ });
61
87
 
62
88
  /**
63
89
  * Pick the OAuth2 WorkOS provider from the headless config. During the
@@ -84,12 +110,11 @@ export async function fetchWorkosClientId(
84
110
  if (!response.ok) {
85
111
  throw new Error(`Failed to fetch auth config (${response.status})`);
86
112
  }
87
- const body = (await response.json()) as {
88
- data?: { socialaccount?: { providers?: HeadlessProviderEntry[] } };
89
- };
90
- const clientId = selectWorkosClientId(
91
- body.data?.socialaccount?.providers ?? [],
92
- );
113
+ const parsed = WorkosAuthConfigSchema.safeParse(await response.json());
114
+ const providers = parsed.success
115
+ ? (parsed.data.data?.socialaccount?.providers ?? [])
116
+ : [];
117
+ const clientId = selectWorkosClientId(providers);
93
118
  if (!clientId) {
94
119
  throw new Error(
95
120
  "Cadet Cloud does not advertise a token-auth WorkOS provider; cannot start PKCE login.",
@@ -123,15 +148,13 @@ export async function exchangeCodeWithWorkos(options: {
123
148
  `WorkOS code exchange failed (${response.status}): ${body}`,
124
149
  );
125
150
  }
126
- const body: unknown = await response.json();
127
- const accessToken =
128
- isJsonObject(body) && typeof body.access_token === "string"
129
- ? body.access_token
130
- : undefined;
131
- if (!accessToken) {
151
+ const parsed = WorkosAccessTokenResponseSchema.safeParse(
152
+ await response.json(),
153
+ );
154
+ if (!parsed.success) {
132
155
  throw new Error("WorkOS code exchange returned no access token.");
133
156
  }
134
- return accessToken;
157
+ return parsed.data.access_token;
135
158
  }
136
159
 
137
160
  /** Exchange the WorkOS access token for a platform session token. */
@@ -154,10 +177,13 @@ export async function exchangeAccessTokenForSession(
154
177
  const body = await response.text();
155
178
  throw new Error(`Session exchange failed (${response.status}): ${body}`);
156
179
  }
157
- const body: unknown = await response.json();
158
- const meta = isJsonObject(body) && isJsonObject(body.meta) ? body.meta : undefined;
159
- const sessionToken = meta?.session_token;
160
- if (typeof sessionToken !== "string" || !sessionToken) {
180
+ const parsed = WorkosSessionTokenResponseSchema.safeParse(
181
+ await response.json(),
182
+ );
183
+ const sessionToken = parsed.success
184
+ ? parsed.data.meta?.session_token
185
+ : undefined;
186
+ if (!sessionToken) {
161
187
  throw new Error("Session exchange returned no session token.");
162
188
  }
163
189
  return sessionToken;
package/src/lib/json.ts DELETED
@@ -1,6 +0,0 @@
1
- /** Narrow unknown JSON data to a string-keyed record, without assertions. */
2
- export function isJsonObject(
3
- value: unknown,
4
- ): value is { [key: string]: unknown } {
5
- return typeof value === "object" && value !== null && !Array.isArray(value);
6
- }