@@ -29,30 +29,67 @@ 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 } = { }
55
+ ) : Promise < ApiAuthenticationResultSuccess | undefined > {
56
+ const apiKey = getApiKeyFromRequest ( request ) ;
57
+
58
+ if ( ! apiKey ) {
59
+ return ;
60
+ }
61
+
62
+ const authentication = await authenticateApiKey ( apiKey , options ) ;
63
+
64
+ return authentication ;
65
+ }
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 } = { }
42
74
) : Promise < ApiAuthenticationResult | undefined > {
43
75
const apiKey = getApiKeyFromRequest ( request ) ;
44
76
45
77
if ( ! apiKey ) {
46
78
return ;
47
79
}
48
80
49
- return authenticateApiKey ( apiKey , options ) ;
81
+ const authentication = await authenticateApiKeyWithFailure ( apiKey , options ) ;
82
+
83
+ return authentication ;
50
84
}
51
85
86
+ /**
87
+ * @deprecated Use `authenticateApiKeyWithFailure` instead.
88
+ */
52
89
export async function authenticateApiKey (
53
90
apiKey : string ,
54
91
options : { allowPublicKey ?: boolean ; allowJWT ?: boolean } = { }
55
- ) : Promise < ApiAuthenticationResult | undefined > {
92
+ ) : Promise < ApiAuthenticationResultSuccess | undefined > {
56
93
const result = getApiKeyResult ( apiKey ) ;
57
94
58
95
if ( ! result ) {
@@ -70,30 +107,120 @@ export async function authenticateApiKey(
70
107
switch ( result . type ) {
71
108
case "PUBLIC" : {
72
109
const environment = await findEnvironmentByPublicApiKey ( result . apiKey ) ;
73
- if ( ! environment ) return ;
110
+ if ( ! environment ) {
111
+ return ;
112
+ }
113
+
74
114
return {
115
+ ok : true ,
75
116
...result ,
76
117
environment,
77
118
} ;
78
119
}
79
120
case "PRIVATE" : {
80
121
const environment = await findEnvironmentByApiKey ( result . apiKey ) ;
81
- if ( ! environment ) return ;
122
+ if ( ! environment ) {
123
+ return ;
124
+ }
125
+
82
126
return {
127
+ ok : true ,
83
128
...result ,
84
129
environment,
85
130
} ;
86
131
}
87
132
case "PUBLIC_JWT" : {
88
133
const validationResults = await validatePublicJwtKey ( result . apiKey ) ;
89
134
90
- if ( ! validationResults ) {
135
+ if ( ! validationResults . ok ) {
91
136
return ;
92
137
}
93
138
94
139
const parsedClaims = ClaimsSchema . safeParse ( validationResults . claims ) ;
95
140
96
141
return {
142
+ ok : true ,
143
+ ...result ,
144
+ environment : validationResults . environment ,
145
+ scopes : parsedClaims . success ? parsedClaims . data . scopes : [ ] ,
146
+ } ;
147
+ }
148
+ }
149
+ }
150
+
151
+ /**
152
+ * This method is the same as `authenticateApiKey` but it returns a failure result instead of undefined.
153
+ * It should be used from now on to ensure that the API key is always validated and provide a failure result.
154
+ */
155
+ export async function authenticateApiKeyWithFailure (
156
+ apiKey : string ,
157
+ options : { allowPublicKey ?: boolean ; allowJWT ?: boolean } = { }
158
+ ) : Promise < ApiAuthenticationResult > {
159
+ const result = getApiKeyResult ( apiKey ) ;
160
+
161
+ if ( ! result ) {
162
+ return {
163
+ ok : false ,
164
+ error : "Invalid API Key" ,
165
+ } ;
166
+ }
167
+
168
+ if ( ! options . allowPublicKey && result . type === "PUBLIC" ) {
169
+ return {
170
+ ok : false ,
171
+ error : "Public API keys are not allowed for this request" ,
172
+ } ;
173
+ }
174
+
175
+ if ( ! options . allowJWT && result . type === "PUBLIC_JWT" ) {
176
+ return {
177
+ ok : false ,
178
+ error : "Public JWT API keys are not allowed for this request" ,
179
+ } ;
180
+ }
181
+
182
+ switch ( result . type ) {
183
+ case "PUBLIC" : {
184
+ const environment = await findEnvironmentByPublicApiKey ( result . apiKey ) ;
185
+ if ( ! environment ) {
186
+ return {
187
+ ok : false ,
188
+ error : "Invalid API Key" ,
189
+ } ;
190
+ }
191
+
192
+ return {
193
+ ok : true ,
194
+ ...result ,
195
+ environment,
196
+ } ;
197
+ }
198
+ case "PRIVATE" : {
199
+ const environment = await findEnvironmentByApiKey ( result . apiKey ) ;
200
+ if ( ! environment ) {
201
+ return {
202
+ ok : false ,
203
+ error : "Invalid API Key" ,
204
+ } ;
205
+ }
206
+
207
+ return {
208
+ ok : true ,
209
+ ...result ,
210
+ environment,
211
+ } ;
212
+ }
213
+ case "PUBLIC_JWT" : {
214
+ const validationResults = await validatePublicJwtKey ( result . apiKey ) ;
215
+
216
+ if ( ! validationResults . ok ) {
217
+ return validationResults ;
218
+ }
219
+
220
+ const parsedClaims = ClaimsSchema . safeParse ( validationResults . claims ) ;
221
+
222
+ return {
223
+ ok : true ,
97
224
...result ,
98
225
environment : validationResults . environment ,
99
226
scopes : parsedClaims . success ? parsedClaims . data . scopes : [ ] ,
@@ -207,6 +334,10 @@ export async function authenticatedEnvironmentForAuthentication(
207
334
208
335
switch ( auth . type ) {
209
336
case "apiKey" : {
337
+ if ( ! auth . result . ok ) {
338
+ throw json ( { error : auth . result . error } , { status : 401 } ) ;
339
+ }
340
+
210
341
if ( auth . result . environment . project . externalRef !== projectRef ) {
211
342
throw json (
212
343
{
@@ -337,6 +468,14 @@ export async function validateJWTTokenAndRenew<T extends z.ZodTypeAny>(
337
468
return ;
338
469
}
339
470
471
+ if ( ! authenticatedEnv . ok ) {
472
+ logger . error ( "Failed to renew JWT token, invalid API key" , {
473
+ error : error . message ,
474
+ } ) ;
475
+
476
+ return ;
477
+ }
478
+
340
479
const payload = payloadSchema . safeParse ( error . payload ) ;
341
480
342
481
if ( ! payload . success ) {
0 commit comments