@@ -37,6 +37,7 @@ describe("requireBearerAuth middleware", () => {
3737 token : "valid-token" ,
3838 clientId : "client-123" ,
3939 scopes : [ "read" , "write" ] ,
40+ expiresAt : Math . floor ( Date . now ( ) / 1000 ) + 3600 , // Token expires in an hour
4041 } ;
4142 mockVerifyAccessToken . mockResolvedValue ( validAuthInfo ) ;
4243
@@ -53,13 +54,17 @@ describe("requireBearerAuth middleware", () => {
5354 expect ( mockResponse . status ) . not . toHaveBeenCalled ( ) ;
5455 expect ( mockResponse . json ) . not . toHaveBeenCalled ( ) ;
5556 } ) ;
56-
57- it ( "should reject expired tokens" , async ( ) => {
57+
58+ it . each ( [
59+ [ 100 ] , // Token expired 100 seconds ago
60+ [ 0 ] , // Token expires at the same time as now
61+ ] ) ( "should reject expired tokens (expired %s seconds ago)" , async ( expiredSecondsAgo : number ) => {
62+ const expiresAt = Math . floor ( Date . now ( ) / 1000 ) - expiredSecondsAgo ;
5863 const expiredAuthInfo : AuthInfo = {
5964 token : "expired-token" ,
6065 clientId : "client-123" ,
6166 scopes : [ "read" , "write" ] ,
62- expiresAt : Math . floor ( Date . now ( ) / 1000 ) - 100 , // Token expired 100 seconds ago
67+ expiresAt
6368 } ;
6469 mockVerifyAccessToken . mockResolvedValue ( expiredAuthInfo ) ;
6570
@@ -82,6 +87,37 @@ describe("requireBearerAuth middleware", () => {
8287 expect ( nextFunction ) . not . toHaveBeenCalled ( ) ;
8388 } ) ;
8489
90+ it . each ( [
91+ [ undefined ] , // Token has no expiration time
92+ [ NaN ] , // Token has no expiration time
93+ ] ) ( "should reject tokens with no expiration time (expiresAt: %s)" , async ( expiresAt : number | undefined ) => {
94+ const noExpirationAuthInfo : AuthInfo = {
95+ token : "no-expiration-token" ,
96+ clientId : "client-123" ,
97+ scopes : [ "read" , "write" ] ,
98+ expiresAt
99+ } ;
100+ mockVerifyAccessToken . mockResolvedValue ( noExpirationAuthInfo ) ;
101+
102+ mockRequest . headers = {
103+ authorization : "Bearer expired-token" ,
104+ } ;
105+
106+ const middleware = requireBearerAuth ( { verifier : mockVerifier } ) ;
107+ await middleware ( mockRequest as Request , mockResponse as Response , nextFunction ) ;
108+
109+ expect ( mockVerifyAccessToken ) . toHaveBeenCalledWith ( "expired-token" ) ;
110+ expect ( mockResponse . status ) . toHaveBeenCalledWith ( 401 ) ;
111+ expect ( mockResponse . set ) . toHaveBeenCalledWith (
112+ "WWW-Authenticate" ,
113+ expect . stringContaining ( 'Bearer error="invalid_token"' )
114+ ) ;
115+ expect ( mockResponse . json ) . toHaveBeenCalledWith (
116+ expect . objectContaining ( { error : "invalid_token" , error_description : "Token has no expiration time" } )
117+ ) ;
118+ expect ( nextFunction ) . not . toHaveBeenCalled ( ) ;
119+ } ) ;
120+
85121 it ( "should accept non-expired tokens" , async ( ) => {
86122 const nonExpiredAuthInfo : AuthInfo = {
87123 token : "valid-token" ,
@@ -141,6 +177,7 @@ describe("requireBearerAuth middleware", () => {
141177 token : "valid-token" ,
142178 clientId : "client-123" ,
143179 scopes : [ "read" , "write" , "admin" ] ,
180+ expiresAt : Math . floor ( Date . now ( ) / 1000 ) + 3600 , // Token expires in an hour
144181 } ;
145182 mockVerifyAccessToken . mockResolvedValue ( authInfo ) ;
146183
0 commit comments