@@ -29,30 +29,70 @@ export type AuthenticatedEnvironment = Optional<
2929 "orgMember"
3030> ;
3131
32- export type ApiAuthenticationResult = {
32+ export type ApiAuthenticationResult =
33+ | ApiAuthenticationResultSuccess
34+ | ApiAuthenticationResultFailure ;
35+
36+ export type ApiAuthenticationResultSuccess = {
37+ ok : true ;
3338 apiKey : string ;
3439 type : "PUBLIC" | "PRIVATE" | "PUBLIC_JWT" ;
3540 environment : AuthenticatedEnvironment ;
3641 scopes ?: string [ ] ;
3742} ;
3843
44+ export type ApiAuthenticationResultFailure = {
45+ ok : false ;
46+ error : string ;
47+ } ;
48+
49+ /**
50+ * @deprecated Use `authenticateApiRequestWithFailure` instead.
51+ */
3952export async function authenticateApiRequest (
4053 request : Request ,
4154 options : { allowPublicKey ?: boolean ; allowJWT ?: boolean } = { }
42- ) : Promise < ApiAuthenticationResult | undefined > {
55+ ) : Promise < ApiAuthenticationResultSuccess | undefined > {
4356 const apiKey = getApiKeyFromRequest ( request ) ;
4457
4558 if ( ! apiKey ) {
4659 return ;
4760 }
4861
49- return authenticateApiKey ( apiKey , options ) ;
62+ const authentication = await authenticateApiKey ( apiKey , options ) ;
63+
64+ return authentication ;
5065}
5166
67+ /**
68+ * This method is the same as `authenticateApiRequest` but it returns a failure result instead of undefined.
69+ * It should be used from now on to ensure that the API key is always validated and provide a failure result.
70+ */
71+ export async function authenticateApiRequestWithFailure (
72+ request : Request ,
73+ options : { allowPublicKey ?: boolean ; allowJWT ?: boolean } = { }
74+ ) : Promise < ApiAuthenticationResult > {
75+ const apiKey = getApiKeyFromRequest ( request ) ;
76+
77+ if ( ! apiKey ) {
78+ return {
79+ ok : false ,
80+ error : "Invalid API Key" ,
81+ } ;
82+ }
83+
84+ const authentication = await authenticateApiKeyWithFailure ( apiKey , options ) ;
85+
86+ return authentication ;
87+ }
88+
89+ /**
90+ * @deprecated Use `authenticateApiKeyWithFailure` instead.
91+ */
5292export async function authenticateApiKey (
5393 apiKey : string ,
5494 options : { allowPublicKey ?: boolean ; allowJWT ?: boolean } = { }
55- ) : Promise < ApiAuthenticationResult | undefined > {
95+ ) : Promise < ApiAuthenticationResultSuccess | undefined > {
5696 const result = getApiKeyResult ( apiKey ) ;
5797
5898 if ( ! result ) {
@@ -70,30 +110,120 @@ export async function authenticateApiKey(
70110 switch ( result . type ) {
71111 case "PUBLIC" : {
72112 const environment = await findEnvironmentByPublicApiKey ( result . apiKey ) ;
73- if ( ! environment ) return ;
113+ if ( ! environment ) {
114+ return ;
115+ }
116+
74117 return {
118+ ok : true ,
75119 ...result ,
76120 environment,
77121 } ;
78122 }
79123 case "PRIVATE" : {
80124 const environment = await findEnvironmentByApiKey ( result . apiKey ) ;
81- if ( ! environment ) return ;
125+ if ( ! environment ) {
126+ return ;
127+ }
128+
82129 return {
130+ ok : true ,
83131 ...result ,
84132 environment,
85133 } ;
86134 }
87135 case "PUBLIC_JWT" : {
88136 const validationResults = await validatePublicJwtKey ( result . apiKey ) ;
89137
90- if ( ! validationResults ) {
138+ if ( ! validationResults . ok ) {
91139 return ;
92140 }
93141
94142 const parsedClaims = ClaimsSchema . safeParse ( validationResults . claims ) ;
95143
96144 return {
145+ ok : true ,
146+ ...result ,
147+ environment : validationResults . environment ,
148+ scopes : parsedClaims . success ? parsedClaims . data . scopes : [ ] ,
149+ } ;
150+ }
151+ }
152+ }
153+
154+ /**
155+ * This method is the same as `authenticateApiKey` but it returns a failure result instead of undefined.
156+ * It should be used from now on to ensure that the API key is always validated and provide a failure result.
157+ */
158+ export async function authenticateApiKeyWithFailure (
159+ apiKey : string ,
160+ options : { allowPublicKey ?: boolean ; allowJWT ?: boolean } = { }
161+ ) : Promise < ApiAuthenticationResult > {
162+ const result = getApiKeyResult ( apiKey ) ;
163+
164+ if ( ! result ) {
165+ return {
166+ ok : false ,
167+ error : "Invalid API Key" ,
168+ } ;
169+ }
170+
171+ if ( ! options . allowPublicKey && result . type === "PUBLIC" ) {
172+ return {
173+ ok : false ,
174+ error : "Public API keys are not allowed for this request" ,
175+ } ;
176+ }
177+
178+ if ( ! options . allowJWT && result . type === "PUBLIC_JWT" ) {
179+ return {
180+ ok : false ,
181+ error : "Public JWT API keys are not allowed for this request" ,
182+ } ;
183+ }
184+
185+ switch ( result . type ) {
186+ case "PUBLIC" : {
187+ const environment = await findEnvironmentByPublicApiKey ( result . apiKey ) ;
188+ if ( ! environment ) {
189+ return {
190+ ok : false ,
191+ error : "Invalid API Key" ,
192+ } ;
193+ }
194+
195+ return {
196+ ok : true ,
197+ ...result ,
198+ environment,
199+ } ;
200+ }
201+ case "PRIVATE" : {
202+ const environment = await findEnvironmentByApiKey ( result . apiKey ) ;
203+ if ( ! environment ) {
204+ return {
205+ ok : false ,
206+ error : "Invalid API Key" ,
207+ } ;
208+ }
209+
210+ return {
211+ ok : true ,
212+ ...result ,
213+ environment,
214+ } ;
215+ }
216+ case "PUBLIC_JWT" : {
217+ const validationResults = await validatePublicJwtKey ( result . apiKey ) ;
218+
219+ if ( ! validationResults . ok ) {
220+ return validationResults ;
221+ }
222+
223+ const parsedClaims = ClaimsSchema . safeParse ( validationResults . claims ) ;
224+
225+ return {
226+ ok : true ,
97227 ...result ,
98228 environment : validationResults . environment ,
99229 scopes : parsedClaims . success ? parsedClaims . data . scopes : [ ] ,
@@ -207,6 +337,10 @@ export async function authenticatedEnvironmentForAuthentication(
207337
208338 switch ( auth . type ) {
209339 case "apiKey" : {
340+ if ( ! auth . result . ok ) {
341+ throw json ( { error : auth . result . error } , { status : 401 } ) ;
342+ }
343+
210344 if ( auth . result . environment . project . externalRef !== projectRef ) {
211345 throw json (
212346 {
@@ -337,6 +471,14 @@ export async function validateJWTTokenAndRenew<T extends z.ZodTypeAny>(
337471 return ;
338472 }
339473
474+ if ( ! authenticatedEnv . ok ) {
475+ logger . error ( "Failed to renew JWT token, invalid API key" , {
476+ error : error . message ,
477+ } ) ;
478+
479+ return ;
480+ }
481+
340482 const payload = payloadSchema . safeParse ( error . payload ) ;
341483
342484 if ( ! payload . success ) {
0 commit comments