1+ import  {  Request ,  Response  }  from  "express" ; 
2+ import  {  requireBearerAuth  }  from  "./bearerAuth.js" ; 
3+ import  {  AuthInfo  }  from  "../types.js" ; 
4+ import  {  InsufficientScopeError ,  InvalidTokenError ,  OAuthError ,  ServerError  }  from  "../errors.js" ; 
5+ import  {  OAuthServerProvider  }  from  "../provider.js" ; 
6+ import  {  OAuthRegisteredClientsStore  }  from  "../clients.js" ; 
7+ 
8+ // Mock provider 
9+ const  mockVerifyAccessToken  =  jest . fn ( ) ; 
10+ const  mockProvider : OAuthServerProvider  =  { 
11+  clientsStore : { }  as  OAuthRegisteredClientsStore , 
12+  authorize : jest . fn ( ) , 
13+  challengeForAuthorizationCode : jest . fn ( ) , 
14+  exchangeAuthorizationCode : jest . fn ( ) , 
15+  exchangeRefreshToken : jest . fn ( ) , 
16+  verifyAccessToken : mockVerifyAccessToken , 
17+ } ; 
18+ 
19+ describe ( "requireBearerAuth middleware" ,  ( )  =>  { 
20+  let  mockRequest : Partial < Request > ; 
21+  let  mockResponse : Partial < Response > ; 
22+  let  nextFunction : jest . Mock ; 
23+ 
24+  beforeEach ( ( )  =>  { 
25+  mockRequest  =  { 
26+  headers : { } , 
27+  } ; 
28+  mockResponse  =  { 
29+  status : jest . fn ( ) . mockReturnThis ( ) , 
30+  json : jest . fn ( ) , 
31+  set : jest . fn ( ) . mockReturnThis ( ) , 
32+  } ; 
33+  nextFunction  =  jest . fn ( ) ; 
34+  jest . clearAllMocks ( ) ; 
35+  } ) ; 
36+ 
37+  it ( "should call next when token is valid" ,  async  ( )  =>  { 
38+  const  validAuthInfo : AuthInfo  =  { 
39+  token : "valid-token" , 
40+  clientId : "client-123" , 
41+  scopes : [ "read" ,  "write" ] , 
42+  } ; 
43+  mockVerifyAccessToken . mockResolvedValue ( validAuthInfo ) ; 
44+ 
45+  mockRequest . headers  =  { 
46+  authorization : "Bearer valid-token" , 
47+  } ; 
48+ 
49+  const  middleware  =  requireBearerAuth ( {  provider : mockProvider  } ) ; 
50+  await  middleware ( mockRequest  as  Request ,  mockResponse  as  Response ,  nextFunction ) ; 
51+ 
52+  expect ( mockVerifyAccessToken ) . toHaveBeenCalledWith ( "valid-token" ) ; 
53+  expect ( mockRequest . auth ) . toEqual ( validAuthInfo ) ; 
54+  expect ( nextFunction ) . toHaveBeenCalled ( ) ; 
55+  expect ( mockResponse . status ) . not . toHaveBeenCalled ( ) ; 
56+  expect ( mockResponse . json ) . not . toHaveBeenCalled ( ) ; 
57+  } ) ; 
58+ 
59+  it ( "should require specific scopes when configured" ,  async  ( )  =>  { 
60+  const  authInfo : AuthInfo  =  { 
61+  token : "valid-token" , 
62+  clientId : "client-123" , 
63+  scopes : [ "read" ] , 
64+  } ; 
65+  mockVerifyAccessToken . mockResolvedValue ( authInfo ) ; 
66+ 
67+  mockRequest . headers  =  { 
68+  authorization : "Bearer valid-token" , 
69+  } ; 
70+ 
71+  const  middleware  =  requireBearerAuth ( { 
72+  provider : mockProvider , 
73+  requiredScopes : [ "read" ,  "write" ] 
74+  } ) ; 
75+ 
76+  await  middleware ( mockRequest  as  Request ,  mockResponse  as  Response ,  nextFunction ) ; 
77+ 
78+  expect ( mockVerifyAccessToken ) . toHaveBeenCalledWith ( "valid-token" ) ; 
79+  expect ( mockResponse . status ) . toHaveBeenCalledWith ( 403 ) ; 
80+  expect ( mockResponse . set ) . toHaveBeenCalledWith ( 
81+  "WWW-Authenticate" , 
82+  expect . stringContaining ( 'Bearer error="insufficient_scope"' ) 
83+  ) ; 
84+  expect ( mockResponse . json ) . toHaveBeenCalledWith ( 
85+  expect . objectContaining ( {  error : "insufficient_scope" ,  error_description : "Insufficient scope"  } ) 
86+  ) ; 
87+  expect ( nextFunction ) . not . toHaveBeenCalled ( ) ; 
88+  } ) ; 
89+ 
90+  it ( "should accept token with all required scopes" ,  async  ( )  =>  { 
91+  const  authInfo : AuthInfo  =  { 
92+  token : "valid-token" , 
93+  clientId : "client-123" , 
94+  scopes : [ "read" ,  "write" ,  "admin" ] , 
95+  } ; 
96+  mockVerifyAccessToken . mockResolvedValue ( authInfo ) ; 
97+ 
98+  mockRequest . headers  =  { 
99+  authorization : "Bearer valid-token" , 
100+  } ; 
101+ 
102+  const  middleware  =  requireBearerAuth ( { 
103+  provider : mockProvider , 
104+  requiredScopes : [ "read" ,  "write" ] 
105+  } ) ; 
106+ 
107+  await  middleware ( mockRequest  as  Request ,  mockResponse  as  Response ,  nextFunction ) ; 
108+ 
109+  expect ( mockVerifyAccessToken ) . toHaveBeenCalledWith ( "valid-token" ) ; 
110+  expect ( mockRequest . auth ) . toEqual ( authInfo ) ; 
111+  expect ( nextFunction ) . toHaveBeenCalled ( ) ; 
112+  expect ( mockResponse . status ) . not . toHaveBeenCalled ( ) ; 
113+  expect ( mockResponse . json ) . not . toHaveBeenCalled ( ) ; 
114+  } ) ; 
115+ 
116+  it ( "should return 401 when no Authorization header is present" ,  async  ( )  =>  { 
117+  const  middleware  =  requireBearerAuth ( {  provider : mockProvider  } ) ; 
118+  await  middleware ( mockRequest  as  Request ,  mockResponse  as  Response ,  nextFunction ) ; 
119+ 
120+  expect ( mockVerifyAccessToken ) . not . toHaveBeenCalled ( ) ; 
121+  expect ( mockResponse . status ) . toHaveBeenCalledWith ( 401 ) ; 
122+  expect ( mockResponse . set ) . toHaveBeenCalledWith ( 
123+  "WWW-Authenticate" , 
124+  expect . stringContaining ( 'Bearer error="invalid_token"' ) 
125+  ) ; 
126+  expect ( mockResponse . json ) . toHaveBeenCalledWith ( 
127+  expect . objectContaining ( {  error : "invalid_token" ,  error_description : "Missing Authorization header"  } ) 
128+  ) ; 
129+  expect ( nextFunction ) . not . toHaveBeenCalled ( ) ; 
130+  } ) ; 
131+ 
132+  it ( "should return 401 when Authorization header format is invalid" ,  async  ( )  =>  { 
133+  mockRequest . headers  =  { 
134+  authorization : "InvalidFormat" , 
135+  } ; 
136+ 
137+  const  middleware  =  requireBearerAuth ( {  provider : mockProvider  } ) ; 
138+  await  middleware ( mockRequest  as  Request ,  mockResponse  as  Response ,  nextFunction ) ; 
139+ 
140+  expect ( mockVerifyAccessToken ) . not . toHaveBeenCalled ( ) ; 
141+  expect ( mockResponse . status ) . toHaveBeenCalledWith ( 401 ) ; 
142+  expect ( mockResponse . set ) . toHaveBeenCalledWith ( 
143+  "WWW-Authenticate" , 
144+  expect . stringContaining ( 'Bearer error="invalid_token"' ) 
145+  ) ; 
146+  expect ( mockResponse . json ) . toHaveBeenCalledWith ( 
147+  expect . objectContaining ( { 
148+  error : "invalid_token" , 
149+  error_description : "Invalid Authorization header format, expected 'Bearer TOKEN'" 
150+  } ) 
151+  ) ; 
152+  expect ( nextFunction ) . not . toHaveBeenCalled ( ) ; 
153+  } ) ; 
154+ 
155+  it ( "should return 401 when token verification fails with InvalidTokenError" ,  async  ( )  =>  { 
156+  mockRequest . headers  =  { 
157+  authorization : "Bearer invalid-token" , 
158+  } ; 
159+ 
160+  mockVerifyAccessToken . mockRejectedValue ( new  InvalidTokenError ( "Token expired" ) ) ; 
161+ 
162+  const  middleware  =  requireBearerAuth ( {  provider : mockProvider  } ) ; 
163+  await  middleware ( mockRequest  as  Request ,  mockResponse  as  Response ,  nextFunction ) ; 
164+ 
165+  expect ( mockVerifyAccessToken ) . toHaveBeenCalledWith ( "invalid-token" ) ; 
166+  expect ( mockResponse . status ) . toHaveBeenCalledWith ( 401 ) ; 
167+  expect ( mockResponse . set ) . toHaveBeenCalledWith ( 
168+  "WWW-Authenticate" , 
169+  expect . stringContaining ( 'Bearer error="invalid_token"' ) 
170+  ) ; 
171+  expect ( mockResponse . json ) . toHaveBeenCalledWith ( 
172+  expect . objectContaining ( {  error : "invalid_token" ,  error_description : "Token expired"  } ) 
173+  ) ; 
174+  expect ( nextFunction ) . not . toHaveBeenCalled ( ) ; 
175+  } ) ; 
176+ 
177+  it ( "should return 403 when access token has insufficient scopes" ,  async  ( )  =>  { 
178+  mockRequest . headers  =  { 
179+  authorization : "Bearer valid-token" , 
180+  } ; 
181+ 
182+  mockVerifyAccessToken . mockRejectedValue ( new  InsufficientScopeError ( "Required scopes: read, write" ) ) ; 
183+ 
184+  const  middleware  =  requireBearerAuth ( {  provider : mockProvider  } ) ; 
185+  await  middleware ( mockRequest  as  Request ,  mockResponse  as  Response ,  nextFunction ) ; 
186+ 
187+  expect ( mockVerifyAccessToken ) . toHaveBeenCalledWith ( "valid-token" ) ; 
188+  expect ( mockResponse . status ) . toHaveBeenCalledWith ( 403 ) ; 
189+  expect ( mockResponse . set ) . toHaveBeenCalledWith ( 
190+  "WWW-Authenticate" , 
191+  expect . stringContaining ( 'Bearer error="insufficient_scope"' ) 
192+  ) ; 
193+  expect ( mockResponse . json ) . toHaveBeenCalledWith ( 
194+  expect . objectContaining ( {  error : "insufficient_scope" ,  error_description : "Required scopes: read, write"  } ) 
195+  ) ; 
196+  expect ( nextFunction ) . not . toHaveBeenCalled ( ) ; 
197+  } ) ; 
198+ 
199+  it ( "should return 500 when a ServerError occurs" ,  async  ( )  =>  { 
200+  mockRequest . headers  =  { 
201+  authorization : "Bearer valid-token" , 
202+  } ; 
203+ 
204+  mockVerifyAccessToken . mockRejectedValue ( new  ServerError ( "Internal server issue" ) ) ; 
205+ 
206+  const  middleware  =  requireBearerAuth ( {  provider : mockProvider  } ) ; 
207+  await  middleware ( mockRequest  as  Request ,  mockResponse  as  Response ,  nextFunction ) ; 
208+ 
209+  expect ( mockVerifyAccessToken ) . toHaveBeenCalledWith ( "valid-token" ) ; 
210+  expect ( mockResponse . status ) . toHaveBeenCalledWith ( 500 ) ; 
211+  expect ( mockResponse . json ) . toHaveBeenCalledWith ( 
212+  expect . objectContaining ( {  error : "server_error" ,  error_description : "Internal server issue"  } ) 
213+  ) ; 
214+  expect ( nextFunction ) . not . toHaveBeenCalled ( ) ; 
215+  } ) ; 
216+ 
217+  it ( "should return 400 for generic OAuthError" ,  async  ( )  =>  { 
218+  mockRequest . headers  =  { 
219+  authorization : "Bearer valid-token" , 
220+  } ; 
221+ 
222+  mockVerifyAccessToken . mockRejectedValue ( new  OAuthError ( "custom_error" ,  "Some OAuth error" ) ) ; 
223+ 
224+  const  middleware  =  requireBearerAuth ( {  provider : mockProvider  } ) ; 
225+  await  middleware ( mockRequest  as  Request ,  mockResponse  as  Response ,  nextFunction ) ; 
226+ 
227+  expect ( mockVerifyAccessToken ) . toHaveBeenCalledWith ( "valid-token" ) ; 
228+  expect ( mockResponse . status ) . toHaveBeenCalledWith ( 400 ) ; 
229+  expect ( mockResponse . json ) . toHaveBeenCalledWith ( 
230+  expect . objectContaining ( {  error : "custom_error" ,  error_description : "Some OAuth error"  } ) 
231+  ) ; 
232+  expect ( nextFunction ) . not . toHaveBeenCalled ( ) ; 
233+  } ) ; 
234+ 
235+  it ( "should return 500 when unexpected error occurs" ,  async  ( )  =>  { 
236+  mockRequest . headers  =  { 
237+  authorization : "Bearer valid-token" , 
238+  } ; 
239+ 
240+  mockVerifyAccessToken . mockRejectedValue ( new  Error ( "Unexpected error" ) ) ; 
241+ 
242+  const  middleware  =  requireBearerAuth ( {  provider : mockProvider  } ) ; 
243+  await  middleware ( mockRequest  as  Request ,  mockResponse  as  Response ,  nextFunction ) ; 
244+ 
245+  expect ( mockVerifyAccessToken ) . toHaveBeenCalledWith ( "valid-token" ) ; 
246+  expect ( mockResponse . status ) . toHaveBeenCalledWith ( 500 ) ; 
247+  expect ( mockResponse . json ) . toHaveBeenCalledWith ( 
248+  expect . objectContaining ( {  error : "server_error" ,  error_description : "Internal Server Error"  } ) 
249+  ) ; 
250+  expect ( nextFunction ) . not . toHaveBeenCalled ( ) ; 
251+  } ) ; 
252+ } ) ; 
0 commit comments