@@ -83,6 +83,7 @@ func TestOAuth2Authentication(t *testing.T) {
8383 oauth2Auth , err := NewOAuth2Authenticator (
8484 jwksServer .URL ,
8585 "https://auth.example.com/" ,
86+ "test-audience" ,
8687 pm ,
8788 testLogger (),
8889 testServiceName ,
@@ -179,6 +180,44 @@ func TestOAuth2Authentication(t *testing.T) {
179180 expectError : true ,
180181 expectedErrMsg : "missing required scope: mqtt" ,
181182 },
183+ {
184+ name : "missing expiration fails" ,
185+ claims : gojwt.MapClaims {
186+ "iss" : "https://auth.example.com/" ,
187+ "sub" : "user@example.com" ,
188+ "aud" : "test-audience" ,
189+ "iat" : now .Unix (),
190+ "scope" : "mqtt" ,
191+ },
192+ expectError : true ,
193+ expectedErrMsg : "token is missing required claim: exp claim is required" ,
194+ },
195+ {
196+ name : "wrong issuer fails" ,
197+ claims : gojwt.MapClaims {
198+ "iss" : "https://other.example.com/" ,
199+ "sub" : "user@example.com" ,
200+ "aud" : "test-audience" ,
201+ "exp" : now .Add (1 * time .Hour ).Unix (),
202+ "iat" : now .Unix (),
203+ "scope" : "mqtt" ,
204+ },
205+ expectError : true ,
206+ expectedErrMsg : "token has invalid issuer" ,
207+ },
208+ {
209+ name : "wrong audience fails" ,
210+ claims : gojwt.MapClaims {
211+ "iss" : "https://auth.example.com/" ,
212+ "sub" : "user@example.com" ,
213+ "aud" : "wrong-audience" ,
214+ "exp" : now .Add (1 * time .Hour ).Unix (),
215+ "iat" : now .Unix (),
216+ "scope" : "mqtt" ,
217+ },
218+ expectError : true ,
219+ expectedErrMsg : "token has invalid audience" ,
220+ },
182221 }
183222
184223 for _ , tt := range tests {
@@ -207,6 +246,66 @@ func TestOAuth2Authentication(t *testing.T) {
207246 }
208247}
209248
249+ func TestOAuth2RejectsUnexpectedSigningMethod (t * testing.T ) {
250+ privateKey , err := rsa .GenerateKey (rand .Reader , 2048 )
251+ require .NoError (t , err )
252+
253+ jwkSet := jwkset .NewMemoryStorage ()
254+ jwk , err := jwkset .NewJWKFromKey (privateKey , jwkset.JWKOptions {
255+ Metadata : jwkset.JWKMetadataOptions {
256+ KID : "test-key-1" ,
257+ },
258+ })
259+ require .NoError (t , err )
260+ require .NoError (t , jwkSet .KeyWrite (context .Background (), jwk ))
261+
262+ jwksServer := httptest .NewServer (http .HandlerFunc (func (w http.ResponseWriter , r * http.Request ) {
263+ jwks , err := jwkSet .JSONPublic (context .Background ())
264+ if err != nil {
265+ http .Error (w , "Failed to get JWKS" , http .StatusInternalServerError )
266+ return
267+ }
268+ w .Header ().Set ("Content-Type" , "application/json" )
269+ if _ , err := w .Write (jwks ); err != nil {
270+ http .Error (w , "Failed to write JWKS" , http .StatusInternalServerError )
271+ }
272+ }))
273+ defer jwksServer .Close ()
274+
275+ permFile := createTestPermissionsFile (t )
276+ defer os .Remove (permFile )
277+
278+ pm , err := config .NewPermissionsManager (permFile , testLogger ())
279+ require .NoError (t , err )
280+ defer pm .Close ()
281+
282+ oauth2Auth , err := NewOAuth2Authenticator (
283+ jwksServer .URL ,
284+ "https://auth.example.com/" ,
285+ "test-audience" ,
286+ pm ,
287+ testLogger (),
288+ testServiceName ,
289+ )
290+ require .NoError (t , err )
291+ defer oauth2Auth .Close ()
292+
293+ token := gojwt .NewWithClaims (gojwt .SigningMethodHS256 , gojwt.MapClaims {
294+ "iss" : "https://auth.example.com/" ,
295+ "sub" : "user@example.com" ,
296+ "aud" : "test-audience" ,
297+ "exp" : time .Now ().Add (1 * time .Hour ).Unix (),
298+ "scope" : "mqtt" ,
299+ })
300+ token .Header ["kid" ] = "test-key-1"
301+ tokenString , err := token .SignedString ([]byte ("secret" ))
302+ require .NoError (t , err )
303+
304+ _ , err = oauth2Auth .Authenticate (context .Background (), tokenString )
305+ require .Error (t , err )
306+ assert .Contains (t , err .Error (), "signing method HS256 is invalid" )
307+ }
308+
210309// TestOAuth2RequiredScope tests per-client required scope validation
211310func TestOAuth2RequiredScope (t * testing.T ) {
212311 // Generate RSA key pair for JWT signing
@@ -278,6 +377,7 @@ func TestOAuth2RequiredScope(t *testing.T) {
278377 oauth2Auth , err := NewOAuth2Authenticator (
279378 jwksServer .URL ,
280379 "https://auth.example.com/" ,
380+ "test-audience" ,
281381 pm ,
282382 testLogger (),
283383 testServiceName ,
@@ -299,6 +399,7 @@ func TestOAuth2RequiredScope(t *testing.T) {
299399 claims : gojwt.MapClaims {
300400 "iss" : "https://auth.example.com/" ,
301401 "sub" : "default@example.com" ,
402+ "aud" : "test-audience" ,
302403 "exp" : now .Add (1 * time .Hour ).Unix (),
303404 "iat" : now .Unix (),
304405 "scope" : "mqtt openid" ,
@@ -311,6 +412,7 @@ func TestOAuth2RequiredScope(t *testing.T) {
311412 claims : gojwt.MapClaims {
312413 "iss" : "https://auth.example.com/" ,
313414 "sub" : "default@example.com" ,
415+ "aud" : "test-audience" ,
314416 "exp" : now .Add (1 * time .Hour ).Unix (),
315417 "iat" : now .Unix (),
316418 "scope" : "openid profile" ,
@@ -324,6 +426,7 @@ func TestOAuth2RequiredScope(t *testing.T) {
324426 "iss" : "https://auth.example.com/" ,
325427 "sub" : "some-service" ,
326428 "azp" : "custom-client-id" ,
429+ "aud" : "test-audience" ,
327430 "exp" : now .Add (1 * time .Hour ).Unix (),
328431 "iat" : now .Unix (),
329432 "scope" : "nats:events openid" ,
@@ -337,6 +440,7 @@ func TestOAuth2RequiredScope(t *testing.T) {
337440 "iss" : "https://auth.example.com/" ,
338441 "sub" : "some-service" ,
339442 "azp" : "custom-client-id" ,
443+ "aud" : "test-audience" ,
340444 "exp" : now .Add (1 * time .Hour ).Unix (),
341445 "iat" : now .Unix (),
342446 "scope" : "mqtt openid" ,
0 commit comments