Skip to content

Commit 454308d

Browse files
Merge pull request #85 from prime-framework/lyle/add-additional-token-call-params
add additional token call params to BaseJWTRefreshTokenCookiesUserLoginSecurityContext
2 parents 78f37ad + 2c0dce3 commit 454308d

File tree

8 files changed

+71
-8
lines changed

8 files changed

+71
-8
lines changed

build.savant

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ logbackVersion = "1.4.14"
2929
slf4jVersion = "2.0.13"
3030
testngVersion = "7.8.0"
3131

32-
project(group: "org.primeframework", name: "prime-mvc", version: "4.33.1", licenses: ["ApacheV2_0"]) {
32+
project(group: "org.primeframework", name: "prime-mvc", version: "4.34.0", licenses: ["ApacheV2_0"]) {
3333
workflow {
3434
fetch {
3535
// Dependency resolution order:

pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
<groupId>org.primeframework</groupId>
77
<artifactId>prime-mvc</artifactId>
8-
<version>4.33.1</version>
8+
<version>4.34.0</version>
99
<packaging>jar</packaging>
1010

1111
<name>FusionAuth App</name>

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 & 2 deletions
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
@@ -174,7 +180,6 @@ public void refreshTokenEndpointUp_auth_client_secret_basic() {
174180
MockOAuthUserLoginSecurityContext.clientSecret = "the client secret";
175181
MockOAuthUserLoginSecurityContext.ValidateJWTOnLogin = false;
176182
MockOAuthUserLoginSecurityContext.TokenEndpoint = "http://localhost:" + simulator.getPort() + "/oauth/token";
177-
178183
// Setting 'expired: true' on the request just tells the Login action to create an expired JWT and store it in the LoginContext.
179184
// - 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.
180185
simulator.test("/oauth/login")
@@ -256,6 +261,36 @@ public void refreshTokenEndpointUp_no_auth() {
256261
.assertBodyContains("Logged in");
257262
}
258263

264+
@Test
265+
public void refreshTokenEndpoint_additionalParameters() {
266+
// The token action should get called with the additional parameters we configure on the security context.
267+
var tenantId = new UUID(5, 0).toString();
268+
MockOAuthUserLoginSecurityContext.additionalParameters.put("tenantId", List.of(tenantId));
269+
MockOAuthUserLoginSecurityContext.TokenEndpoint = "http://localhost:" + simulator.getPort() + "/oauth/token";
270+
MockOAuthUserLoginSecurityContext.ValidateJWTOnLogin = false;
271+
272+
// Setting 'expired: true' on the request just tells the Login action to create an expired JWT and store it in the LoginContext.
273+
// - 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.
274+
//
275+
// The refresh action is what will call the token endpoint with the additional parameters.
276+
simulator.test("/oauth/login")
277+
.withParameter("expired", "true")
278+
.post()
279+
.assertStatusCode(200);
280+
281+
// The JWT in the security context was found to be expired on first use even though login succeeded.
282+
// - This will have caused us to refresh the token, so this action should succeed.
283+
simulator.test("/oauth/protected-resource")
284+
.get()
285+
.assertStatusCode(200)
286+
.assertBodyContains("Logged in");
287+
288+
// Ensure that the tenantId was added to the request and caught in unknown parameters
289+
// if additional parameters were sent, validate them
290+
assertTrue(TokenAction.UnknownParameters.containsKey("tenantId"), "Missing tenantId in unknown parameters");
291+
assertEquals(TokenAction.UnknownParameters.get("tenantId"), new String[]{tenantId}, "Mismatched tenantId in unknown parameters");
292+
}
293+
259294
public static class TestScopeModule extends AbstractModule {
260295
@Override
261296
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
@@ -53,6 +53,7 @@
5353
import io.fusionauth.http.server.HTTPResponse;
5454
import io.fusionauth.http.server.HTTPServerConfiguration;
5555
import org.example.action.SecureAction;
56+
import org.example.action.oauth.TokenAction;
5657
import org.example.action.user.EditAction;
5758
import org.primeframework.mvc.action.ActionInvocation;
5859
import org.primeframework.mvc.action.ExecuteMethodConfiguration;
@@ -79,6 +80,7 @@
7980
import org.primeframework.mvc.security.CBCCipherProvider;
8081
import org.primeframework.mvc.security.CipherProvider;
8182
import org.primeframework.mvc.security.GCMCipherProvider;
83+
import org.primeframework.mvc.security.MockOAuthUserLoginSecurityContext;
8284
import org.primeframework.mvc.security.MockStaticClasspathResourceFilter;
8385
import org.primeframework.mvc.security.MockStaticResourceFilter;
8486
import org.primeframework.mvc.security.MockUserLoginSecurityContext;
@@ -200,6 +202,9 @@ public void beforeMethod() {
200202
MockUserLoginSecurityContext.roles.clear();
201203
MockUserLoginSecurityContext.currentUser = null;
202204

205+
// clear any additional params set in the MockOAuthUserLoginSecurityContext
206+
MockOAuthUserLoginSecurityContext.additionalParameters.clear();
207+
203208
// Reset CSRF configuration
204209
configuration.csrfEnabled = false;
205210

@@ -219,6 +224,7 @@ public void beforeMethod() {
219224
// Reset
220225
EditAction.getCalled = false;
221226
SecureAction.UnknownParameters.clear();
227+
TokenAction.UnknownParameters.clear();
222228

223229
TestUnhandledExceptionHandler.reset();
224230

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

Lines changed: 9 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,8 +15,12 @@
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;
23+
import java.util.UUID;
2024

2125
import com.google.inject.Inject;
2226
import io.fusionauth.http.server.HTTPRequest;
@@ -37,6 +41,8 @@ public class MockOAuthUserLoginSecurityContext extends BaseJWTRefreshTokenCookie
3741

3842
public static boolean ValidateJWTOnLogin = true;
3943

44+
public static Map<String, List<String>> additionalParameters = new HashMap<>();
45+
4046
public static String clientId;
4147

4248
public static String clientSecret;
@@ -95,7 +101,8 @@ protected OAuthConfiguration oauthConfiguration() {
95101
return new OAuthConfiguration().with(c -> c.authenticationMethod = tokenAuthenticationMethod)
96102
.with(c -> c.clientId = clientId)
97103
.with(c -> c.clientSecret = clientSecret)
98-
.with(c -> c.tokenEndpoint = TokenEndpoint);
104+
.with(c -> c.tokenEndpoint = TokenEndpoint)
105+
.with(c -> c.additionalParameters.putAll(additionalParameters));
99106
}
100107

101108
@Override

0 commit comments

Comments
 (0)