Skip to content

Commit 4e69553

Browse files
committed
feat(clerk-sdk-node): Deprecate Session named middleware, introduce withAuth, requireAuth
feat(edge): Rename withSession to withAuth feat(backend-core): Expose JWTPayload type
1 parent 052ff1e commit 4e69553

9 files changed

Lines changed: 107 additions & 59 deletions

File tree

packages/backend-core/src/index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,5 @@ export * from './api/ClerkBackendAPI';
33
export * from './api/resources';
44
export type { ClerkFetcher } from './api/utils/RestClient';
55
export type { Session } from './api/resources/Session';
6-
export type { Nullable } from "./util/nullable"
6+
export type { Nullable } from './util/nullable';
7+
export type { JWTPayload } from './util/types';

packages/edge/README.md

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -24,13 +24,13 @@ yarn add @clerk/edge
2424
Methods for supported platforms can be imported from the specific path:
2525

2626
```ts
27-
import { withSession } from '@clerk/edge/vercel-edge';
27+
import { withAuth } from '@clerk/edge/vercel-edge';
2828

2929
async function handler(req, event) {
3030
// ...
3131
}
3232

33-
export const middleware = withSession(handler);
33+
export const middleware = withAuth(handler);
3434
```
3535

3636
## Supported platforms
@@ -42,32 +42,33 @@ Currently supported environments/platforms:
4242
To use with [Edge Functions](https://vercel.com/docs/concepts/functions/edge-functions) :
4343

4444
```ts
45-
import { withSession } from '@clerk/edge/vercel-edge';
45+
import { withAuth } from '@clerk/edge/vercel-edge';
4646

4747
async function handler(req, event) {
4848
// ...
4949
}
5050

51-
export const middleware = withSession(handler);
51+
export const middleware = withAuth(handler);
5252
```
5353

5454
Supported methods:
5555

56-
- `withSession`
56+
- `withAuth`
5757
- `verifySessionToken`
5858
- Resources API through `ClerkAPI`
5959

6060
### Validate the Authorized Party of a session token
61+
6162
Clerk's JWT session token, contains the azp claim, which equals the Origin of the request during token generation. You can provide the middlewares with a list of whitelisted origins to verify against, to protect your application of the subdomain cookie leaking attack. You can find an example below:
6263

6364
```ts
64-
import { withSession } from '@clerk/edge/vercel-edge';
65+
import { withAuth } from '@clerk/edge/vercel-edge';
6566

66-
const authorizedParties = ['http://localhost:3000', 'https://example.com']
67+
const authorizedParties = ['http://localhost:3000', 'https://example.com'];
6768

6869
async function handler(req, event) {
6970
// ...
7071
}
7172

72-
export const middleware = withSession(handler, { authorizedParties });
73+
export const middleware = withAuth(handler, { authorizedParties });
7374
```

packages/edge/src/vercel-edge.ts

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import type { JWTPayload } from '@clerk/backend-core';
12
import {
23
AuthStatus,
34
Base,
@@ -11,7 +12,7 @@ import { LIB_NAME, LIB_VERSION } from './info';
1112

1213
type Middleware = (
1314
req: NextRequest,
14-
event: NextFetchEvent
15+
event: NextFetchEvent,
1516
) => Response | void | Promise<Response | void>;
1617

1718
/**
@@ -33,7 +34,7 @@ const verifySignature = async (
3334
algorithm: Algorithm,
3435
key: CryptoKey,
3536
signature: Uint8Array,
36-
data: Uint8Array
37+
data: Uint8Array,
3738
) => {
3839
return await crypto.subtle.verify(algorithm, key, signature, data);
3940
};
@@ -64,7 +65,7 @@ export const ClerkAPI = new ClerkBackendAPI({
6465
'X-Clerk-SDK': `vercel-edge/${LIB_VERSION}`,
6566
},
6667
...(body && { body: JSON.stringify(body) }),
67-
}).then((body) => body.json());
68+
}).then(body => body.json());
6869
},
6970
});
7071

@@ -75,16 +76,22 @@ async function fetchInterstitial() {
7576

7677
/** Export middleware wrapper */
7778

78-
export type NextRequestWithSession = NextRequest & { session: Session };
79+
export type NextRequestWithAuth = NextRequest & {
80+
session?: Session;
81+
sessionClaims?: JWTPayload;
82+
};
7983

8084
export type MiddlewareOptions = {
8185
authorizedParties?: string[];
8286
};
8387

84-
export function withSession(handler: Middleware, { authorizedParties }: MiddlewareOptions = { authorizedParties: []}) {
88+
export function withAuth(
89+
handler: Middleware,
90+
{ authorizedParties }: MiddlewareOptions = { authorizedParties: [] },
91+
) {
8592
return async function clerkAuth(req: NextRequest, event: NextFetchEvent) {
86-
const { status, session, interstitial } = await vercelEdgeBase.getAuthState(
87-
{
93+
const { status, session, interstitial, sessionClaims } =
94+
await vercelEdgeBase.getAuthState({
8895
cookieToken: req.cookies['__session'],
8996
clientUat: req.cookies['__client_uat'],
9097
headerToken: req.headers.get('authorization'),
@@ -95,8 +102,7 @@ export function withSession(handler: Middleware, { authorizedParties }: Middlewa
95102
referrer: req.headers.get('referrer'),
96103
authorizedParties: authorizedParties,
97104
fetchInterstitial,
98-
}
99-
);
105+
});
100106

101107
if (status === AuthStatus.SignedOut) {
102108
return handler(req, event);
@@ -112,6 +118,8 @@ export function withSession(handler: Middleware, { authorizedParties }: Middlewa
112118
if (status === AuthStatus.SignedIn) {
113119
// @ts-ignore
114120
req.session = session;
121+
// @ts-ignore
122+
req.sessionClaims = sessionClaims;
115123
return handler(req, event);
116124
}
117125
};

packages/react/src/info.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11

22
/** DO NOT EDIT: This file is automatically generated by ../scripts/info.js */
3-
export const LIB_VERSION = '2.11.0';
4-
export const LIB_NAME = '@clerk/clerk-react';
3+
export const LIB_VERSION='2.11.0';
4+
export const LIB_NAME='@clerk/clerk-react';

packages/sdk-node/README.md

Lines changed: 25 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -547,36 +547,36 @@ The error handling is pretty generic at the moment but more fine-grained errors
547547

548548
## Express middleware
549549

550-
For usage with <a href="https://github.com/expressjs/express" target="_blank">Express</a>, this package also exports `ClerkExpressWithSession` (lax) & `ClerkExpressRequireSession` (strict)
550+
For usage with <a href="https://github.com/expressjs/express" target="_blank">Express</a>, this package also exports `ClerkExpressWithAuth` (lax) & `ClerkExpressRequireAuth` (strict)
551551
middlewares that can be used in the standard manner:
552552

553553
```ts
554-
import { ClerkWithSession } from '@clerk/clerk-sdk-node';
554+
import { ClerkWithAuth } from '@clerk/clerk-sdk-node';
555555

556556
// Initialize express app the usual way
557557

558-
app.use(ClerkWithSession());
558+
app.use(ClerkWithAuth());
559559
```
560560

561-
The `ClerkWithSession` middleware will set the Clerk session on the request object as `req.session` and then call the next middleware.
561+
The `ClerkWithAuth` middleware will set the Clerk session on the request object as `req.session` and then call the next middleware.
562562

563563
You can then implement your own logic for handling a logged-in or logged-out user in your express endpoints or custom
564564
middleware, depending on whether your users are trying to access a public or protected resource.
565565

566566
If you want to use the express middleware of your custom `Clerk` instance, you can use:
567567

568568
```ts
569-
app.use(clerk.expressWithSession());
569+
app.use(clerk.expressWithAuth());
570570
```
571571

572572
Where `clerk` is your own instance.
573573

574574
If you prefer that the middleware renders a 401 (Unauthenticated) itself, you can use the following variant instead:
575575

576576
```ts
577-
import { ClerkExpressRequireSession } from '@clerk/clerk-sdk-node';
577+
import { ClerkExpressRequireAuth } from '@clerk/clerk-sdk-node';
578578

579-
app.use(ClerkExpressRequireSession());
579+
app.use(ClerkExpressRequireAuth());
580580
```
581581

582582
### onError option
@@ -635,51 +635,48 @@ The current package also offers a way of making
635635
your <a href="https://nextjs.org/docs/api-routes/api-middlewares" target="_blank">Next.js api middleware</a> aware of the Clerk Session.
636636

637637
You can define your handler function with the usual signature (`function handler(req, res) {}`) then wrap it
638-
with `withSession`:
638+
with `withAuth`:
639639

640640
```ts
641-
import { withSession, WithSessionProp } from '@clerk/clerk-sdk-node';
641+
import { withAuth, WithAuthProp } from '@clerk/clerk-sdk-node';
642642
```
643643

644644
Note: Since the request will be extended with a session property, the signature of your handler in TypeScript would be:
645645

646646
```ts
647-
function handler(req: WithSessionProp<NextApiRequest>, res: NextApiResponse) {
647+
function handler(req: WithAuthProp<NextApiRequest>, res: NextApiResponse) {
648648
if (req.session) {
649649
// do something with session.userId
650650
} else {
651651
// Respond with 401 or similar
652652
}
653653
}
654654

655-
export withSession(handler);
655+
export withAuth(handler);
656656
```
657657

658658
You can also pass an `onError` handler to the underlying Express middleware that is called (see previous section):
659659

660660
```ts
661-
export withSession(handler, { clerk, onError: error => console.log(error) });
661+
export withAuth(handler, { clerk, onError: error => console.log(error) });
662662
```
663663

664664
In case you would like the request to be rejected automatically when no session exists,
665665
without having to implement such logic yourself, you can opt for the stricter variant:
666666

667667
```ts
668-
import clerk, {
669-
requireSession,
670-
RequireSessionProp,
671-
} from '@clerk/clerk-sdk-node';
668+
import clerk, { requireAuth, RequireAuthProp } from '@clerk/clerk-sdk-node';
672669
```
673670

674671
In this case your handler can be even simpler because the existence of the session can be assumed, otherwise the
675672
execution will never reach your handler:
676673

677674
```ts
678-
function handler(req: RequireSessionProp<NextApiRequest>, res: NextApiResponse) {
675+
function handler(req: RequireAuthProp<NextApiRequest>, res: NextApiResponse) {
679676
// do something with session.userId
680677
}
681678

682-
export requireSession(handler, { clerk, onError });
679+
export requireAuth(handler, { clerk, onError });
683680
```
684681

685682
Note that by default the error returned will be the Clerk server error encountered (or in case of misconfiguration, the error raised by the SDK itself).
@@ -711,34 +708,35 @@ The aforementioned usage pertains to the singleton case. If you would like to us
711708
yourself (e.g. named `clerk`), you can use the following syntax instead:
712709

713710
```ts
714-
export clerk.withSession(handler);
711+
export clerk.withAuth(handler);
715712
// OR
716-
export clerk.requireSession(handler);
713+
export clerk.requireAuth(handler);
717714
```
718715

719716
## Validate the Authorized Party of a session token
717+
720718
Clerk's JWT session token, contains the azp claim, which equals the Origin of the request during token generation. You can provide the middlewares with a list of whitelisted origins to verify against, to protect your application of the subdomain cookie leaking attack. You can find an example below:
721719

722720
### Express
723721

724722
```ts
725-
import { ClerkExpressRequireSession } from '@clerk/clerk-sdk-node';
723+
import { ClerkExpressRequireAuth } from '@clerk/clerk-sdk-node';
726724

727-
const authorizedParties = ['http://localhost:3000', 'https://example.com']
725+
const authorizedParties = ['http://localhost:3000', 'https://example.com'];
728726

729-
app.use(ClerkExpressRequireSession({ authorizedParties }));
727+
app.use(ClerkExpressRequireAuth({ authorizedParties }));
730728
```
731729

732730
### Next
733731

734732
```ts
735733
const authorizedParties = ['http://localhost:3000', 'https://example.com']
736734

737-
function handler(req: RequireSessionProp<NextApiRequest>, res: NextApiResponse) {
735+
function handler(req: RequireAuthProp<NextApiRequest>, res: NextApiResponse) {
738736
// do something with session.userId
739737
}
740738

741-
export requireSession(handler, { authorizedParties });
739+
export requireAuth(handler, { authorizedParties });
742740
```
743741

744742
## Troubleshooting
@@ -753,10 +751,10 @@ Please consult the following check-list for some potential quick fixes:
753751
- In development mode, do your frontend & API reside on the same domain? Unless the clerk `__session` is sent to your API server, the SDK will fail to authenticate your user.
754752
- If you are still experiencing issues, it is advisable to set the `CLERK_LOGGING` environment variable to `true` to get additional logging output that may help identify the issue.
755753

756-
Note: The strict middleware variants (i.e. the "require session" variants) will produce an erroneous response if the user is not signed in.
754+
Note: The strict middleware variants (i.e. the "require auth" variants) will produce an erroneous response if the user is not signed in.
757755
Please ensure you are not mounting them on routes that are meant to be publicly accessible.
758756

759757
## Feedback / Issue reporting
760758

761759
Please report issues or open feature request in
762-
the [github issue section](https://github.com/clerkinc/clerk-sdk-node/issues).
760+
the [github issue section](https://github.com/clerkinc/javascript/issues).

packages/sdk-node/examples/express/src/server.ts

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import {
2-
ClerkExpressRequireSession,
3-
ClerkExpressWithSession,
4-
WithSessionProp,
2+
ClerkExpressRequireAuth,
3+
ClerkExpressWithAuth,
4+
RequireAuthProp,
5+
WithAuthProp,
56
} from '@clerk/clerk-sdk-node';
67
import dotenv from 'dotenv';
78
import express, { Application, Request, Response } from 'express';
@@ -14,17 +15,17 @@ const app: Application = express();
1415
// Root path uses lax middleware
1516
app.get(
1617
'/',
17-
ClerkExpressWithSession(),
18-
(req: WithSessionProp<Request>, res: Response) => {
18+
ClerkExpressWithAuth(),
19+
(req: WithAuthProp<Request>, res: Response) => {
1920
res.json(req.session || 'No session detected');
2021
}
2122
);
2223

2324
// /require-session path uses strict middleware
2425
app.get(
2526
'/require-session',
26-
ClerkExpressRequireSession(),
27-
(req: WithSessionProp<Request>, res) => {
27+
ClerkExpressRequireAuth(),
28+
(req: RequireAuthProp<Request>, res) => {
2829
res.json(req.session);
2930
}
3031
);

packages/sdk-node/src/Clerk.ts

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,14 @@
33
Base,
44
ClerkBackendAPI,
55
ClerkFetcher,
6+
JWTPayload,
67
Session,
78
} from '@clerk/backend-core';
89
import Cookies from 'cookies';
910
import deepmerge from 'deepmerge';
1011
import type { NextFunction, Request, Response } from 'express';
1112
import got, { OptionsOfUnknownResponseBody } from 'got';
12-
import jwt, { JwtPayload } from 'jsonwebtoken';
13+
import jwt from 'jsonwebtoken';
1314
import jwks, { JwksClient } from 'jwks-rsa';
1415
import querystring from 'querystring';
1516

@@ -31,10 +32,24 @@ export type MiddlewareOptions = {
3132
authorizedParties?: string[];
3233
};
3334

35+
/** @deprecated DEPRECATED Use WithAuthProp Est. 2.10.0 */
3436
export type WithSessionProp<T> = T & { session?: Session };
37+
/** @deprecated DEPRECATED Use RequireAuthProp Est. 2.10.0 */
3538
export type RequireSessionProp<T> = T & { session: Session };
36-
export type WithSessionClaimsProp<T> = T & { sessionClaims?: JwtPayload };
37-
export type RequireSessionClaimsProp<T> = T & { sessionClaims: JwtPayload };
39+
/** @deprecated DEPRECATED Use WithAuthProp Est. 2.10.0 */
40+
export type WithSessionClaimsProp<T> = T & { sessionClaims?: JWTPayload };
41+
/** @deprecated DEPRECATED Use RequireAuthProp Est. 2.10.0 */
42+
export type RequireSessionClaimsProp<T> = T & { sessionClaims: JWTPayload };
43+
44+
export type WithAuthProp<T> = T & {
45+
session?: Session;
46+
sessionClaims?: JWTPayload;
47+
};
48+
49+
export type RequireAuthProp<T> = T & {
50+
session: Session;
51+
sessionClaims: JWTPayload;
52+
};
3853

3954
import { Crypto, CryptoKey } from '@peculiar/webcrypto';
4055

@@ -159,7 +174,7 @@ export default class Clerk extends ClerkBackendAPI {
159174
async verifyToken(
160175
token: string,
161176
authorizedParties?: string[]
162-
): Promise<JwtPayload> {
177+
): Promise<JWTPayload> {
163178
const decoded = jwt.decode(token, { complete: true });
164179
if (!decoded) {
165180
throw new Error(`Failed to verify token: ${token}`);

0 commit comments

Comments
 (0)