@@ -197,6 +197,113 @@ func TestProxyRouter_HeaderMapping(t *testing.T) {
197197 }
198198}
199199
200+ func TestProxyRouter_HeaderMapping_FallbackToTopLevelClaims (t * testing.T ) {
201+ privateKey , publicKey , err := generateRSAKeyPair ()
202+ require .NoError (t , err )
203+
204+ cases := []struct {
205+ name string
206+ headerMapping map [string ]string
207+ claims jwt.MapClaims
208+ expectedHeaders map [string ]string
209+ missingHeaders []string
210+ }{
211+ {
212+ name : "top-level claim when userinfo is absent" ,
213+ headerMapping : map [string ]string {"/email" : "X-Forwarded-Email" },
214+ claims : jwt.MapClaims {
215+ "sub" : "test-user" ,
216+ 217+ "exp" : time .Now ().Add (time .Hour ).Unix (),
218+ "iat" : time .Now ().Unix (),
219+ },
220+ expectedHeaders : map [string ]string {
221+ "X-Forwarded-Email" :
"[email protected] " ,
222+ },
223+ },
224+ {
225+ name : "userinfo takes precedence over top-level claim" ,
226+ headerMapping : map [string ]string {"/email" : "X-Forwarded-Email" },
227+ claims : jwt.MapClaims {
228+ "sub" : "test-user" ,
229+ 230+ "userinfo" :
map [
string ]
any {
"email" :
"[email protected] " },
231+ "exp" : time .Now ().Add (time .Hour ).Unix (),
232+ "iat" : time .Now ().Unix (),
233+ },
234+ expectedHeaders : map [string ]string {
235+ "X-Forwarded-Email" :
"[email protected] " ,
236+ },
237+ },
238+ {
239+ name : "multiple top-level claims without userinfo" ,
240+ headerMapping : map [string ]string {"/email" : "X-Forwarded-Email" , "/name" : "X-Forwarded-Name" },
241+ claims : jwt.MapClaims {
242+ "sub" : "test-user" ,
243+ 244+ "name" : "John Doe" ,
245+ "exp" : time .Now ().Add (time .Hour ).Unix (),
246+ "iat" : time .Now ().Unix (),
247+ },
248+ expectedHeaders : map [string ]string {
249+ "X-Forwarded-Email" :
"[email protected] " ,
250+ "X-Forwarded-Name" : "John Doe" ,
251+ },
252+ },
253+ {
254+ name : "missing claim in both userinfo and top-level" ,
255+ headerMapping : map [string ]string {"/email" : "X-Forwarded-Email" , "/missing" : "X-Missing" },
256+ claims : jwt.MapClaims {
257+ "sub" : "test-user" ,
258+ 259+ "exp" : time .Now ().Add (time .Hour ).Unix (),
260+ "iat" : time .Now ().Unix (),
261+ },
262+ expectedHeaders : map [string ]string {
263+ "X-Forwarded-Email" :
"[email protected] " ,
264+ },
265+ missingHeaders : []string {"X-Missing" },
266+ },
267+ }
268+
269+ for _ , tt := range cases {
270+ t .Run (tt .name , func (t * testing.T ) {
271+ receivedHeaders := http.Header {}
272+ proxyHandler := http .HandlerFunc (func (w http.ResponseWriter , r * http.Request ) {
273+ for k , v := range r .Header {
274+ receivedHeaders [k ] = v
275+ }
276+ w .WriteHeader (http .StatusOK )
277+ })
278+
279+ proxyRouter , err := NewProxyRouter ("https://example.com" , proxyHandler , publicKey , http.Header {}, false , tt .headerMapping )
280+ require .NoError (t , err )
281+
282+ gin .SetMode (gin .TestMode )
283+ router := gin .New ()
284+ proxyRouter .SetupRoutes (router )
285+
286+ token , err := createJWT (privateKey , tt .claims )
287+ require .NoError (t , err )
288+
289+ req , err := http .NewRequest ("GET" , "/test" , nil )
290+ require .NoError (t , err )
291+ req .Header .Set ("Authorization" , "Bearer " + token )
292+
293+ w := httptest .NewRecorder ()
294+ router .ServeHTTP (w , req )
295+
296+ assert .Equal (t , http .StatusOK , w .Code )
297+ for header , expected := range tt .expectedHeaders {
298+ assert .Equal (t , expected , receivedHeaders .Get (header ), "header %s mismatch" , header )
299+ }
300+ for _ , header := range tt .missingHeaders {
301+ assert .Empty (t , receivedHeaders .Get (header ), "header %s should not be set" , header )
302+ }
303+ })
304+ }
305+ }
306+
200307func TestProxyRouter_ProtectedResourceTrailingSlash (t * testing.T ) {
201308 _ , publicKey , err := generateRSAKeyPair ()
202309 require .NoError (t , err )
0 commit comments