thirdparty-credentials.test.ts89 lines · main
1/**
2 * Per-project OAuth credentials (product rule 2026-07-27).
3 * Platform env must not unlock social login for customer projects.
4 */
5
6import { afterEach, describe, expect, mock, test } from 'bun:test';
7
8const loadMock = mock(
9 async (
10 _projectId: string,
11 ): Promise<
12 Array<{ thirdPartyId: string; clientId: string; clientSecret: string }>
13 > => [],
14);
15
16mock.module('./project-config.js', () => ({
17 loadProjectProviderSecrets: (projectId: string) => loadMock(projectId),
18}));
19
20// Import after mock so resolveProviderCredentials uses the stub.
21const { resolveProviderCredentials } = await import('./thirdparty.js');
22
23describe('resolveProviderCredentials — per-project only', () => {
24 afterEach(() => {
25 loadMock.mockClear();
26 delete process.env.BRIVEN_KONNOS_CLIENT_ID;
27 delete process.env.BRIVEN_KONNOS_CLIENT_SECRET;
28 delete process.env.BRIVEN_GOOGLE_CLIENT_ID;
29 delete process.env.BRIVEN_GOOGLE_CLIENT_SECRET;
30 });
31
32 test('returns project secrets when both id and secret are stored', async () => {
33 loadMock.mockImplementation(async () => [
34 {
35 thirdPartyId: 'konnos',
36 clientId: 'kc_mavi',
37 clientSecret: 'sec_mavi',
38 },
39 ]);
40 const creds = await resolveProviderCredentials('p_mavi', 'konnos');
41 expect(creds).toEqual({
42 clientId: 'kc_mavi',
43 clientSecret: 'sec_mavi',
44 source: 'project_secrets',
45 });
46 });
47
48 test('does not use platform env when this project has no secrets', async () => {
49 loadMock.mockImplementation(async () => []);
50 process.env.BRIVEN_KONNOS_CLIENT_ID = 'kc_platform';
51 process.env.BRIVEN_KONNOS_CLIENT_SECRET = 'sec_platform';
52 const creds = await resolveProviderCredentials('p_other', 'konnos');
53 expect(creds).toBeNull();
54 });
55
56 test('does not use platform env without a project id', async () => {
57 process.env.BRIVEN_KONNOS_CLIENT_ID = 'kc_platform';
58 process.env.BRIVEN_KONNOS_CLIENT_SECRET = 'sec_platform';
59 expect(await resolveProviderCredentials(undefined, 'konnos')).toBeNull();
60 expect(await resolveProviderCredentials('', 'konnos')).toBeNull();
61 expect(await resolveProviderCredentials(' ', 'konnos')).toBeNull();
62 });
63
64 test('project A secrets do not apply when loader returns empty for project B', async () => {
65 loadMock.mockImplementation(async (projectId: string) => {
66 if (projectId === 'p_mavi') {
67 return [
68 {
69 thirdPartyId: 'konnos',
70 clientId: 'kc_mavi',
71 clientSecret: 'sec_mavi',
72 },
73 ];
74 }
75 return [];
76 });
77 process.env.BRIVEN_KONNOS_CLIENT_ID = 'kc_platform';
78 process.env.BRIVEN_KONNOS_CLIENT_SECRET = 'sec_platform';
79 expect(await resolveProviderCredentials('p_mavi', 'konnos')).not.toBeNull();
80 expect(await resolveProviderCredentials('p_cyberbear', 'konnos')).toBeNull();
81 });
82
83 test('same rule for google (no shared env fallback)', async () => {
84 loadMock.mockImplementation(async () => []);
85 process.env.BRIVEN_GOOGLE_CLIENT_ID = 'g_platform';
86 process.env.BRIVEN_GOOGLE_CLIENT_SECRET = 'g_secret';
87 expect(await resolveProviderCredentials('p_any', 'google')).toBeNull();
88 });
89});