55 Session ,
66} from '@clerk/backend-core' ;
77import Cookies from 'cookies' ;
8+ import deepmerge from 'deepmerge' ;
89import type { NextFunction , Request , Response } from 'express' ;
9- import got from 'got' ;
10+ import got , { OptionsOfJSONResponseBody } from 'got' ;
1011import jwt , { JwtPayload } from 'jsonwebtoken' ;
11- import jwks , { JwksClient } from 'jwks-rsa' ;
12+ import jwks from 'jwks-rsa' ;
1213import querystring from 'querystring' ;
1314
1415import { SupportMessages } from './constants/SupportMessages' ;
@@ -21,7 +22,7 @@ const defaultApiKey = process.env.CLERK_API_KEY || '';
2122const defaultApiVersion = process . env . CLERK_API_VERSION || 'v1' ;
2223const defaultServerApiUrl =
2324 process . env . CLERK_API_URL || 'https://api.clerk.dev' ;
24- const defaultJWKSCacheMaxAge = 3600000 ; // 1 hour
25+ const JWKS_MAX_AGE = 3600000 ; // 1 hour
2526const packageRepo = 'https://github.com/clerkinc/clerk-sdk-node' ;
2627
2728export type MiddlewareOptions = {
@@ -54,13 +55,8 @@ const verifySignature = async (
5455 return await crypto . subtle . verify ( algorithm , key , signature , data ) ;
5556} ;
5657
57- /** Base initialization */
58-
59- const nodeBase = new Base ( importKey , verifySignature , decodeBase64 ) ;
60-
6158export default class Clerk extends ClerkBackendAPI {
62- // private _restClient: RestClient;
63- private _jwksClient : JwksClient ;
59+ base : Base ;
6460
6561 // singleton instance
6662 static _instance : Clerk ;
@@ -70,7 +66,7 @@ export default class Clerk extends ClerkBackendAPI {
7066 serverApiUrl = defaultServerApiUrl ,
7167 apiVersion = defaultApiVersion ,
7268 httpOptions = { } ,
73- jwksCacheMaxAge = defaultJWKSCacheMaxAge ,
69+ jwksCacheMaxAge = JWKS_MAX_AGE ,
7470 } : {
7571 apiKey ?: string ;
7672 serverApiUrl ?: string ;
@@ -82,16 +78,22 @@ export default class Clerk extends ClerkBackendAPI {
8278 url ,
8379 { method, authorization, contentType, userAgent, body }
8480 ) => {
85- return got ( url , {
86- method,
87- responseType : 'json' ,
88- headers : {
89- authorization,
90- 'Content-Type' : contentType ,
91- 'User-Agent' : userAgent ,
81+ const finalHTTPOptions = deepmerge (
82+ {
83+ method,
84+ responseType : 'json' ,
85+ headers : {
86+ authorization,
87+ 'Content-Type' : contentType ,
88+ 'User-Agent' : userAgent ,
89+ } ,
90+ // @ts -ignore
91+ ...( body && { body : querystring . stringify ( body ) } ) ,
9292 } ,
93- ...( body && { body : querystring . stringify ( body ) } ) ,
94- } ) ;
93+ httpOptions
94+ ) as OptionsOfJSONResponseBody ;
95+
96+ return got ( url , finalHTTPOptions ) ;
9597 } ;
9698
9799 super ( {
@@ -108,21 +110,48 @@ export default class Clerk extends ClerkBackendAPI {
108110 throw Error ( SupportMessages . API_KEY_NOT_FOUND ) ;
109111 }
110112
111- // TBD: Add jwk client as an argument to getAuthState ?
112- // this._jwksClient = jwks({
113- // jwksUri: `${serverApiUrl}/${apiVersion}/jwks`,
114- // requestHeaders: {
115- // Authorization: `Bearer ${apiKey}`,
116- // },
117- // timeout: 5000,
118- // cache: true,
119- // cacheMaxAge: jwksCacheMaxAge,
120- // });
121-
122- // const key = await this._jwksClient.getSigningKey(decoded.header.kid);
123- // const verified = jwt.verify(token, key.getPublicKey(), {
124- // algorithms: algorithms as jwt.Algorithm[],
125- // }) as JwtPayload;
113+ const loadCryptoKey = async ( token : string ) => {
114+ const decoded = jwt . decode ( token , { complete : true } ) ;
115+ if ( ! decoded ) {
116+ throw new Error ( `Failed to decode token: ${ token } ` ) ;
117+ }
118+
119+ const jwksClient = jwks ( {
120+ jwksUri : `${ serverApiUrl } /${ apiVersion } /jwks` ,
121+ requestHeaders : {
122+ Authorization : `Bearer ${ defaultApiKey } ` ,
123+ } ,
124+ timeout : 5000 ,
125+ cache : true ,
126+ cacheMaxAge : jwksCacheMaxAge ,
127+ } ) ;
128+
129+ const encoder = new TextEncoder ( ) ;
130+
131+ return await crypto . subtle . importKey (
132+ 'raw' ,
133+ encoder . encode (
134+ (
135+ await jwksClient . getSigningKey ( decoded . header . kid )
136+ ) . getPublicKey ( ) as string
137+ ) ,
138+ {
139+ name : 'RSASSA-PKCS1-v1_5' ,
140+ hash : 'SHA-256' ,
141+ } ,
142+ true ,
143+ [ 'verify' ]
144+ ) ;
145+ } ;
146+
147+ /** Base initialization */
148+
149+ this . base = new Base (
150+ importKey ,
151+ verifySignature ,
152+ decodeBase64 ,
153+ loadCryptoKey
154+ ) ;
126155 }
127156
128157 // For use as singleton, always returns the same instance
@@ -172,18 +201,19 @@ export default class Clerk extends ClerkBackendAPI {
172201 const cookies = new Cookies ( req , res ) ;
173202
174203 try {
175- const { status, session, interstitial } = await nodeBase . getAuthState ( {
176- cookieToken : cookies . get ( '__session' ) as string ,
177- clientUat : cookies . get ( '__client_uat' ) as string ,
178- headerToken : req . headers . authorization ?. replace ( 'Bearer ' , '' ) ,
179- origin : req . headers . origin ,
180- host : req . headers . host ,
181- forwardedPort : req . headers [ 'x-forwarded-port' ] as string ,
182- forwardedHost : req . headers [ 'x-forwarded-host' ] as string ,
183- referrer : req . headers . referer ,
184- userAgent : req . headers [ 'user-agent' ] as string ,
185- fetchInterstitial : ( ) => this . fetchInterstitial ( ) ,
186- } ) ;
204+ const { status, session, interstitial, sessionClaims } =
205+ await this . base . getAuthState ( {
206+ cookieToken : cookies . get ( '__session' ) as string ,
207+ clientUat : cookies . get ( '__client_uat' ) as string ,
208+ headerToken : req . headers . authorization ?. replace ( 'Bearer ' , '' ) ,
209+ origin : req . headers . origin ,
210+ host : req . headers . host ,
211+ forwardedPort : req . headers [ 'x-forwarded-port' ] as string ,
212+ forwardedHost : req . headers [ 'x-forwarded-host' ] as string ,
213+ referrer : req . headers . referer ,
214+ userAgent : req . headers [ 'user-agent' ] as string ,
215+ fetchInterstitial : ( ) => this . fetchInterstitial ( ) ,
216+ } ) ;
187217
188218 if ( status === AuthStatus . SignedOut ) {
189219 return signedOut ( ) ;
@@ -192,6 +222,8 @@ export default class Clerk extends ClerkBackendAPI {
192222 if ( status === AuthStatus . SignedIn ) {
193223 // @ts -ignore
194224 req . session = session ;
225+ // @ts -ignore
226+ req . sessionClaims = sessionClaims ;
195227 return next ( ) ;
196228 }
197229
0 commit comments