instant-cli 1.0.21-branch-cli-codex.25196009658.1 → 1.0.21-branch-cli-codex-update.25196209406.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/.turbo/turbo-build.log +1 -1
- package/__tests__/authClientAddGoogle.test.ts +4 -4
- package/__tests__/authClientUpdate.test.ts +528 -0
- package/dist/commands/auth/client/add.d.ts +1 -2
- package/dist/commands/auth/client/add.d.ts.map +1 -1
- package/dist/commands/auth/client/add.js +57 -260
- package/dist/commands/auth/client/add.js.map +1 -1
- package/dist/commands/auth/client/delete.d.ts +1 -2
- package/dist/commands/auth/client/delete.d.ts.map +1 -1
- package/dist/commands/auth/client/delete.js +8 -18
- package/dist/commands/auth/client/delete.js.map +1 -1
- package/dist/commands/auth/client/shared.d.ts +79 -0
- package/dist/commands/auth/client/shared.d.ts.map +1 -0
- package/dist/commands/auth/client/shared.js +171 -0
- package/dist/commands/auth/client/shared.js.map +1 -0
- package/dist/commands/auth/client/update.d.ts +8 -0
- package/dist/commands/auth/client/update.d.ts.map +1 -0
- package/dist/commands/auth/client/update.js +482 -0
- package/dist/commands/auth/client/update.js.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +46 -0
- package/dist/index.js.map +1 -1
- package/dist/lib/oauth.d.ts +75 -0
- package/dist/lib/oauth.d.ts.map +1 -1
- package/dist/lib/oauth.js +40 -0
- package/dist/lib/oauth.js.map +1 -1
- package/package.json +4 -4
- package/src/commands/auth/client/add.ts +75 -306
- package/src/commands/auth/client/delete.ts +8 -20
- package/src/commands/auth/client/shared.ts +234 -0
- package/src/commands/auth/client/update.ts +786 -0
- package/src/index.ts +59 -0
- package/src/lib/oauth.ts +60 -0
package/.turbo/turbo-build.log
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
|
|
2
|
-
> instant-cli@1.0.21-branch-cli-codex.
|
|
2
|
+
> instant-cli@1.0.21-branch-cli-codex-update.25196209406.1 build /home/runner/work/instant/instant/client/packages/cli
|
|
3
3
|
> rm -rf dist; tsc -p tsconfig.build.json
|
|
4
4
|
|
|
@@ -264,10 +264,11 @@ describe('web: dev credentials', () => {
|
|
|
264
264
|
const output = logs.join('\n');
|
|
265
265
|
expect(output).toContain('Credentials: Instant dev credentials');
|
|
266
266
|
expect(output).toContain('No Google Console setup required');
|
|
267
|
+
expect(output).toContain('Ready for production? Run:');
|
|
268
|
+
expect(output).not.toContain('For production');
|
|
267
269
|
expect(output).toContain(
|
|
268
|
-
'
|
|
270
|
+
'instant-cli auth client update --name google-web',
|
|
269
271
|
);
|
|
270
|
-
expect(output).not.toContain('instant-cli auth client update');
|
|
271
272
|
expect(output).not.toContain('Add this redirect URI in Google Console');
|
|
272
273
|
});
|
|
273
274
|
|
|
@@ -287,9 +288,8 @@ describe('web: dev credentials', () => {
|
|
|
287
288
|
expect(output).toContain('Credentials: Instant dev credentials');
|
|
288
289
|
expect(output).toContain('No Google Console setup required');
|
|
289
290
|
expect(output).toContain(
|
|
290
|
-
'
|
|
291
|
+
'instant-cli auth client update --name google-web',
|
|
291
292
|
);
|
|
292
|
-
expect(output).not.toContain('instant-cli auth client update');
|
|
293
293
|
expect(output).not.toContain('Add this redirect URI in Google Console');
|
|
294
294
|
});
|
|
295
295
|
|
|
@@ -0,0 +1,528 @@
|
|
|
1
|
+
import { test, expect, describe, vi, beforeEach } from 'vitest';
|
|
2
|
+
import { Effect, Layer, Logger } from 'effect';
|
|
3
|
+
import { NodeContext } from '@effect/platform-node';
|
|
4
|
+
import { GlobalOpts } from '../src/context/globalOpts.ts';
|
|
5
|
+
import { CurrentApp } from '../src/context/currentApp.ts';
|
|
6
|
+
import { InstantHttpAuthed } from '../src/lib/http.ts';
|
|
7
|
+
import { BadArgsError } from '../src/errors.ts';
|
|
8
|
+
|
|
9
|
+
// Prevent src/index.ts side-effect (program.parse) from running.
|
|
10
|
+
vi.mock('../src/index.ts', () => ({}));
|
|
11
|
+
|
|
12
|
+
let prompts: any[] = [];
|
|
13
|
+
let mockPromptReturn: any = '';
|
|
14
|
+
|
|
15
|
+
vi.mock('../src/ui/lib.ts', async (importOriginal) => {
|
|
16
|
+
const orig: any = await importOriginal();
|
|
17
|
+
return {
|
|
18
|
+
...orig,
|
|
19
|
+
renderUnwrap: (prompt: any) => {
|
|
20
|
+
prompts.push(prompt);
|
|
21
|
+
const value = Array.isArray(mockPromptReturn)
|
|
22
|
+
? mockPromptReturn.shift()
|
|
23
|
+
: mockPromptReturn;
|
|
24
|
+
return Promise.resolve(value);
|
|
25
|
+
},
|
|
26
|
+
};
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
let updatedClients: any[] = [];
|
|
30
|
+
let mockClients: any[] = [];
|
|
31
|
+
const providers = [
|
|
32
|
+
{ id: 'prov-google', provider_name: 'google' },
|
|
33
|
+
{ id: 'prov-github', provider_name: 'github' },
|
|
34
|
+
{ id: 'prov-linkedin', provider_name: 'linkedin' },
|
|
35
|
+
{ id: 'prov-apple', provider_name: 'apple' },
|
|
36
|
+
{ id: 'prov-clerk', provider_name: 'clerk' },
|
|
37
|
+
{ id: 'prov-firebase', provider_name: 'firebase' },
|
|
38
|
+
];
|
|
39
|
+
|
|
40
|
+
vi.mock('../src/lib/oauth.ts', () => ({
|
|
41
|
+
getAppsAuth: () =>
|
|
42
|
+
Effect.succeed({
|
|
43
|
+
oauth_service_providers: providers,
|
|
44
|
+
oauth_clients: mockClients,
|
|
45
|
+
}),
|
|
46
|
+
findClientByIdOrName: ({ id, name }: { id?: string; name?: string }) =>
|
|
47
|
+
Effect.gen(function* () {
|
|
48
|
+
if (id && name) {
|
|
49
|
+
return yield* BadArgsError.make({
|
|
50
|
+
message: 'Cannot specify both --id and --name',
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
if (!id && !name) {
|
|
54
|
+
return yield* BadArgsError.make({
|
|
55
|
+
message: 'Must specify --id or --name',
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
const client = id
|
|
59
|
+
? mockClients.find((entry) => entry.id === id)
|
|
60
|
+
: mockClients.find((entry) => entry.client_name === name);
|
|
61
|
+
if (!client) {
|
|
62
|
+
return yield* BadArgsError.make({
|
|
63
|
+
message: `OAuth client not found: ${id ?? name}`,
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
return {
|
|
67
|
+
client,
|
|
68
|
+
auth: {
|
|
69
|
+
oauth_service_providers: providers,
|
|
70
|
+
oauth_clients: mockClients,
|
|
71
|
+
},
|
|
72
|
+
};
|
|
73
|
+
}),
|
|
74
|
+
updateOAuthClient: (params: any) => {
|
|
75
|
+
updatedClients.push(params);
|
|
76
|
+
const client = mockClients.find((c) => c.id === params.oauthClientId);
|
|
77
|
+
return Effect.succeed({
|
|
78
|
+
client: {
|
|
79
|
+
id: params.oauthClientId,
|
|
80
|
+
client_name: client?.client_name ?? 'unknown',
|
|
81
|
+
},
|
|
82
|
+
});
|
|
83
|
+
},
|
|
84
|
+
}));
|
|
85
|
+
|
|
86
|
+
const { authClientUpdateCmd } = await import(
|
|
87
|
+
'../src/commands/auth/client/update.ts'
|
|
88
|
+
);
|
|
89
|
+
|
|
90
|
+
let logs: string[] = [];
|
|
91
|
+
|
|
92
|
+
const run = (flags: Record<string, any>, { yes }: { yes: boolean }) =>
|
|
93
|
+
Effect.runPromise(
|
|
94
|
+
authClientUpdateCmd(flags as any).pipe(
|
|
95
|
+
Effect.provide(
|
|
96
|
+
Layer.mergeAll(
|
|
97
|
+
Layer.succeed(GlobalOpts, { yes }),
|
|
98
|
+
Layer.succeed(CurrentApp, { appId: 'test-app', source: 'env' }),
|
|
99
|
+
Layer.succeed(InstantHttpAuthed, {} as any),
|
|
100
|
+
NodeContext.layer,
|
|
101
|
+
Logger.replace(
|
|
102
|
+
Logger.defaultLogger,
|
|
103
|
+
Logger.make(({ message }) => {
|
|
104
|
+
logs.push(String(message));
|
|
105
|
+
}),
|
|
106
|
+
),
|
|
107
|
+
),
|
|
108
|
+
),
|
|
109
|
+
),
|
|
110
|
+
);
|
|
111
|
+
|
|
112
|
+
beforeEach(() => {
|
|
113
|
+
prompts = [];
|
|
114
|
+
updatedClients = [];
|
|
115
|
+
logs = [];
|
|
116
|
+
mockPromptReturn = '';
|
|
117
|
+
mockClients = [
|
|
118
|
+
{
|
|
119
|
+
id: 'google-shared',
|
|
120
|
+
provider_id: 'prov-google',
|
|
121
|
+
client_name: 'google-shared',
|
|
122
|
+
meta: { appType: 'web' },
|
|
123
|
+
use_shared_credentials: true,
|
|
124
|
+
},
|
|
125
|
+
{
|
|
126
|
+
id: 'google-web',
|
|
127
|
+
provider_id: 'prov-google',
|
|
128
|
+
client_name: 'google-web',
|
|
129
|
+
client_id: 'old-google-id',
|
|
130
|
+
redirect_to: 'https://api.instantdb.com/runtime/oauth/callback',
|
|
131
|
+
meta: { appType: 'web' },
|
|
132
|
+
use_shared_credentials: false,
|
|
133
|
+
},
|
|
134
|
+
{
|
|
135
|
+
id: 'google-ios',
|
|
136
|
+
provider_id: 'prov-google',
|
|
137
|
+
client_name: 'google-ios',
|
|
138
|
+
meta: { appType: 'ios' },
|
|
139
|
+
},
|
|
140
|
+
{
|
|
141
|
+
id: 'github',
|
|
142
|
+
provider_id: 'prov-github',
|
|
143
|
+
client_name: 'github',
|
|
144
|
+
client_id: 'old-gh-id',
|
|
145
|
+
},
|
|
146
|
+
{
|
|
147
|
+
id: 'linkedin',
|
|
148
|
+
provider_id: 'prov-linkedin',
|
|
149
|
+
client_name: 'linkedin',
|
|
150
|
+
client_id: 'old-linkedin-id',
|
|
151
|
+
},
|
|
152
|
+
{
|
|
153
|
+
id: 'apple',
|
|
154
|
+
provider_id: 'prov-apple',
|
|
155
|
+
client_name: 'apple',
|
|
156
|
+
client_id: 'old.apple.service',
|
|
157
|
+
meta: {
|
|
158
|
+
teamId: 'OLDTEAM',
|
|
159
|
+
keyId: 'OLDKEY',
|
|
160
|
+
},
|
|
161
|
+
},
|
|
162
|
+
{
|
|
163
|
+
id: 'clerk',
|
|
164
|
+
provider_id: 'prov-clerk',
|
|
165
|
+
client_name: 'clerk',
|
|
166
|
+
},
|
|
167
|
+
{
|
|
168
|
+
id: 'firebase',
|
|
169
|
+
provider_id: 'prov-firebase',
|
|
170
|
+
client_name: 'firebase',
|
|
171
|
+
},
|
|
172
|
+
];
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
describe('google', () => {
|
|
176
|
+
test('upgrades shared dev credentials to custom credentials', async () => {
|
|
177
|
+
await run(
|
|
178
|
+
{
|
|
179
|
+
name: 'google-shared',
|
|
180
|
+
'client-id': 'new-google-id',
|
|
181
|
+
'client-secret': 'new-google-secret',
|
|
182
|
+
},
|
|
183
|
+
{ yes: true },
|
|
184
|
+
);
|
|
185
|
+
|
|
186
|
+
expect(updatedClients).toHaveLength(1);
|
|
187
|
+
expect(updatedClients[0]).toMatchObject({
|
|
188
|
+
oauthClientId: 'google-shared',
|
|
189
|
+
clientId: 'new-google-id',
|
|
190
|
+
clientSecret: 'new-google-secret',
|
|
191
|
+
redirectTo: 'https://api.instantdb.com/runtime/oauth/callback',
|
|
192
|
+
useSharedCredentials: false,
|
|
193
|
+
});
|
|
194
|
+
expect(logs.join('\n')).toContain(
|
|
195
|
+
'This client no longer uses Instant dev credentials.',
|
|
196
|
+
);
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
test('switches custom Google web client back to dev credentials', async () => {
|
|
200
|
+
await run(
|
|
201
|
+
{
|
|
202
|
+
name: 'google-web',
|
|
203
|
+
'dev-credentials': true,
|
|
204
|
+
},
|
|
205
|
+
{ yes: true },
|
|
206
|
+
);
|
|
207
|
+
|
|
208
|
+
expect(updatedClients).toHaveLength(1);
|
|
209
|
+
expect(updatedClients[0]).toMatchObject({
|
|
210
|
+
oauthClientId: 'google-web',
|
|
211
|
+
clientId: null,
|
|
212
|
+
clientSecret: null,
|
|
213
|
+
useSharedCredentials: true,
|
|
214
|
+
redirectTo: null,
|
|
215
|
+
});
|
|
216
|
+
const output = logs.join('\n');
|
|
217
|
+
expect(output).toContain('Credentials: Instant dev credentials');
|
|
218
|
+
expect(output).toContain('Ready for production? Run:');
|
|
219
|
+
expect(output).toContain(
|
|
220
|
+
'instant-cli auth client update --name google-web',
|
|
221
|
+
);
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
test('rejects dev credentials with custom credential flags', async () => {
|
|
225
|
+
await run(
|
|
226
|
+
{
|
|
227
|
+
name: 'google-web',
|
|
228
|
+
'dev-credentials': true,
|
|
229
|
+
'client-id': 'new-google-id',
|
|
230
|
+
},
|
|
231
|
+
{ yes: true },
|
|
232
|
+
);
|
|
233
|
+
|
|
234
|
+
expect(logs.join('\n')).toContain(
|
|
235
|
+
'--dev-credentials cannot be combined with --client-id',
|
|
236
|
+
);
|
|
237
|
+
expect(updatedClients).toHaveLength(0);
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
test('updates redirect URI only', async () => {
|
|
241
|
+
await run(
|
|
242
|
+
{
|
|
243
|
+
name: 'google-web',
|
|
244
|
+
'custom-redirect-uri': 'https://example.com/oauth/callback',
|
|
245
|
+
},
|
|
246
|
+
{ yes: true },
|
|
247
|
+
);
|
|
248
|
+
|
|
249
|
+
expect(updatedClients).toHaveLength(1);
|
|
250
|
+
expect(updatedClients[0]).toMatchObject({
|
|
251
|
+
oauthClientId: 'google-web',
|
|
252
|
+
redirectTo: 'https://example.com/oauth/callback',
|
|
253
|
+
});
|
|
254
|
+
expect(updatedClients[0].clientId).toBeUndefined();
|
|
255
|
+
expect(updatedClients[0].clientSecret).toBeUndefined();
|
|
256
|
+
expect(logs.join('\n')).toContain(
|
|
257
|
+
'Add this redirect URI in Google Console',
|
|
258
|
+
);
|
|
259
|
+
expect(logs.join('\n')).toContain('Your custom redirect must forward to');
|
|
260
|
+
expect(logs.join('\n')).toContain(
|
|
261
|
+
'https://example.com/oauth/callback?test-redirect=true',
|
|
262
|
+
);
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
test('interactive Google web update can select dev credentials', async () => {
|
|
266
|
+
mockPromptReturn = 'dev';
|
|
267
|
+
await run({ name: 'google-web' }, { yes: false });
|
|
268
|
+
|
|
269
|
+
expect(prompts).toHaveLength(1);
|
|
270
|
+
expect((prompts[0] as any).params.promptText).toBe(
|
|
271
|
+
'What do you want to update?',
|
|
272
|
+
);
|
|
273
|
+
expect(updatedClients[0]).toMatchObject({
|
|
274
|
+
oauthClientId: 'google-web',
|
|
275
|
+
useSharedCredentials: true,
|
|
276
|
+
redirectTo: null,
|
|
277
|
+
});
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
test('rejects dev credentials for native Google clients', async () => {
|
|
281
|
+
await run(
|
|
282
|
+
{
|
|
283
|
+
name: 'google-ios',
|
|
284
|
+
'dev-credentials': true,
|
|
285
|
+
},
|
|
286
|
+
{ yes: true },
|
|
287
|
+
);
|
|
288
|
+
|
|
289
|
+
expect(logs.join('\n')).toContain(
|
|
290
|
+
'--dev-credentials is only supported for Google web clients',
|
|
291
|
+
);
|
|
292
|
+
expect(updatedClients).toHaveLength(0);
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
test('interactive native Google update only offers credential rotation', async () => {
|
|
296
|
+
mockPromptReturn = ['custom', 'native-google-id'];
|
|
297
|
+
|
|
298
|
+
await run({ name: 'google-ios' }, { yes: false });
|
|
299
|
+
|
|
300
|
+
expect(prompts).toHaveLength(2);
|
|
301
|
+
expect((prompts[0] as any).params.options.map((o: any) => o.value)).toEqual(
|
|
302
|
+
['custom'],
|
|
303
|
+
);
|
|
304
|
+
expect(updatedClients[0]).toMatchObject({
|
|
305
|
+
oauthClientId: 'google-ios',
|
|
306
|
+
clientId: 'native-google-id',
|
|
307
|
+
});
|
|
308
|
+
expect(updatedClients[0].clientSecret).toBeUndefined();
|
|
309
|
+
});
|
|
310
|
+
});
|
|
311
|
+
|
|
312
|
+
describe('provider credential updates', () => {
|
|
313
|
+
test('updates GitHub client secret without requiring a new client ID', async () => {
|
|
314
|
+
await run(
|
|
315
|
+
{
|
|
316
|
+
name: 'github',
|
|
317
|
+
'client-secret': 'new-gh-secret',
|
|
318
|
+
},
|
|
319
|
+
{ yes: true },
|
|
320
|
+
);
|
|
321
|
+
|
|
322
|
+
expect(updatedClients).toHaveLength(1);
|
|
323
|
+
expect(updatedClients[0]).toMatchObject({
|
|
324
|
+
oauthClientId: 'github',
|
|
325
|
+
clientSecret: 'new-gh-secret',
|
|
326
|
+
});
|
|
327
|
+
expect(updatedClients[0].clientId).toBeUndefined();
|
|
328
|
+
});
|
|
329
|
+
|
|
330
|
+
test('interactive GitHub update can select redirect URI', async () => {
|
|
331
|
+
mockPromptReturn = ['redirect', 'https://example.com/oauth/callback'];
|
|
332
|
+
|
|
333
|
+
await run({ name: 'github' }, { yes: false });
|
|
334
|
+
|
|
335
|
+
expect(prompts).toHaveLength(2);
|
|
336
|
+
expect((prompts[0] as any).params.promptText).toBe(
|
|
337
|
+
'What do you want to update?',
|
|
338
|
+
);
|
|
339
|
+
expect(updatedClients).toHaveLength(1);
|
|
340
|
+
expect(updatedClients[0]).toMatchObject({
|
|
341
|
+
oauthClientId: 'github',
|
|
342
|
+
redirectTo: 'https://example.com/oauth/callback',
|
|
343
|
+
});
|
|
344
|
+
expect(logs.join('\n')).toContain(
|
|
345
|
+
'Add this callback URL in your GitHub OAuth App settings',
|
|
346
|
+
);
|
|
347
|
+
expect(logs.join('\n')).toContain(
|
|
348
|
+
'https://example.com/oauth/callback?test-redirect=true',
|
|
349
|
+
);
|
|
350
|
+
});
|
|
351
|
+
|
|
352
|
+
test('LinkedIn redirect update prints app settings guidance', async () => {
|
|
353
|
+
await run(
|
|
354
|
+
{
|
|
355
|
+
name: 'linkedin',
|
|
356
|
+
'custom-redirect-uri': 'https://example.com/linkedin/callback',
|
|
357
|
+
},
|
|
358
|
+
{ yes: true },
|
|
359
|
+
);
|
|
360
|
+
|
|
361
|
+
expect(updatedClients).toHaveLength(1);
|
|
362
|
+
expect(updatedClients[0]).toMatchObject({
|
|
363
|
+
oauthClientId: 'linkedin',
|
|
364
|
+
redirectTo: 'https://example.com/linkedin/callback',
|
|
365
|
+
});
|
|
366
|
+
expect(logs.join('\n')).toContain(
|
|
367
|
+
'Add this redirect URI in your LinkedIn app settings',
|
|
368
|
+
);
|
|
369
|
+
expect(logs.join('\n')).toContain(
|
|
370
|
+
'https://example.com/linkedin/callback?test-redirect=true',
|
|
371
|
+
);
|
|
372
|
+
});
|
|
373
|
+
|
|
374
|
+
test('updates Apple Services ID only', async () => {
|
|
375
|
+
await run(
|
|
376
|
+
{
|
|
377
|
+
name: 'apple',
|
|
378
|
+
'services-id': 'new.apple.service',
|
|
379
|
+
},
|
|
380
|
+
{ yes: true },
|
|
381
|
+
);
|
|
382
|
+
|
|
383
|
+
expect(updatedClients).toHaveLength(1);
|
|
384
|
+
expect(updatedClients[0]).toMatchObject({
|
|
385
|
+
oauthClientId: 'apple',
|
|
386
|
+
clientId: 'new.apple.service',
|
|
387
|
+
});
|
|
388
|
+
expect(updatedClients[0].meta).toBeUndefined();
|
|
389
|
+
});
|
|
390
|
+
|
|
391
|
+
test('updates Apple team and key metadata', async () => {
|
|
392
|
+
await run(
|
|
393
|
+
{
|
|
394
|
+
name: 'apple',
|
|
395
|
+
'team-id': 'TEAM123',
|
|
396
|
+
'key-id': 'KEY456',
|
|
397
|
+
},
|
|
398
|
+
{ yes: true },
|
|
399
|
+
);
|
|
400
|
+
|
|
401
|
+
expect(updatedClients).toHaveLength(1);
|
|
402
|
+
expect(updatedClients[0]).toMatchObject({
|
|
403
|
+
oauthClientId: 'apple',
|
|
404
|
+
meta: {
|
|
405
|
+
teamId: 'TEAM123',
|
|
406
|
+
keyId: 'KEY456',
|
|
407
|
+
},
|
|
408
|
+
});
|
|
409
|
+
expect(updatedClients[0].clientId).toBeUndefined();
|
|
410
|
+
});
|
|
411
|
+
|
|
412
|
+
test('Apple redirect update prints Services ID return URL guidance', async () => {
|
|
413
|
+
await run(
|
|
414
|
+
{
|
|
415
|
+
name: 'apple',
|
|
416
|
+
'custom-redirect-uri': 'https://example.com/apple/callback',
|
|
417
|
+
},
|
|
418
|
+
{ yes: true },
|
|
419
|
+
);
|
|
420
|
+
|
|
421
|
+
expect(updatedClients).toHaveLength(1);
|
|
422
|
+
expect(updatedClients[0]).toMatchObject({
|
|
423
|
+
oauthClientId: 'apple',
|
|
424
|
+
redirectTo: 'https://example.com/apple/callback',
|
|
425
|
+
});
|
|
426
|
+
expect(logs.join('\n')).toContain(
|
|
427
|
+
'Add this return URL under your Services ID on',
|
|
428
|
+
);
|
|
429
|
+
expect(logs.join('\n')).toContain(
|
|
430
|
+
'https://example.com/apple/callback?test-redirect=true',
|
|
431
|
+
);
|
|
432
|
+
});
|
|
433
|
+
|
|
434
|
+
test('updates Clerk publishable key and discovery endpoint', async () => {
|
|
435
|
+
await run(
|
|
436
|
+
{
|
|
437
|
+
name: 'clerk',
|
|
438
|
+
'publishable-key':
|
|
439
|
+
'pk_test_Z3VpZGluZy1wZWdhc3VzLTkzLmNsZXJrLmFjY291bnRzLmRldiQ',
|
|
440
|
+
},
|
|
441
|
+
{ yes: true },
|
|
442
|
+
);
|
|
443
|
+
|
|
444
|
+
expect(updatedClients).toHaveLength(1);
|
|
445
|
+
expect(updatedClients[0]).toMatchObject({
|
|
446
|
+
oauthClientId: 'clerk',
|
|
447
|
+
discoveryEndpoint:
|
|
448
|
+
'https://guiding-pegasus-93.clerk.accounts.dev/.well-known/openid-configuration',
|
|
449
|
+
meta: {
|
|
450
|
+
clerkPublishableKey:
|
|
451
|
+
'pk_test_Z3VpZGluZy1wZWdhc3VzLTkzLmNsZXJrLmFjY291bnRzLmRldiQ',
|
|
452
|
+
},
|
|
453
|
+
});
|
|
454
|
+
});
|
|
455
|
+
|
|
456
|
+
test('updates Firebase project ID and discovery endpoint', async () => {
|
|
457
|
+
await run(
|
|
458
|
+
{
|
|
459
|
+
name: 'firebase',
|
|
460
|
+
'project-id': 'my-app-123',
|
|
461
|
+
},
|
|
462
|
+
{ yes: true },
|
|
463
|
+
);
|
|
464
|
+
|
|
465
|
+
expect(updatedClients).toHaveLength(1);
|
|
466
|
+
expect(updatedClients[0]).toMatchObject({
|
|
467
|
+
oauthClientId: 'firebase',
|
|
468
|
+
discoveryEndpoint:
|
|
469
|
+
'https://securetoken.google.com/my-app-123/.well-known/openid-configuration',
|
|
470
|
+
});
|
|
471
|
+
});
|
|
472
|
+
|
|
473
|
+
test('rejects invalid Firebase project ID', async () => {
|
|
474
|
+
await run(
|
|
475
|
+
{
|
|
476
|
+
name: 'firebase',
|
|
477
|
+
'project-id': 'BAD',
|
|
478
|
+
},
|
|
479
|
+
{ yes: true },
|
|
480
|
+
);
|
|
481
|
+
|
|
482
|
+
expect(logs.join('\n')).toContain('Invalid Firebase project ID');
|
|
483
|
+
expect(updatedClients).toHaveLength(0);
|
|
484
|
+
});
|
|
485
|
+
});
|
|
486
|
+
|
|
487
|
+
describe('--yes validation', () => {
|
|
488
|
+
test('requires an identifier', async () => {
|
|
489
|
+
await run({ 'client-id': 'new-id' }, { yes: true });
|
|
490
|
+
expect(logs.join('\n')).toContain('Must specify --id or --name');
|
|
491
|
+
expect(updatedClients).toHaveLength(0);
|
|
492
|
+
});
|
|
493
|
+
|
|
494
|
+
test('requires at least one update field', async () => {
|
|
495
|
+
await run({ name: 'github' }, { yes: true });
|
|
496
|
+
expect(logs.join('\n')).toContain(
|
|
497
|
+
'Must specify at least one of --client-id, --client-secret, or --custom-redirect-uri.',
|
|
498
|
+
);
|
|
499
|
+
expect(updatedClients).toHaveLength(0);
|
|
500
|
+
});
|
|
501
|
+
|
|
502
|
+
test('rejects both id and name', async () => {
|
|
503
|
+
await run(
|
|
504
|
+
{
|
|
505
|
+
id: 'github',
|
|
506
|
+
name: 'github',
|
|
507
|
+
'client-secret': 'new-gh-secret',
|
|
508
|
+
},
|
|
509
|
+
{ yes: true },
|
|
510
|
+
);
|
|
511
|
+
|
|
512
|
+
expect(logs.join('\n')).toContain('Cannot specify both --id and --name');
|
|
513
|
+
expect(updatedClients).toHaveLength(0);
|
|
514
|
+
});
|
|
515
|
+
|
|
516
|
+
test('rejects unknown client name', async () => {
|
|
517
|
+
await run(
|
|
518
|
+
{
|
|
519
|
+
name: 'unknown',
|
|
520
|
+
'client-secret': 'new-gh-secret',
|
|
521
|
+
},
|
|
522
|
+
{ yes: true },
|
|
523
|
+
);
|
|
524
|
+
|
|
525
|
+
expect(logs.join('\n')).toContain('OAuth client not found');
|
|
526
|
+
expect(updatedClients).toHaveLength(0);
|
|
527
|
+
});
|
|
528
|
+
});
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
import { Effect, Schema } from 'effect';
|
|
2
|
-
import { FileSystem } from '@effect/platform';
|
|
3
2
|
import { GlobalOpts } from '../../../context/globalOpts.ts';
|
|
4
3
|
export declare const ClientTypeSchema: Schema.Literal<["google", "github", "apple", "linkedin", "clerk", "firebase"]>;
|
|
5
4
|
export declare const authClientAddCmd: (opts: {
|
|
6
5
|
type?: string | undefined;
|
|
7
6
|
name?: string | undefined;
|
|
8
7
|
app?: string | undefined;
|
|
9
|
-
} & Record<string, unknown>) => Effect.Effect<void | undefined, import("../../../lib/ui.ts").UIError | import("../../../lib/http.ts").InstantHttpError | import("effect/Cause").TimeoutException | import("@effect/platform/HttpClientError").RequestError | import("effect/ParseResult").ParseError | import("@effect/platform/HttpClientError").ResponseError, GlobalOpts | FileSystem.FileSystem | import("../../../lib/http.ts").InstantHttpAuthed | import("../../../context/currentApp.ts").CurrentApp>;
|
|
8
|
+
} & Record<string, unknown>) => Effect.Effect<void | undefined, import("../../../lib/ui.ts").UIError | import("../../../lib/http.ts").InstantHttpError | import("effect/Cause").TimeoutException | import("@effect/platform/HttpClientError").RequestError | import("effect/ParseResult").ParseError | import("@effect/platform/HttpClientError").ResponseError, GlobalOpts | import("@effect/platform/FileSystem").FileSystem | import("../../../lib/http.ts").InstantHttpAuthed | import("../../../context/currentApp.ts").CurrentApp>;
|
|
10
9
|
//# sourceMappingURL=add.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"add.d.ts","sourceRoot":"","sources":["../../../../src/commands/auth/client/add.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAiB,MAAM,EAAE,MAAM,QAAQ,CAAC;
|
|
1
|
+
{"version":3,"file":"add.d.ts","sourceRoot":"","sources":["../../../../src/commands/auth/client/add.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAiB,MAAM,EAAE,MAAM,QAAQ,CAAC;AAGvD,OAAO,EAAE,UAAU,EAAE,MAAM,gCAAgC,CAAC;AAmD5D,eAAO,MAAM,gBAAgB,gFAO5B,CAAC;AA8pBF,eAAO,MAAM,gBAAgB;;;;wgBAwD5B,CAAC"}
|