Skip to content

Commit 4b88762

Browse files
authored
Merge branch 'main' into tm/sdki-982-apply-testing-tokens-to-e2e-tests
2 parents ef8eca4 + 3a2b439 commit 4b88762

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

52 files changed

+768
-245
lines changed

.changeset/cool-socks-invite.md

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
'@clerk/shared': minor
3+
'@clerk/types': minor
4+
---
5+
6+
Adding the new `o` claim that contains all organization related info for JWT v2 schema

.changeset/cyan-hairs-share.md

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
'@clerk/clerk-js': patch
3+
'@clerk/backend': patch
4+
---
5+
6+
Uses the helper function `__experimental_JWTPayloadToAuthObjectProperties` from `@clerk/shared` to handle the new JWT v2 schema.

.changeset/deep-moose-slide.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@clerk/astro': patch
3+
---
4+
5+
Fixes issue with `useAuth()` erroring due to missing auth context on static output.

.changeset/great-moons-occur.md

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
'@clerk/clerk-js': patch
3+
'@clerk/shared': patch
4+
'@clerk/types': patch
5+
---
6+
7+
Add Payment Sources to `<OrgProfile />`, hook up all org-related payment source and checkout methods to the org-specific endpoints

.changeset/hot-cats-smell.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@clerk/clerk-js': patch
3+
---
4+
5+
Fix issue where the SSO callback URL was incorrectly generated when using the transfer flow within a modal.

.changeset/tidy-dolls-join.md

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
'@clerk/clerk-js': patch
3+
'@clerk/types': patch
4+
---
5+
6+
Optionally handle the `intent` parameter on SSO redirects to reload specific resources.

.github/workflows/ci.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,7 @@ jobs:
179179
next-version: '13'
180180
- test-name: 'nextjs'
181181
test-project: 'chrome'
182-
next-version: '14.2.26'
182+
next-version: '14'
183183
- test-name: 'nextjs'
184184
test-project: 'chrome'
185185
next-version: '15'

packages/astro/src/react/hooks.ts

+21-6
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { resolveAuthState } from '@clerk/shared/authorization';
2+
import { deriveState } from '@clerk/shared/deriveState';
23
import type {
34
CheckAuthorizationWithCustomPermissions,
45
Clerk,
@@ -83,7 +84,7 @@ type UseAuth = (options?: PendingSessionOptions) => UseAuthReturn;
8384
* }
8485
*/
8586
export const useAuth: UseAuth = ({ treatPendingAsSignedOut } = {}) => {
86-
const authContext = useStore($authStore);
87+
const authContext = useAuthStore();
8788
const clerkContext = useStore($clerkStore);
8889

8990
const getToken: GetToken = useCallback(createGetToken(), []);
@@ -139,14 +140,18 @@ export const useAuth: UseAuth = ({ treatPendingAsSignedOut } = {}) => {
139140
return payload;
140141
};
141142

143+
function useStore<T extends Store, SV extends StoreValue<T>>(store: T, getServerSnapshot?: () => SV): SV {
144+
const get = store.get.bind(store);
145+
return useSyncExternalStore<SV>(store.listen, get, getServerSnapshot || get);
146+
}
147+
142148
/**
143149
* This implementation of `useStore` is an alternative solution to the hook exported by nanostores
144150
* Reference: https://github.com/nanostores/react/blob/main/index.js
145151
*/
146-
function useStore<T extends Store, SV extends StoreValue<T>>(store: T): SV {
147-
const get = store.get.bind(store);
148-
149-
return useSyncExternalStore<SV>(store.listen, get, () => {
152+
function useAuthStore() {
153+
const get = $authStore.get.bind($authStore);
154+
return useStore($authStore, () => {
150155
// Per react docs
151156
/**
152157
* optional getServerSnapshot:
@@ -160,7 +165,17 @@ function useStore<T extends Store, SV extends StoreValue<T>>(store: T): SV {
160165
* When this runs on the server we want to grab the content from the async-local-storage.
161166
*/
162167
if (typeof window === 'undefined') {
163-
return authAsyncStorage.getStore();
168+
return deriveState(
169+
false,
170+
{
171+
user: null,
172+
session: null,
173+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
174+
client: null!,
175+
organization: null,
176+
},
177+
authAsyncStorage.getStore() as any,
178+
);
164179
}
165180

166181
/**

packages/backend/src/tokens/authObjects.ts

+4-53
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,11 @@
11
import { createCheckAuthorization } from '@clerk/shared/authorization';
2+
import { __experimental_JWTPayloadToAuthObjectProperties } from '@clerk/shared/jwtPayloadParser';
23
import type {
3-
ActClaim,
44
CheckAuthorizationFromSessionClaims,
55
JwtPayload,
6-
OrganizationCustomPermissionKey,
7-
OrganizationCustomRoleKey,
86
ServerGetToken,
97
ServerGetTokenOptions,
10-
SessionStatusClaim,
8+
SharedSignedInAuthObjectProperties,
119
} from '@clerk/types';
1210

1311
import type { CreateBackendApiOptions } from '../api';
@@ -27,28 +25,7 @@ export type SignedInAuthObjectOptions = CreateBackendApiOptions & {
2725
/**
2826
* @internal
2927
*/
30-
type SignedInAuthObjectProperties = {
31-
sessionClaims: JwtPayload;
32-
sessionId: string;
33-
sessionStatus: SessionStatusClaim | null;
34-
actor: ActClaim | undefined;
35-
userId: string;
36-
orgId: string | undefined;
37-
orgRole: OrganizationCustomRoleKey | undefined;
38-
orgSlug: string | undefined;
39-
orgPermissions: OrganizationCustomPermissionKey[] | undefined;
40-
/**
41-
* Factor Verification Age
42-
* Each item represents the minutes that have passed since the last time a first or second factor were verified.
43-
* [fistFactorAge, secondFactorAge]
44-
*/
45-
factorVerificationAge: [firstFactorAge: number, secondFactorAge: number] | null;
46-
};
47-
48-
/**
49-
* @internal
50-
*/
51-
export type SignedInAuthObject = SignedInAuthObjectProperties & {
28+
export type SignedInAuthObject = SharedSignedInAuthObjectProperties & {
5229
getToken: ServerGetToken;
5330
has: CheckAuthorizationFromSessionClaims;
5431
debug: AuthObjectDebug;
@@ -92,31 +69,6 @@ const createDebug = (data: AuthObjectDebugData | undefined) => {
9269
};
9370
};
9471

95-
const generateSignedInAuthObjectProperties = (claims: JwtPayload): SignedInAuthObjectProperties => {
96-
// fva can be undefined for instances that have not opt-in
97-
const factorVerificationAge = claims.fva ?? null;
98-
99-
// sts can be undefined for instances that have not opt-in
100-
const sessionStatus = claims.sts ?? null;
101-
102-
// TODO(jwt-v2): replace this when the new claim for org permissions is added, this will not break
103-
// anything since the JWT v2 is not yet available
104-
const orgPermissions = claims.org_permissions;
105-
106-
return {
107-
sessionClaims: claims,
108-
sessionId: claims.sid,
109-
sessionStatus,
110-
actor: claims.act,
111-
userId: claims.sub,
112-
orgId: claims.org_id,
113-
orgRole: claims.org_role,
114-
orgSlug: claims.org_slug,
115-
orgPermissions,
116-
factorVerificationAge,
117-
};
118-
};
119-
12072
/**
12173
* @internal
12274
*/
@@ -126,14 +78,13 @@ export function signedInAuthObject(
12678
sessionClaims: JwtPayload,
12779
): SignedInAuthObject {
12880
const { actor, sessionId, sessionStatus, userId, orgId, orgRole, orgSlug, orgPermissions, factorVerificationAge } =
129-
generateSignedInAuthObjectProperties(sessionClaims);
81+
__experimental_JWTPayloadToAuthObjectProperties(sessionClaims);
13082
const apiClient = createBackendApiClient(authenticateContext);
13183
const getToken = createGetToken({
13284
sessionId,
13385
sessionToken,
13486
fetcher: async (...args) => (await apiClient.sessions.getToken(...args)).jwt,
13587
});
136-
13788
return {
13889
actor,
13990
sessionClaims,

packages/clerk-js/bundlewatch.config.json

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
{
22
"files": [
33
{ "path": "./dist/clerk.js", "maxSize": "590kB" },
4-
{ "path": "./dist/clerk.browser.js", "maxSize": "72.5KB" },
4+
{ "path": "./dist/clerk.browser.js", "maxSize": "73.21KB" },
55
{ "path": "./dist/clerk.headless*.js", "maxSize": "55KB" },
6-
{ "path": "./dist/ui-common*.js", "maxSize": "98KB" },
6+
{ "path": "./dist/ui-common*.js", "maxSize": "98.2KB" },
77
{ "path": "./dist/vendors*.js", "maxSize": "36KB" },
88
{ "path": "./dist/coinbase*.js", "maxSize": "35.5KB" },
99
{ "path": "./dist/createorganization*.js", "maxSize": "5KB" },
@@ -21,7 +21,7 @@
2121
{ "path": "./dist/keylessPrompt*.js", "maxSize": "5.9KB" },
2222
{ "path": "./dist/pricingTable*.js", "maxSize": "5KB" },
2323
{ "path": "./dist/checkout*.js", "maxSize": "3KB" },
24-
{ "path": "./dist/paymentSources*.js", "maxSize": "8KB" },
24+
{ "path": "./dist/paymentSources*.js", "maxSize": "8.1KB" },
2525
{ "path": "./dist/up-billing-page*.js", "maxSize": "1KB" },
2626
{ "path": "./dist/op-billing-page*.js", "maxSize": "1KB" },
2727
{ "path": "./dist/sessionTasks*.js", "maxSize": "1KB" }

packages/clerk-js/src/core/clerk.ts

+7-8
Original file line numberDiff line numberDiff line change
@@ -1528,16 +1528,15 @@ export class Clerk implements ClerkInterface {
15281528
// directs the opening page to navigate to the /sso-callback route), we need to reload the signIn and signUp resources
15291529
// to ensure that we have the latest state. This operation can fail when we try reloading a resource that doesn't
15301530
// exist (such as when reloading a signIn resource during a signUp attempt), but this can be safely ignored.
1531-
if (!window.opener) {
1531+
if (!window.opener && params.reloadResource) {
15321532
try {
1533-
await signIn.reload();
1534-
} catch {
1535-
// This can be safely ignored
1536-
}
1537-
try {
1538-
await signUp.reload();
1533+
if (params.reloadResource === 'signIn') {
1534+
await signIn.reload();
1535+
} else if (params.reloadResource === 'signUp') {
1536+
await signUp.reload();
1537+
}
15391538
} catch {
1540-
// This can be safely ignored
1539+
// This catch intentionally left blank.
15411540
}
15421541
}
15431542

packages/clerk-js/src/core/jwt-client.ts

+17-16
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { __experimental_JWTPayloadToAuthObjectProperties } from '@clerk/shared/jwtPayloadParser';
12
import type {
23
ClientJSON,
34
OrganizationMembershipJSON,
@@ -43,47 +44,47 @@ export function createClientFromJwt(jwt: string | undefined | null): Client {
4344
} as unknown as ClientJSON);
4445
}
4546

46-
const { sid, sub, org_id, org_role, org_permissions, org_slug, fva } = token.jwt.claims;
47-
48-
// TODO(jwt-v2): when JWT version 2 is available, we should use the new claims instead of the old ones
47+
const { sessionId, userId, orgId, orgRole, orgPermissions, orgSlug, factorVerificationAge } =
48+
__experimental_JWTPayloadToAuthObjectProperties(token.jwt.claims);
4949

50+
// TODO(jwt-v2): when JWT version 2 is available, we should revise org permissions
5051
const defaultClient = {
5152
object: 'client',
52-
last_active_session_id: sid,
53+
last_active_session_id: sessionId,
5354
id: 'client_init',
5455
sessions: [
5556
{
5657
object: 'session',
57-
id: sid,
58+
id: sessionId,
5859
status: 'active',
59-
last_active_organization_id: org_id || null,
60+
last_active_organization_id: orgId || null,
6061
// @ts-expect-error - ts is not happy about `id:undefined`, but this is allowed and expected
6162
last_active_token: {
6263
id: undefined,
6364
object: 'token',
6465
jwt,
6566
} as TokenJSON,
66-
factor_verification_age: fva || null,
67+
factor_verification_age: factorVerificationAge || null,
6768
public_user_data: {
68-
user_id: sub,
69+
user_id: userId,
6970
} as PublicUserDataJSON,
7071
user: {
7172
object: 'user',
72-
id: sub,
73+
id: userId,
7374
organization_memberships:
74-
org_id && org_slug && org_role
75+
orgId && orgSlug && orgRole
7576
? [
7677
{
7778
object: 'organization_membership',
78-
id: org_id,
79-
role: org_role,
80-
permissions: org_permissions || [],
79+
id: orgId,
80+
role: orgRole,
81+
permissions: orgPermissions || [],
8182
organization: {
8283
object: 'organization',
83-
id: org_id,
84+
id: orgId,
8485
// Use slug as name for the organization, since name is not available in the token.
85-
name: org_slug,
86-
slug: org_slug,
86+
name: orgSlug,
87+
slug: orgSlug,
8788
members_count: 1,
8889
max_allowed_memberships: 1,
8990
},

packages/clerk-js/src/core/modules/commerce/Commerce.ts

+15-7
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import type {
99
ClerkPaginatedResponse,
1010
} from '@clerk/types';
1111

12+
import { convertPageToOffsetSearchParams } from '../../../utils/convertPageToOffsetSearchParams';
1213
import {
1314
__experimental_CommerceInitializedPaymentSource,
1415
__experimental_CommercePaymentSource,
@@ -27,35 +28,42 @@ export class __experimental_Commerce implements __experimental_CommerceNamespace
2728
}
2829

2930
initializePaymentSource = async (params: __experimental_InitializePaymentSourceParams) => {
31+
const { orgId, ...rest } = params;
3032
const json = (
3133
await BaseResource._fetch({
32-
path: `/me/commerce/payment_sources/initialize`,
34+
path: orgId
35+
? `/organizations/${orgId}/commerce/payment_sources/initialize`
36+
: `/me/commerce/payment_sources/initialize`,
3337
method: 'POST',
34-
body: params as any,
38+
body: rest as any,
3539
})
3640
)?.response as unknown as __experimental_CommerceInitializedPaymentSourceJSON;
3741
return new __experimental_CommerceInitializedPaymentSource(json);
3842
};
3943

4044
addPaymentSource = async (params: __experimental_AddPaymentSourceParams) => {
45+
const { orgId, ...rest } = params;
46+
4147
const json = (
4248
await BaseResource._fetch({
43-
path: `/me/commerce/payment_sources`,
49+
path: orgId ? `/organizations/${orgId}/commerce/payment_sources` : `/me/commerce/payment_sources`,
4450
method: 'POST',
45-
body: params as any,
51+
body: rest as any,
4652
})
4753
)?.response as unknown as __experimental_CommercePaymentSourceJSON;
4854
return new __experimental_CommercePaymentSource(json);
4955
};
5056

5157
getPaymentSources = async (params: __experimental_GetPaymentSourcesParams) => {
58+
const { orgId, ...rest } = params;
59+
5260
return await BaseResource._fetch({
53-
path: `/me/commerce/payment_sources`,
61+
path: orgId ? `/organizations/${orgId}/commerce/payment_sources` : `/me/commerce/payment_sources`,
5462
method: 'GET',
55-
search: { orgId: params.orgId || '' },
63+
search: convertPageToOffsetSearchParams(rest),
5664
}).then(res => {
5765
const { data: paymentSources, total_count } =
58-
res as unknown as ClerkPaginatedResponse<__experimental_CommercePaymentSourceJSON>;
66+
res?.response as unknown as ClerkPaginatedResponse<__experimental_CommercePaymentSourceJSON>;
5967
return {
6068
total_count,
6169
data: paymentSources.map(paymentSource => new __experimental_CommercePaymentSource(paymentSource)),

0 commit comments

Comments
 (0)