@@ -29,30 +29,70 @@ export type AuthenticatedEnvironment = Optional<
29
29
"orgMember"
30
30
> ;
31
31
32
- export type ApiAuthenticationResult = {
32
+ export type ApiAuthenticationResult =
33
+ | ApiAuthenticationResultSuccess
34
+ | ApiAuthenticationResultFailure ;
35
+
36
+ export type ApiAuthenticationResultSuccess = {
37
+ ok : true ;
33
38
apiKey : string ;
34
39
type : "PUBLIC" | "PRIVATE" | "PUBLIC_JWT" ;
35
40
environment : AuthenticatedEnvironment ;
36
41
scopes ?: string [ ] ;
37
42
} ;
38
43
44
+ export type ApiAuthenticationResultFailure = {
45
+ ok : false ;
46
+ error : string ;
47
+ } ;
48
+
49
+ /**
50
+ * @deprecated Use `authenticateApiRequestWithFailure` instead.
51
+ */
39
52
export async function authenticateApiRequest (
40
53
request : Request ,
41
54
options : { allowPublicKey ?: boolean ; allowJWT ?: boolean } = { }
42
- ) : Promise < ApiAuthenticationResult | undefined > {
55
+ ) : Promise < ApiAuthenticationResultSuccess | undefined > {
43
56
const apiKey = getApiKeyFromRequest ( request ) ;
44
57
45
58
if ( ! apiKey ) {
46
59
return ;
47
60
}
48
61
49
- return authenticateApiKey ( apiKey , options ) ;
62
+ const authentication = await authenticateApiKey ( apiKey , options ) ;
63
+
64
+ return authentication ;
50
65
}
51
66
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
+ */
52
92
export async function authenticateApiKey (
53
93
apiKey : string ,
54
94
options : { allowPublicKey ?: boolean ; allowJWT ?: boolean } = { }
55
- ) : Promise < ApiAuthenticationResult | undefined > {
95
+ ) : Promise < ApiAuthenticationResultSuccess | undefined > {
56
96
const result = getApiKeyResult ( apiKey ) ;
57
97
58
98
if ( ! result ) {
@@ -70,30 +110,120 @@ export async function authenticateApiKey(
70
110
switch ( result . type ) {
71
111
case "PUBLIC" : {
72
112
const environment = await findEnvironmentByPublicApiKey ( result . apiKey ) ;
73
- if ( ! environment ) return ;
113
+ if ( ! environment ) {
114
+ return ;
115
+ }
116
+
74
117
return {
118
+ ok : true ,
75
119
...result ,
76
120
environment,
77
121
} ;
78
122
}
79
123
case "PRIVATE" : {
80
124
const environment = await findEnvironmentByApiKey ( result . apiKey ) ;
81
- if ( ! environment ) return ;
125
+ if ( ! environment ) {
126
+ return ;
127
+ }
128
+
82
129
return {
130
+ ok : true ,
83
131
...result ,
84
132
environment,
85
133
} ;
86
134
}
87
135
case "PUBLIC_JWT" : {
88
136
const validationResults = await validatePublicJwtKey ( result . apiKey ) ;
89
137
90
- if ( ! validationResults ) {
138
+ if ( ! validationResults . ok ) {
91
139
return ;
92
140
}
93
141
94
142
const parsedClaims = ClaimsSchema . safeParse ( validationResults . claims ) ;
95
143
96
144
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 ,
97
227
...result ,
98
228
environment : validationResults . environment ,
99
229
scopes : parsedClaims . success ? parsedClaims . data . scopes : [ ] ,
@@ -207,6 +337,10 @@ export async function authenticatedEnvironmentForAuthentication(
207
337
208
338
switch ( auth . type ) {
209
339
case "apiKey" : {
340
+ if ( ! auth . result . ok ) {
341
+ throw json ( { error : auth . result . error } , { status : 401 } ) ;
342
+ }
343
+
210
344
if ( auth . result . environment . project . externalRef !== projectRef ) {
211
345
throw json (
212
346
{
@@ -337,6 +471,14 @@ export async function validateJWTTokenAndRenew<T extends z.ZodTypeAny>(
337
471
return ;
338
472
}
339
473
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
+
340
482
const payload = payloadSchema . safeParse ( error . payload ) ;
341
483
342
484
if ( ! payload . success ) {
0 commit comments