@@ -41,6 +41,12 @@ private static readonly ConcurrentTLruCache<string, ImageMetadata> SourceMetadat
4141 private static readonly ConcurrentTLruCache < string , ( IImageCacheResolver , ImageCacheMetadata ) > CacheResolverLru
4242 = new ( 1024 , TimeSpan . FromSeconds ( 30 ) ) ;
4343
44+ /// <summary>
45+ /// Used to temporarily store cached HMAC-s to reduce the overhead of HMAC token generation.
46+ /// </summary>
47+ private static readonly ConcurrentTLruCache < string , string > HMACTokenLru
48+ = new ( 1024 , TimeSpan . FromSeconds ( 30 ) ) ;
49+
4450 /// <summary>
4551 /// The function processing the Http request.
4652 /// </summary>
@@ -222,19 +228,6 @@ private async Task Invoke(HttpContext httpContext, bool retry)
222228
223229 ImageCommandContext imageCommandContext = new ( httpContext , commands , this . commandParser , this . parserCulture ) ;
224230
225- if ( doHMAC )
226- {
227- // Compare the passed token to our generated mac.
228- string mac = await this . options . OnComputeHMACAsync ( imageCommandContext , secret ) ;
229- if ( mac != token )
230- {
231- // Throw a 401. We don't log the error to avoid attempts at log poisoning.
232- httpContext . Response . Clear ( ) ;
233- httpContext . Response . StatusCode = StatusCodes . Status401Unauthorized ;
234- return ;
235- }
236- }
237-
238231 await this . options . OnParseCommandsAsync . Invoke ( imageCommandContext ) ;
239232
240233 // Get the correct service for the request
@@ -256,6 +249,26 @@ private async Task Invoke(HttpContext httpContext, bool retry)
256249 return ;
257250 }
258251
252+ // At this point we know that this is a valid image request
253+ // Check for a token if required and reject if invalid.
254+ if ( doHMAC )
255+ {
256+ if ( token is null )
257+ {
258+ // Throw a 401. We don't log the error to avoid attempts at log poisoning.
259+ SetUnauthorized ( httpContext ) ;
260+ return ;
261+ }
262+
263+ // Compare the passed token to our generated mac.
264+ string mac = await HMACTokenLru . GetOrAddAsync ( token , _ => this . options . OnComputeHMACAsync ( imageCommandContext , secret ) ) ;
265+ if ( mac != token )
266+ {
267+ SetUnauthorized ( httpContext ) ;
268+ return ;
269+ }
270+ }
271+
259272 IImageResolver sourceImageResolver = await provider . GetAsync ( httpContext ) ;
260273
261274 if ( sourceImageResolver is null )
@@ -275,6 +288,13 @@ await this.ProcessRequestAsync(
275288 retry ) ;
276289 }
277290
291+ private static void SetUnauthorized ( HttpContext httpContext )
292+ {
293+ httpContext . Response . Clear ( ) ;
294+ httpContext . Response . Headers . Add ( "WWW-Authenticate" , "HMAC realm=\" " + httpContext . Request . Host + "\" " ) ;
295+ httpContext . Response . StatusCode = StatusCodes . Status401Unauthorized ;
296+ }
297+
278298 private void StripUnknownCommands ( CommandCollection commands , int startAtIndex )
279299 {
280300 var keys = new List < string > ( commands . Keys ) ;
0 commit comments