Skip to content

Commit 7015950

Browse files
authored
Merge branch 'main' into jacek/fix-testing-concurrent-workers
2 parents c782480 + 9ec56ab commit 7015950

18 files changed

Lines changed: 316 additions & 67 deletions

File tree

.changeset/fix-ci-artifact-name.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
---
2+
---
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
---
2+
---
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@clerk/backend': patch
3+
---
4+
5+
Fix frontend API proxy following redirects server-side instead of passing them to the browser. The proxy's `fetch()` call now uses `redirect: 'manual'` so that 3xx responses from FAPI (e.g. after OAuth callbacks) are returned to the client as-is, matching standard HTTP proxy behavior.

.changeset/green-hotels-study.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@clerk/localizations': patch
3+
---
4+
5+
fix(localizations): add missing Hungarian translations for form placeholders and legal consent

.changeset/humble-trams-laugh.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
---
2+
'@clerk/clerk-js': minor
3+
'@clerk/shared': minor
4+
'@clerk/ui': minor
5+
---
6+
7+
Add `EnterpriseConnection` resource
8+
9+
`User.getEnterpriseConnections()` was wrongly typed as returning `EnterpriseAccountConnectionResource[]`, it now returns `EnterpriseConnectionResource[]`

.github/workflows/ci.yml

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -457,11 +457,20 @@ jobs:
457457
NODE_EXTRA_CA_CERTS: ${{ github.workspace }}/integration/certs/rootCA.pem
458458
VERCEL_AUTOMATION_BYPASS_SECRET: ${{ secrets.VERCEL_AUTOMATION_BYPASS_SECRET }}
459459

460+
- name: Sanitize artifact name
461+
if: ${{ cancelled() || failure() }}
462+
id: sanitize
463+
run: |
464+
SANITIZED="${TEST_NAME//:/-}"
465+
echo "artifact-suffix=${SANITIZED}" >> $GITHUB_OUTPUT
466+
env:
467+
TEST_NAME: ${{ matrix.test-name }}
468+
460469
- name: Upload test-results
461470
if: ${{ cancelled() || failure() }}
462471
uses: actions/upload-artifact@v4
463472
with:
464-
name: playwright-traces-${{ github.run_id }}-${{ github.run_attempt }}-${{ matrix.test-name }}${{ matrix.next-version && format('-next{0}', matrix.next-version) || '' }}
473+
name: playwright-traces-${{ github.run_id }}-${{ github.run_attempt }}-${{ steps.sanitize.outputs.artifact-suffix }}${{ matrix.next-version && format('-next{0}', matrix.next-version) || '' }}
465474
path: test-results
466475
retention-days: 1
467476

integration/testUtils/keylessHelpers.ts

Lines changed: 9 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -48,34 +48,15 @@ export async function testToggleCollapsePopoverAndClaim({
4848

4949
const claim = u.po.keylessPopover.promptsToClaim();
5050

51-
const [newPage] = await Promise.all([context.waitForEvent('page'), claim.click()]);
52-
53-
await newPage.waitForLoadState();
54-
55-
await newPage.waitForURL(url => {
56-
const signInForceRedirectUrl = url.searchParams.get('sign_in_force_redirect_url');
57-
const signUpForceRedirectUrl = url.searchParams.get('sign_up_force_redirect_url');
58-
59-
const signInHasRequiredParams =
60-
signInForceRedirectUrl?.includes(`${dashboardUrl}apps/claim`) &&
61-
signInForceRedirectUrl?.includes('token=') &&
62-
signInForceRedirectUrl?.includes(`framework=${framework}`);
63-
64-
const signUpRegularCase =
65-
signUpForceRedirectUrl?.includes(`${dashboardUrl}apps/claim`) &&
66-
signUpForceRedirectUrl?.includes('token=') &&
67-
signUpForceRedirectUrl?.includes(`framework=${framework}`);
68-
69-
const signUpPrepareAccountCase =
70-
signUpForceRedirectUrl?.startsWith(`${dashboardUrl}prepare-account`) &&
71-
signUpForceRedirectUrl?.includes(encodeURIComponent('apps/claim')) &&
72-
signUpForceRedirectUrl?.includes(encodeURIComponent('token=')) &&
73-
signUpForceRedirectUrl?.includes(encodeURIComponent(`framework=${framework}`));
74-
75-
const signUpHasRequiredParams = signUpRegularCase || signUpPrepareAccountCase;
76-
77-
return url.pathname === '/apps/claim/sign-in' && signInHasRequiredParams && signUpHasRequiredParams;
78-
});
51+
const href = await claim.getAttribute('href');
52+
expect(href).toBeTruthy();
53+
54+
const claimUrl = new URL(href!);
55+
expect(claimUrl.origin + '/').toBe(dashboardUrl);
56+
expect(claimUrl.pathname).toBe('/apps/claim');
57+
expect(claimUrl.searchParams.get('framework')).toBe(framework);
58+
expect(claimUrl.searchParams.has('token')).toBe(true);
59+
expect(claimUrl.searchParams.has('return_url')).toBe(true);
7960
}
8061

8162
/**

packages/backend/src/proxy.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -278,6 +278,7 @@ export async function clerkFrontendApiProxy(request: Request, options?: Frontend
278278
const fetchOptions: RequestInit = {
279279
method: request.method,
280280
headers,
281+
redirect: 'manual',
281282
// @ts-expect-error - duplex is required for streaming bodies but not in all TS definitions
282283
duplex: hasBody ? 'half' : undefined,
283284
};

packages/clerk-js/bundlewatch.config.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
{
22
"files": [
3-
{ "path": "./dist/clerk.js", "maxSize": "540KB" },
3+
{ "path": "./dist/clerk.js", "maxSize": "543KB" },
44
{ "path": "./dist/clerk.browser.js", "maxSize": "67KB" },
5-
{ "path": "./dist/clerk.legacy.browser.js", "maxSize": "108KB" },
6-
{ "path": "./dist/clerk.no-rhc.js", "maxSize": "307KB" },
7-
{ "path": "./dist/clerk.native.js", "maxSize": "66KB" },
5+
{ "path": "./dist/clerk.legacy.browser.js", "maxSize": "110KB" },
6+
{ "path": "./dist/clerk.no-rhc.js", "maxSize": "309KB" },
7+
{ "path": "./dist/clerk.native.js", "maxSize": "68KB" },
88
{ "path": "./dist/vendors*.js", "maxSize": "7KB" },
99
{ "path": "./dist/coinbase*.js", "maxSize": "36KB" },
1010
{ "path": "./dist/base-account-sdk*.js", "maxSize": "203KB" },
Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
import type {
2+
EnterpriseConnectionJSON,
3+
EnterpriseConnectionJSONSnapshot,
4+
EnterpriseConnectionResource,
5+
EnterpriseOAuthConfigJSON,
6+
EnterpriseOAuthConfigResource,
7+
EnterpriseSamlConnectionNestedJSON,
8+
EnterpriseSamlConnectionNestedResource,
9+
} from '@clerk/shared/types';
10+
11+
import { unixEpochToDate } from '../../utils/date';
12+
import { BaseResource } from './Base';
13+
14+
function samlNestedFromJSON(data: EnterpriseSamlConnectionNestedJSON): EnterpriseSamlConnectionNestedResource {
15+
return {
16+
id: data.id,
17+
name: data.name,
18+
active: data.active,
19+
idpEntityId: data.idp_entity_id,
20+
idpSsoUrl: data.idp_sso_url,
21+
idpCertificate: data.idp_certificate,
22+
idpMetadataUrl: data.idp_metadata_url,
23+
idpMetadata: data.idp_metadata,
24+
acsUrl: data.acs_url,
25+
spEntityId: data.sp_entity_id,
26+
spMetadataUrl: data.sp_metadata_url,
27+
allowSubdomains: data.allow_subdomains,
28+
allowIdpInitiated: data.allow_idp_initiated,
29+
forceAuthn: data.force_authn,
30+
};
31+
}
32+
33+
function samlNestedToJSON(data: EnterpriseSamlConnectionNestedResource): EnterpriseSamlConnectionNestedJSON {
34+
return {
35+
id: data.id,
36+
name: data.name,
37+
active: data.active,
38+
idp_entity_id: data.idpEntityId,
39+
idp_sso_url: data.idpSsoUrl,
40+
idp_certificate: data.idpCertificate,
41+
idp_metadata_url: data.idpMetadataUrl,
42+
idp_metadata: data.idpMetadata,
43+
acs_url: data.acsUrl,
44+
sp_entity_id: data.spEntityId,
45+
sp_metadata_url: data.spMetadataUrl,
46+
allow_subdomains: data.allowSubdomains,
47+
allow_idp_initiated: data.allowIdpInitiated,
48+
force_authn: data.forceAuthn,
49+
};
50+
}
51+
52+
function oauthConfigFromJSON(data: EnterpriseOAuthConfigJSON): EnterpriseOAuthConfigResource {
53+
return {
54+
id: data.id,
55+
name: data.name,
56+
clientId: data.client_id,
57+
providerKey: data.provider_key,
58+
discoveryUrl: data.discovery_url,
59+
logoPublicUrl: data.logo_public_url,
60+
requiresPkce: data.requires_pkce,
61+
createdAt: unixEpochToDate(data.created_at),
62+
updatedAt: unixEpochToDate(data.updated_at),
63+
};
64+
}
65+
66+
function oauthConfigToJSON(data: EnterpriseOAuthConfigResource): EnterpriseOAuthConfigJSON {
67+
return {
68+
id: data.id,
69+
name: data.name,
70+
client_id: data.clientId,
71+
provider_key: data.providerKey,
72+
discovery_url: data.discoveryUrl,
73+
logo_public_url: data.logoPublicUrl,
74+
requires_pkce: data.requiresPkce,
75+
created_at: data.createdAt?.getTime() ?? 0,
76+
updated_at: data.updatedAt?.getTime() ?? 0,
77+
};
78+
}
79+
80+
export class EnterpriseConnection extends BaseResource implements EnterpriseConnectionResource {
81+
id!: string;
82+
name!: string;
83+
active!: boolean;
84+
domains: string[] = [];
85+
organizationId: string | null = null;
86+
syncUserAttributes!: boolean;
87+
disableAdditionalIdentifications!: boolean;
88+
allowOrganizationAccountLinking!: boolean;
89+
customAttributes: unknown[] = [];
90+
oauthConfig: EnterpriseOAuthConfigResource | null = null;
91+
samlConnection: EnterpriseSamlConnectionNestedResource | null = null;
92+
createdAt: Date | null = null;
93+
updatedAt: Date | null = null;
94+
95+
constructor(data: EnterpriseConnectionJSON | EnterpriseConnectionJSONSnapshot | null) {
96+
super();
97+
this.fromJSON(data);
98+
}
99+
100+
protected fromJSON(data: EnterpriseConnectionJSON | EnterpriseConnectionJSONSnapshot | null): this {
101+
if (!data) {
102+
return this;
103+
}
104+
105+
this.id = data.id;
106+
this.name = data.name;
107+
this.active = data.active;
108+
this.domains = data.domains ?? [];
109+
this.organizationId = data.organization_id ?? null;
110+
this.syncUserAttributes = data.sync_user_attributes;
111+
this.disableAdditionalIdentifications = data.disable_additional_identifications;
112+
this.allowOrganizationAccountLinking = data.allow_organization_account_linking ?? false;
113+
this.customAttributes = data.custom_attributes ?? [];
114+
this.createdAt = unixEpochToDate(data.created_at);
115+
this.updatedAt = unixEpochToDate(data.updated_at);
116+
117+
this.samlConnection = data.saml_connection ? samlNestedFromJSON(data.saml_connection) : null;
118+
this.oauthConfig = data.oauth_config ? oauthConfigFromJSON(data.oauth_config) : null;
119+
120+
return this;
121+
}
122+
123+
public __internal_toSnapshot(): EnterpriseConnectionJSONSnapshot {
124+
return {
125+
object: 'enterprise_connection',
126+
id: this.id,
127+
name: this.name,
128+
active: this.active,
129+
domains: this.domains,
130+
organization_id: this.organizationId,
131+
sync_user_attributes: this.syncUserAttributes,
132+
disable_additional_identifications: this.disableAdditionalIdentifications,
133+
allow_organization_account_linking: this.allowOrganizationAccountLinking,
134+
custom_attributes: this.customAttributes,
135+
saml_connection: this.samlConnection ? samlNestedToJSON(this.samlConnection) : undefined,
136+
oauth_config: this.oauthConfig ? oauthConfigToJSON(this.oauthConfig) : undefined,
137+
created_at: this.createdAt?.getTime() ?? 0,
138+
updated_at: this.updatedAt?.getTime() ?? 0,
139+
};
140+
}
141+
}

0 commit comments

Comments
 (0)