Skip to content

Commit e8ed0a1

Browse files
committed
WIP for displaying the device RP always
1 parent 3db4a64 commit e8ed0a1

File tree

4 files changed

+78
-8
lines changed

4 files changed

+78
-8
lines changed

oidc/pom.xml

+5
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,11 @@
234234
<artifactId>protobuf-java</artifactId>
235235
<version>${protobuf-java.version}</version>
236236
</dependency>
237+
<dependency>
238+
<groupId>org.jasypt</groupId>
239+
<artifactId>jasypt</artifactId>
240+
<version>1.9.3</version>
241+
</dependency>
237242
</dependencies>
238243

239244
<profiles>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package oidc.crypto;
2+
3+
import lombok.SneakyThrows;
4+
import org.jasypt.util.text.AES256TextEncryptor;
5+
6+
import java.util.UUID;
7+
8+
public class SimpleEncryptionHandler {
9+
10+
private final static AES256TextEncryptor encryptor = new AES256TextEncryptor();
11+
12+
static {
13+
encryptor.setPassword(UUID.randomUUID().toString());
14+
}
15+
16+
private SimpleEncryptionHandler() {
17+
}
18+
19+
@SneakyThrows
20+
public static String encrypt(String data) {
21+
return encryptor.encrypt(data);
22+
}
23+
24+
@SneakyThrows
25+
public static String decrypt(String encryptedData) {
26+
return encryptor.decrypt(encryptedData);
27+
}
28+
}

oidc/src/main/java/oidc/endpoints/DeviceAuthorizationEndpoint.java

+19-8
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import com.nimbusds.oauth2.sdk.http.JakartaServletUtils;
77
import jakarta.servlet.http.HttpServletRequest;
88
import jakarta.servlet.http.HttpSession;
9+
import oidc.crypto.SimpleEncryptionHandler;
910
import oidc.exceptions.InvalidGrantException;
1011
import oidc.exceptions.UnknownClientException;
1112
import oidc.model.DeviceAuthorization;
@@ -35,11 +36,13 @@
3536

3637
import java.io.IOException;
3738
import java.net.URLEncoder;
39+
import java.nio.charset.Charset;
3840
import java.security.SecureRandom;
3941
import java.time.Instant;
4042
import java.time.temporal.ChronoUnit;
4143
import java.util.*;
4244
import java.util.concurrent.atomic.AtomicBoolean;
45+
import java.util.concurrent.atomic.AtomicReference;
4346

4447
import static com.nimbusds.oauth2.sdk.GrantType.DEVICE_CODE;
4548
import static java.nio.charset.Charset.defaultCharset;
@@ -106,12 +109,15 @@ public ResponseEntity<Map<String, Object>> deviceAuthorization(HttpServletReques
106109
);
107110
deviceAuthorizationRepository.save(deviceAuthorization);
108111

112+
String hint = URLEncoder.encode(SimpleEncryptionHandler.encrypt(userCode), Charset.defaultCharset());
113+
String verificationUrlWithHint = String.format("%s?hint=%s", verificationUrl, hint);
114+
109115
Map<String, Object> results = Map.of(
110116
"device_code", deviceCode,
111117
"user_code", userCode,
112-
"verification_uri", verificationUrl,
118+
"verification_uri", verificationUrlWithHint,
113119
"verification_uri_complete", String.format("%s?user_code=%s", verificationUrl, userCode),
114-
"qr_code", QRGenerator.qrCode(verificationUrl).getImage(),
120+
"qr_code", QRGenerator.qrCode(verificationUrlWithHint).getImage(),
115121
//The lifetime in seconds of the "device_code" and "user_code"
116122
"expires_in", 60 * 15,
117123
//The minimum amount of time in seconds that the client SHOULD wait between polling requests to the token endpoint
@@ -122,18 +128,23 @@ public ResponseEntity<Map<String, Object>> deviceAuthorization(HttpServletReques
122128

123129
@GetMapping(value = "oidc/verify")
124130
public ModelAndView verification(@RequestParam(value = "user_code", required = false) String userCode,
131+
@RequestParam(value = "hint", required = false) String hint,
125132
@RequestParam(value = "error", required = false, defaultValue = "false") String error,
126133
HttpServletRequest request) {
134+
AtomicReference<String> userCodeRef = new AtomicReference<>(userCode);
127135
Map<String, Object> model = new HashMap<>();
128-
if (StringUtils.hasText(userCode)) {
136+
if (StringUtils.hasText(hint)) {
137+
userCodeRef.set(SimpleEncryptionHandler.decrypt(hint));
138+
}
139+
if (StringUtils.hasText(userCodeRef.get())) {
129140
//When the code checks out, then retrieve the client for displaying purposes
130-
findByUserCode(userCode)
141+
findByUserCode(userCodeRef.get())
131142
.flatMap(deviceAuthorization -> openIDClientRepository.findOptionalByClientId(deviceAuthorization.getClientId()))
132143
//Check the very strange use-case for the client not existing anymore
133144
.ifPresent(openIDClient -> {
134145
model.put("client", openIDClient);
135-
model.put("userCode", userCode);
136-
model.put("completeURI", true);
146+
model.put("userCode", StringUtils.hasText(userCode) ? userCodeRef.get() : null);
147+
model.put("completeURI", StringUtils.hasText(userCode));
137148
});
138149
}
139150
model.putIfAbsent("completeURI", false);
@@ -169,7 +180,7 @@ public ModelAndView postVerify(@RequestParam Map<String, String> body, HttpServl
169180
logout(request);
170181
return new ModelAndView(new RedirectView(deviceAuthorizeURL(deviceAuthorization), true));
171182
})
172-
.orElseGet(() -> this.verification(null, "true", request));
183+
.orElseGet(() -> this.verification(null, null, "true", request));
173184
return modelAndView;
174185
}
175186

@@ -178,7 +189,7 @@ public ModelAndView deviceAuthorize(@RequestParam(value = "state") String state,
178189
@RequestParam(value = "user_code") String userCode,
179190
Authentication authentication) {
180191
LOG.debug(String.format("/oidc/device_authorize %s %s", authentication.getDetails(), userCode));
181-
//If the state (e.g. userCode) corresponds with a DeviceAuthentication then mark this as success and inform the user
192+
//If the state (e.g. userCode) corresponds with a DeviceAuthentication then mark this as succes and inform the user
182193
Map<String, Object> model = new HashMap<>();
183194
Optional<DeviceAuthorization> optionalDeviceAuthorization = findByUserCode(userCode);
184195
AtomicBoolean stateMatches = new AtomicBoolean(false);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package oidc.crypto;
2+
3+
import org.jasypt.util.text.AES256TextEncryptor;
4+
import org.junit.jupiter.api.Test;
5+
6+
import java.util.UUID;
7+
8+
import static org.junit.jupiter.api.Assertions.*;
9+
10+
class SimpleEncryptionHandlerTest {
11+
12+
@Test
13+
void flow() {
14+
String data = "1234-5678";
15+
String encrypted = SimpleEncryptionHandler.encrypt(data);
16+
String original = SimpleEncryptionHandler.decrypt(encrypted);
17+
assertEquals(data, original);
18+
19+
String encryptedAgain = SimpleEncryptionHandler.encrypt(data);
20+
assertNotEquals(encrypted, encryptedAgain);
21+
22+
String decryptedAgain = SimpleEncryptionHandler.decrypt(encryptedAgain);
23+
assertEquals(data, decryptedAgain);
24+
}
25+
26+
}

0 commit comments

Comments
 (0)