Skip to content

Commit 9dcce61

Browse files
Add support OneTimeToken customization
Closes spring-projectsgh-16939 Signed-off-by: Max Batischev <[email protected]>
1 parent ba320fb commit 9dcce61

File tree

5 files changed

+48
-7
lines changed

5 files changed

+48
-7
lines changed

Diff for: core/src/main/java/org/springframework/security/authentication/ott/GenerateOneTimeTokenRequest.java

+18
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,15 @@
1717
package org.springframework.security.authentication.ott;
1818

1919
import java.time.Duration;
20+
import java.util.UUID;
2021

2122
import org.springframework.util.Assert;
2223

2324
/**
2425
* Class to store information related to an One-Time Token authentication request
2526
*
2627
* @author Marcus da Coregio
28+
* @author Max Batiscev
2729
* @since 6.4
2830
*/
2931
public class GenerateOneTimeTokenRequest {
@@ -34,6 +36,8 @@ public class GenerateOneTimeTokenRequest {
3436

3537
private final Duration expiresIn;
3638

39+
private final String tokenValue;
40+
3741
public GenerateOneTimeTokenRequest(String username) {
3842
this(username, DEFAULT_EXPIRES_IN);
3943
}
@@ -43,6 +47,16 @@ public GenerateOneTimeTokenRequest(String username, Duration expiresIn) {
4347
Assert.notNull(expiresIn, "expiresIn cannot be null");
4448
this.username = username;
4549
this.expiresIn = expiresIn;
50+
this.tokenValue = UUID.randomUUID().toString();
51+
}
52+
53+
public GenerateOneTimeTokenRequest(String username, Duration expiresIn, String tokenValue) {
54+
Assert.hasText(username, "username cannot be empty");
55+
Assert.hasText(tokenValue, "tokenValue cannot be empty");
56+
Assert.notNull(expiresIn, "expiresIn cannot be null");
57+
this.username = username;
58+
this.expiresIn = expiresIn;
59+
this.tokenValue = tokenValue;
4660
}
4761

4862
public String getUsername() {
@@ -53,4 +67,8 @@ public Duration getExpiresIn() {
5367
return this.expiresIn;
5468
}
5569

70+
public String getTokenValue() {
71+
return this.tokenValue;
72+
}
73+
5674
}

Diff for: core/src/main/java/org/springframework/security/authentication/ott/InMemoryOneTimeTokenService.java

+3-3
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
* there is more or equal than 100 tokens stored in the map.
3333
*
3434
* @author Marcus da Coregio
35+
* @author Max Batischev
3536
* @since 6.4
3637
*/
3738
public final class InMemoryOneTimeTokenService implements OneTimeTokenService {
@@ -43,10 +44,9 @@ public final class InMemoryOneTimeTokenService implements OneTimeTokenService {
4344
@Override
4445
@NonNull
4546
public OneTimeToken generate(GenerateOneTimeTokenRequest request) {
46-
String token = UUID.randomUUID().toString();
4747
Instant expiresAt = this.clock.instant().plus(request.getExpiresIn());
48-
OneTimeToken ott = new DefaultOneTimeToken(token, request.getUsername(), expiresAt);
49-
this.oneTimeTokenByToken.put(token, ott);
48+
OneTimeToken ott = new DefaultOneTimeToken(request.getTokenValue(), request.getUsername(), expiresAt);
49+
this.oneTimeTokenByToken.put(request.getTokenValue(), ott);
5050
cleanExpiredTokensIfNeeded();
5151
return ott;
5252
}

Diff for: core/src/main/java/org/springframework/security/authentication/ott/JdbcOneTimeTokenService.java

+1-3
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@
2424
import java.time.Instant;
2525
import java.util.ArrayList;
2626
import java.util.List;
27-
import java.util.UUID;
2827
import java.util.function.Function;
2928

3029
import org.apache.commons.logging.Log;
@@ -130,9 +129,8 @@ public void setCleanupCron(String cleanupCron) {
130129
@Override
131130
public OneTimeToken generate(GenerateOneTimeTokenRequest request) {
132131
Assert.notNull(request, "generateOneTimeTokenRequest cannot be null");
133-
String token = UUID.randomUUID().toString();
134132
Instant expiresAt = this.clock.instant().plus(request.getExpiresIn());
135-
OneTimeToken oneTimeToken = new DefaultOneTimeToken(token, request.getUsername(), expiresAt);
133+
OneTimeToken oneTimeToken = new DefaultOneTimeToken(request.getTokenValue(), request.getUsername(), expiresAt);
136134
insertOneTimeToken(oneTimeToken);
137135
return oneTimeToken;
138136
}

Diff for: web/src/main/java/org/springframework/security/web/authentication/ott/DefaultGenerateOneTimeTokenRequestResolver.java

+15-1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717
package org.springframework.security.web.authentication.ott;
1818

1919
import java.time.Duration;
20+
import java.util.UUID;
21+
import java.util.function.Supplier;
2022

2123
import jakarta.servlet.http.HttpServletRequest;
2224

@@ -37,13 +39,15 @@ public final class DefaultGenerateOneTimeTokenRequestResolver implements Generat
3739

3840
private Duration expiresIn = DEFAULT_EXPIRES_IN;
3941

42+
private Supplier<String> tokenValueFactory = () -> UUID.randomUUID().toString();
43+
4044
@Override
4145
public GenerateOneTimeTokenRequest resolve(HttpServletRequest request) {
4246
String username = request.getParameter("username");
4347
if (!StringUtils.hasText(username)) {
4448
return null;
4549
}
46-
return new GenerateOneTimeTokenRequest(username, this.expiresIn);
50+
return new GenerateOneTimeTokenRequest(username, this.expiresIn, this.tokenValueFactory.get());
4751
}
4852

4953
/**
@@ -55,4 +59,14 @@ public void setExpiresIn(Duration expiresIn) {
5559
this.expiresIn = expiresIn;
5660
}
5761

62+
/**
63+
* Sets factory for token value generation
64+
* @param tokenValueFactory factory for token value generation
65+
* @since 6.5
66+
*/
67+
public void setTokenValueFactory(Supplier<String> tokenValueFactory) {
68+
Assert.notNull(tokenValueFactory, "tokenValueFactory cannot be null");
69+
this.tokenValueFactory = tokenValueFactory;
70+
}
71+
5872
}

Diff for: web/src/test/java/org/springframework/security/web/authentication/ott/DefaultGenerateOneTimeTokenRequestResolverTests.java

+11
Original file line numberDiff line numberDiff line change
@@ -64,4 +64,15 @@ void resolveWhenExpiresInSetThenResolvesGenerateRequest() {
6464
assertThat(generateRequest.getExpiresIn()).isEqualTo(Duration.ofSeconds(600));
6565
}
6666

67+
@Test
68+
void resolveWhenTokenValueFactorySetThenResolvesGenerateRequest() {
69+
MockHttpServletRequest request = new MockHttpServletRequest();
70+
request.setParameter("username", "test");
71+
this.requestResolver.setTokenValueFactory(() -> "tokenValue");
72+
73+
GenerateOneTimeTokenRequest generateRequest = this.requestResolver.resolve(request);
74+
75+
assertThat(generateRequest.getTokenValue()).isEqualTo("tokenValue");
76+
}
77+
6778
}

0 commit comments

Comments
 (0)