Skip to content

Commit 352d79d

Browse files
Merge branch 'main' into feat/localization-pt-BR-billing
2 parents 9eb23c5 + 183e382 commit 352d79d

Some content is hidden

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

56 files changed

+1299
-200
lines changed

.changeset/brown-masks-admire.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
'@clerk/localizations': patch
3+
'@clerk/clerk-js': patch
4+
'@clerk/types': patch
5+
---
6+
7+
Add payment history tab to UserProfile and OrgProfile

.changeset/free-times-refuse.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
'@clerk/backend': patch
3+
'@clerk/nextjs': patch
4+
---
5+
6+
Re-organize internal types for the recently added "machine authentication" feature.

.changeset/large-adults-juggle.md

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
---
2+
'@clerk/tanstack-react-start': minor
3+
---
4+
5+
Introduces machine authentication, supporting four token types: `api_key`, `oauth_token`, `machine_token`, and `session_token`. For backwards compatibility, `session_token` remains the default when no token type is specified. This enables machine-to-machine authentication and use cases such as API keys and OAuth integrations. Existing applications continue to work without modification.
6+
7+
You can specify which token types are allowed by using the `acceptsToken` option in the `getAuth()` function. This option can be set to a specific type, an array of types, or `'any'` to accept all supported tokens.
8+
9+
Example usage:
10+
11+
```ts
12+
import { createServerFn } from '@tanstack/react-start'
13+
import { getAuth } from '@clerk/tanstack-react-start/server'
14+
import { getWebRequest } from '@tanstack/react-start/server'
15+
16+
const authStateFn = createServerFn({ method: 'GET' }).handler(async () => {
17+
const request = getWebRequest()
18+
const auth = await getAuth(request, { acceptsToken: 'any' })
19+
20+
if (authObject.tokenType === 'session_token') {
21+
console.log('this is session token from a user')
22+
} else {
23+
console.log('this is some other type of machine token')
24+
console.log('more specifically, a ' + authObject.tokenType)
25+
}
26+
27+
return {}
28+
})
29+
30+
```

.changeset/petite-sites-see.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@clerk/clerk-js': patch
3+
---
4+
5+
Fix Stripe Elements error handling

.changeset/social-carrots-melt.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
'@clerk/backend': patch
3+
'@clerk/nextjs': patch
4+
---
5+
6+
Resolve machine token property mixing in discriminated unions

.changeset/sour-onions-wear.md

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
---
2+
'@clerk/express': minor
3+
---
4+
5+
Introduces machine authentication, supporting four token types: `api_key`, `oauth_token`, `machine_token`, and `session_token`. For backwards compatibility, `session_token` remains the default when no token type is specified. This enables machine-to-machine authentication and use cases such as API keys and OAuth integrations. Existing applications continue to work without modification.
6+
7+
You can specify which token types are allowed by using the `acceptsToken` option in the `getAuth()` function. This option can be set to a specific type, an array of types, or `'any'` to accept all supported tokens.
8+
9+
Example usage:
10+
11+
```ts
12+
import express from 'express';
13+
import { getAuth } from '@clerk/express';
14+
15+
const app = express();
16+
17+
app.get('/path', (req, res) => {
18+
const authObject = getAuth(req, { acceptsToken: 'any' });
19+
20+
if (authObject.tokenType === 'session_token') {
21+
console.log('this is session token from a user')
22+
} else {
23+
console.log('this is some other type of machine token')
24+
console.log('more specifically, a ' + authObject.tokenType)
25+
}
26+
});
27+
```

.changeset/two-trains-pull.md

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
---
2+
'@clerk/react-router': minor
3+
---
4+
5+
Introduces machine authentication, supporting four token types: `api_key`, `oauth_token`, `machine_token`, and `session_token`. For backwards compatibility, `session_token` remains the default when no token type is specified. This enables machine-to-machine authentication and use cases such as API keys and OAuth integrations. Existing applications continue to work without modification.
6+
7+
You can specify which token types are allowed by using the `acceptsToken` option in the `getAuth()` function. This option can be set to a specific type, an array of types, or `'any'` to accept all supported tokens.
8+
9+
Example usage:
10+
11+
```ts
12+
import { getAuth } from '@clerk/react-router/ssr.server'
13+
import type { Route } from './+types/profile'
14+
15+
export async function loader(args: Route.LoaderArgs) {
16+
const authObject = await getAuth(args, { acceptsToken: 'any' })
17+
18+
if (authObject.tokenType === 'session_token') {
19+
console.log('this is session token from a user')
20+
} else {
21+
console.log('this is some other type of machine token')
22+
console.log('more specifically, a ' + authObject.tokenType)
23+
}
24+
25+
return {}
26+
}
27+
```

.changeset/yummy-socks-join.md

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
---
2+
'@clerk/fastify': minor
3+
---
4+
5+
Introduces machine authentication, supporting four token types: `api_key`, `oauth_token`, `machine_token`, and `session_token`. For backwards compatibility, `session_token` remains the default when no token type is specified. This enables machine-to-machine authentication and use cases such as API keys and OAuth integrations. Existing applications continue to work without modification.
6+
7+
You can specify which token types are allowed by using the `acceptsToken` option in the `getAuth()` function. This option can be set to a specific type, an array of types, or `'any'` to accept all supported tokens.
8+
9+
Example usage:
10+
11+
```ts
12+
import Fastify from 'fastify'
13+
import { getAuth } from '@clerk/fastify'
14+
15+
const fastify = Fastify()
16+
17+
fastify.get('/path', (request, reply) => {
18+
const authObject = getAuth(req, { acceptsToken: 'any' });
19+
20+
if (authObject.tokenType === 'session_token') {
21+
console.log('this is session token from a user')
22+
} else {
23+
console.log('this is some other type of machine token')
24+
console.log('more specifically, a ' + authObject.tokenType)
25+
}
26+
});
27+
```

.typedoc/__tests__/__snapshots__/file-structure.test.ts.snap

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,10 @@ exports[`Typedoc output > should have a deliberate file structure 1`] = `
150150
"backend/client.mdx",
151151
"backend/email-address.mdx",
152152
"backend/external-account.mdx",
153+
"backend/get-auth-fn.mdx",
153154
"backend/identification-link.mdx",
155+
"backend/infer-auth-object-from-token-array.mdx",
156+
"backend/infer-auth-object-from-token.mdx",
154157
"backend/invitation-status.mdx",
155158
"backend/invitation.mdx",
156159
"backend/organization-invitation-status.mdx",

packages/backend/src/__tests__/exports.test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ describe('subpath /internal exports', () => {
4747
"createRedirect",
4848
"debugRequestState",
4949
"decorateObjectWithResources",
50+
"getAuthObjectForAcceptedToken",
5051
"getAuthObjectFromJwt",
5152
"getMachineTokenType",
5253
"isMachineToken",

packages/backend/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,3 +162,4 @@ export type {
162162
* Auth objects
163163
*/
164164
export type { AuthObject } from './tokens/authObjects';
165+
export type { SessionAuthObject, MachineAuthObject } from './tokens/types';

packages/backend/src/internal.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,13 @@ export { createAuthenticateRequest } from './tokens/factory';
77

88
export { debugRequestState } from './tokens/request';
99

10-
export type { AuthenticateRequestOptions, OrganizationSyncOptions } from './tokens/types';
10+
export type {
11+
AuthenticateRequestOptions,
12+
OrganizationSyncOptions,
13+
InferAuthObjectFromToken,
14+
InferAuthObjectFromTokenArray,
15+
GetAuthFn,
16+
} from './tokens/types';
1117

1218
export { TokenType } from './tokens/tokenTypes';
1319
export type { SessionTokenType, MachineTokenType } from './tokens/tokenTypes';
@@ -26,6 +32,7 @@ export {
2632
authenticatedMachineObject,
2733
unauthenticatedMachineObject,
2834
getAuthObjectFromJwt,
35+
getAuthObjectForAcceptedToken,
2936
} from './tokens/authObjects';
3037

3138
export { AuthStatus } from './tokens/authStatus';
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
import { assertType, test } from 'vitest';
2+
3+
import type { AuthObject } from '../authObjects';
4+
import type { GetAuthFn, MachineAuthObject, SessionAuthObject } from '../types';
5+
6+
// Across our SDKs, we have a getAuth() function
7+
const getAuth: GetAuthFn<Request> = (_request: any, _options: any) => {
8+
return {} as any;
9+
};
10+
11+
test('infers the correct AuthObject type for each accepted token type', () => {
12+
const request = new Request('https://example.com');
13+
14+
// Session token by default
15+
assertType<SessionAuthObject>(getAuth(request));
16+
17+
// Individual token types
18+
assertType<SessionAuthObject>(getAuth(request, { acceptsToken: 'session_token' }));
19+
assertType<MachineAuthObject<'api_key'>>(getAuth(request, { acceptsToken: 'api_key' }));
20+
assertType<MachineAuthObject<'machine_token'>>(getAuth(request, { acceptsToken: 'machine_token' }));
21+
assertType<MachineAuthObject<'oauth_token'>>(getAuth(request, { acceptsToken: 'oauth_token' }));
22+
23+
// Array of token types
24+
assertType<SessionAuthObject | MachineAuthObject<'machine_token'>>(
25+
getAuth(request, { acceptsToken: ['session_token', 'machine_token'] }),
26+
);
27+
assertType<MachineAuthObject<'machine_token' | 'oauth_token'>>(
28+
getAuth(request, { acceptsToken: ['machine_token', 'oauth_token'] }),
29+
);
30+
31+
// Any token type
32+
assertType<AuthObject>(getAuth(request, { acceptsToken: 'any' }));
33+
});
34+
35+
test('verifies correct properties exist for each token type', () => {
36+
const request = new Request('https://example.com');
37+
38+
// Session token should have userId
39+
const sessionAuth = getAuth(request, { acceptsToken: 'session_token' });
40+
assertType<string | null>(sessionAuth.userId);
41+
42+
// All machine tokens should have id and subject
43+
const apiKeyAuth = getAuth(request, { acceptsToken: 'api_key' });
44+
const machineTokenAuth = getAuth(request, { acceptsToken: 'machine_token' });
45+
const oauthTokenAuth = getAuth(request, { acceptsToken: 'oauth_token' });
46+
47+
assertType<string | null>(apiKeyAuth.id);
48+
assertType<string | null>(machineTokenAuth.id);
49+
assertType<string | null>(oauthTokenAuth.id);
50+
assertType<string | null>(apiKeyAuth.subject);
51+
assertType<string | null>(machineTokenAuth.subject);
52+
assertType<string | null>(oauthTokenAuth.subject);
53+
54+
// Only api_key and machine_token should have name and claims
55+
assertType<string | null>(apiKeyAuth.name);
56+
assertType<Record<string, any> | null>(apiKeyAuth.claims);
57+
58+
assertType<string | null>(machineTokenAuth.name);
59+
assertType<Record<string, any> | null>(machineTokenAuth.claims);
60+
61+
// oauth_token should NOT have name and claims
62+
// @ts-expect-error oauth_token does not have name property
63+
void oauthTokenAuth.name;
64+
// @ts-expect-error oauth_token does not have claims property
65+
void oauthTokenAuth.claims;
66+
});
67+
68+
test('verifies discriminated union works correctly with acceptsToken: any', () => {
69+
const request = new Request('https://example.com');
70+
71+
const auth = getAuth(request, { acceptsToken: 'any' });
72+
73+
if (auth.tokenType === 'session_token') {
74+
// Should be SessionAuthObject - has userId
75+
assertType<string | null>(auth.userId);
76+
// Should NOT have machine token properties
77+
// @ts-expect-error session_token does not have id property
78+
void auth.id;
79+
} else if (auth.tokenType === 'api_key') {
80+
// Should be AuthenticatedMachineObject<'api_key'> - has id, name, claims
81+
assertType<string | null>(auth.id);
82+
assertType<string | null>(auth.name);
83+
assertType<Record<string, any> | null>(auth.claims);
84+
// Should NOT have session token properties
85+
// @ts-expect-error api_key does not have userId property
86+
void auth.userId;
87+
} else if (auth.tokenType === 'machine_token') {
88+
// Should be AuthenticatedMachineObject<'machine_token'> - has id, name, claims
89+
assertType<string | null>(auth.id);
90+
assertType<string | null>(auth.name);
91+
assertType<Record<string, any> | null>(auth.claims);
92+
// Should NOT have session token properties
93+
// @ts-expect-error machine_token does not have userId property
94+
void auth.userId;
95+
} else if (auth.tokenType === 'oauth_token') {
96+
// Should be AuthenticatedMachineObject<'oauth_token'> - has id but NOT name/claims
97+
assertType<string | null>(auth.id);
98+
// Should NOT have name or claims
99+
// @ts-expect-error oauth_token does not have name property
100+
void auth.name;
101+
// @ts-expect-error oauth_token does not have claims property
102+
void auth.claims;
103+
// Should NOT have session token properties
104+
// @ts-expect-error oauth_token does not have userId property
105+
void auth.userId;
106+
}
107+
});

0 commit comments

Comments
 (0)