Skip to content

Commit 7b6daee

Browse files
add additional token call params to BaseJWTRefreshTokenCookiesUserLoginSecurityContext
1 parent 8cf4b6a commit 7b6daee

File tree

6 files changed

+68
-5
lines changed

6 files changed

+68
-5
lines changed

src/main/java/org/primeframework/mvc/security/BaseJWTRefreshTokenCookiesUserLoginSecurityContext.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -288,6 +288,8 @@ private Tokens refreshJWT(Tokens tokens) {
288288
body.put("client_secret", List.of(oauthConfiguration.clientSecret));
289289
}
290290

291+
body.putAll(oauthConfiguration.additionalParameters);
292+
291293
HttpRequest refreshRequest = requestBuilder.header(Headers.ContentType, ContentTypes.Form)
292294
.POST(new FormBodyPublisher(body))
293295
.build();

src/main/java/org/primeframework/mvc/security/oauth/OAuthConfiguration.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2021, Inversoft Inc., All Rights Reserved
2+
* Copyright (c) 2021-2025, Inversoft Inc., All Rights Reserved
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -15,12 +15,18 @@
1515
*/
1616
package org.primeframework.mvc.security.oauth;
1717

18+
import java.util.HashMap;
19+
import java.util.List;
20+
import java.util.Map;
21+
1822
import org.primeframework.mvc.util.Buildable;
1923

2024
/**
2125
* @author Daniel DeGroff
2226
*/
2327
public class OAuthConfiguration implements Buildable<OAuthConfiguration> {
28+
public Map<String, List<String>> additionalParameters = new HashMap<>();
29+
2430
public TokenAuthenticationMethod authenticationMethod = TokenAuthenticationMethod.client_secret_basic;
2531

2632
public String clientId;

src/test/java/org/example/action/oauth/TokenAction.java

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2021-2024, Inversoft Inc., All Rights Reserved
2+
* Copyright (c) 2021-2025, Inversoft Inc., All Rights Reserved
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -17,6 +17,8 @@
1717

1818
import java.time.ZoneOffset;
1919
import java.time.ZonedDateTime;
20+
import java.util.HashMap;
21+
import java.util.Map;
2022

2123
import com.google.inject.Inject;
2224
import io.fusionauth.http.server.HTTPRequest;
@@ -27,6 +29,7 @@
2729
import org.primeframework.mvc.action.result.annotation.JSON;
2830
import org.primeframework.mvc.content.json.annotation.JSONResponse;
2931
import org.primeframework.mvc.parameter.annotation.FieldName;
32+
import org.primeframework.mvc.parameter.annotation.UnknownParameters;
3033
import org.primeframework.mvc.security.MockOAuthUserLoginSecurityContext;
3134
import org.primeframework.mvc.security.oauth.RefreshResponse;
3235
import static org.example.action.oauth.LoginAction.Subject;
@@ -36,6 +39,9 @@
3639
@Action
3740
@JSON
3841
public class TokenAction {
42+
@UnknownParameters
43+
public static Map<String, String[]> UnknownParameters = new HashMap<>();
44+
3945
@FieldName("client_id")
4046
public String clientId;
4147

@@ -64,6 +70,7 @@ public String post() {
6470
case client_secret_basic -> assertEquals(httpRequest.getHeader("Authorization"), "Basic dGhlIGNsaWVudCBJRDp0aGUgY2xpZW50IHNlY3JldA==");
6571
case none -> assertTrue(true);
6672
}
73+
6774
JWT jwt = new JWT();
6875
jwt.audience = "prime-tests";
6976
jwt.issuedAt = ZonedDateTime.now(ZoneOffset.UTC);

src/test/java/org/primeframework/mvc/JWTRefreshTokenLoginTest.java

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2021-2024, Inversoft Inc., All Rights Reserved
2+
* Copyright (c) 2021-2025, Inversoft Inc., All Rights Reserved
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -15,6 +15,9 @@
1515
*/
1616
package org.primeframework.mvc;
1717

18+
import java.util.List;
19+
import java.util.UUID;
20+
1821
import com.codahale.metrics.MetricRegistry;
1922
import com.google.inject.AbstractModule;
2023
import com.google.inject.Injector;
@@ -24,6 +27,7 @@
2427
import io.fusionauth.http.server.HTTPRequest;
2528
import io.fusionauth.http.server.HTTPResponse;
2629
import io.fusionauth.http.server.HTTPServerConfiguration;
30+
import org.example.action.oauth.TokenAction;
2731
import org.primeframework.mvc.PrimeBaseTest.TestContentModule;
2832
import org.primeframework.mvc.PrimeBaseTest.TestMVCConfigurationModule;
2933
import org.primeframework.mvc.cors.CORSConfigurationProvider;
@@ -47,6 +51,8 @@
4751
import org.testng.annotations.BeforeClass;
4852
import org.testng.annotations.BeforeMethod;
4953
import org.testng.annotations.Test;
54+
import static org.testng.Assert.assertEquals;
55+
import static org.testng.Assert.assertTrue;
5056

5157
/**
5258
* @author Brian Pontarelli
@@ -256,6 +262,36 @@ public void refreshTokenEndpointUp_no_auth() {
256262
.assertBodyContains("Logged in");
257263
}
258264

265+
@Test
266+
public void refreshTokenEndpoint_additionalParameters() {
267+
// The token action should get called with the additional parameters we configure on the security context.
268+
var tenantId = new UUID(5, 0).toString();
269+
MockOAuthUserLoginSecurityContext.additionalParameters.put("tenantId", List.of(tenantId));
270+
MockOAuthUserLoginSecurityContext.TokenEndpoint = "http://localhost:" + simulator.getPort() + "/oauth/token";
271+
MockOAuthUserLoginSecurityContext.ValidateJWTOnLogin = false;
272+
273+
// Setting 'expired: true' on the request just tells the Login action to create an expired JWT and store it in the LoginContext.
274+
// - So we expect this to succeed, but the login context wil now contain an expired JWT. This means it will be refreshed on first use.
275+
//
276+
// The refresh action is what will call the token endpoint with the additional parameters.
277+
simulator.test("/oauth/login")
278+
.withParameter("expired", "true")
279+
.post()
280+
.assertStatusCode(200);
281+
282+
// The JWT in the security context was found to be expired on first use even though login succeeded.
283+
// - This will have caused us to refresh the token, so this action should succeed.
284+
simulator.test("/oauth/protected-resource")
285+
.get()
286+
.assertStatusCode(200)
287+
.assertBodyContains("Logged in");
288+
289+
// Ensure that the tenantId was added to the request and caught in unknown parameters
290+
// if additional parameters were sent, validate them
291+
assertTrue(TokenAction.UnknownParameters.containsKey("tenantId"), "Missing tenantId in unknown parameters");
292+
assertEquals(TokenAction.UnknownParameters.get("tenantId"), new String[]{tenantId}, "Mismatched tenantId in unknown parameters");
293+
}
294+
259295
public static class TestScopeModule extends AbstractModule {
260296
@Override
261297
protected void configure() {

src/test/java/org/primeframework/mvc/PrimeBaseTest.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@
5151
import io.fusionauth.http.server.HTTPResponse;
5252
import io.fusionauth.http.server.HTTPServerConfiguration;
5353
import org.example.action.SecureAction;
54+
import org.example.action.oauth.TokenAction;
5455
import org.example.action.user.EditAction;
5556
import org.primeframework.mvc.action.ActionInvocation;
5657
import org.primeframework.mvc.action.ExecuteMethodConfiguration;
@@ -77,6 +78,7 @@
7778
import org.primeframework.mvc.security.CBCCipherProvider;
7879
import org.primeframework.mvc.security.CipherProvider;
7980
import org.primeframework.mvc.security.GCMCipherProvider;
81+
import org.primeframework.mvc.security.MockOAuthUserLoginSecurityContext;
8082
import org.primeframework.mvc.security.MockStaticClasspathResourceFilter;
8183
import org.primeframework.mvc.security.MockStaticResourceFilter;
8284
import org.primeframework.mvc.security.MockUserLoginSecurityContext;
@@ -198,6 +200,9 @@ public void beforeMethod() {
198200
MockUserLoginSecurityContext.roles.clear();
199201
MockUserLoginSecurityContext.currentUser = null;
200202

203+
// clear any additional params set in the MockOAuthUserLoginSecurityContext
204+
MockOAuthUserLoginSecurityContext.additionalParameters.clear();
205+
201206
// Reset CSRF configuration
202207
configuration.csrfEnabled = false;
203208

@@ -214,6 +219,7 @@ public void beforeMethod() {
214219
// Reset
215220
EditAction.getCalled = false;
216221
SecureAction.UnknownParameters.clear();
222+
TokenAction.UnknownParameters.clear();
217223

218224
TestUnhandledExceptionHandler.reset();
219225

src/test/java/org/primeframework/mvc/security/MockOAuthUserLoginSecurityContext.java

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2016-2023, Inversoft Inc., All Rights Reserved
2+
* Copyright (c) 2016-2025, Inversoft Inc., All Rights Reserved
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -15,7 +15,10 @@
1515
*/
1616
package org.primeframework.mvc.security;
1717

18+
import java.util.HashMap;
1819
import java.util.HashSet;
20+
import java.util.List;
21+
import java.util.Map;
1922
import java.util.Set;
2023

2124
import com.google.inject.Inject;
@@ -37,6 +40,8 @@ public class MockOAuthUserLoginSecurityContext extends BaseJWTRefreshTokenCookie
3740

3841
public static boolean ValidateJWTOnLogin = true;
3942

43+
public static Map<String, List<String>> additionalParameters = new HashMap<>();
44+
4045
public static String clientId;
4146

4247
public static String clientSecret;
@@ -95,7 +100,8 @@ protected OAuthConfiguration oauthConfiguration() {
95100
return new OAuthConfiguration().with(c -> c.authenticationMethod = tokenAuthenticationMethod)
96101
.with(c -> c.clientId = clientId)
97102
.with(c -> c.clientSecret = clientSecret)
98-
.with(c -> c.tokenEndpoint = TokenEndpoint);
103+
.with(c -> c.tokenEndpoint = TokenEndpoint)
104+
.with(c -> c.additionalParameters.putAll(additionalParameters));
99105
}
100106

101107
@Override

0 commit comments

Comments
 (0)