Skip to content

Commit 6e4cec6

Browse files
authored
Merge pull request #9 from GluuFederation/agama-lab-flow-designer
fix(agama-passkeys): Aligning with FIDO server
2 parents 75db461 + b8cacb9 commit 6e4cec6

File tree

6 files changed

+222
-219
lines changed

6 files changed

+222
-219
lines changed

lib/org/gluu/agama/passkey/ScimFido2Helper.java

+4-2
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,11 @@ public Map<String, Object> getFidoDeviceByUser(String userInum) throws IOExcepti
3333
StringJoiner joiner = new StringJoiner("&");
3434
Map.of("userId", userInum).forEach((k, v) -> joiner.add(k + "=" + v));
3535
request.setQuery(joiner.toString());
36-
36+
log.info("userId:"+userInum)
37+
log.info("request:"+request)
38+
3739
String response = sendRequest(request, true, true).getContentAsJSONObject().toJSONString();
38-
log.debug("Response scim fido2 devices: {}", response);
40+
log.info("Response scim fido2 devices: {}", response);
3941
JSONObject resObject = new JSONObject(response);
4042
int count = resObject.getInt("totalResults");
4143
List<Map<String, String>> mapList = new ArrayList<>();

lib/org/gluu/agama/passkey/ScimWSBase.java

+5-5
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ public ScimWSBase(boolean doHealthCheck, ScimSetting scimSetting) throws IOExcep
4848
String authz = scimSetting.getScimClientId() + ":" + scimSetting.getScimClientSecret();
4949
String authzEncoded = new String(Base64.getEncoder().encode(authz.getBytes(UTF_8)), UTF_8);
5050
basicAuthnHeader = "Basic " + authzEncoded;
51-
log.debug("Scim loaded successfully, basicAuthnHeader: {}, apiBase: {}", authz, apiBase);
51+
log.info("Scim loaded successfully, basicAuthnHeader: {}, apiBase: {}", authz, apiBase);
5252
}
5353
}
5454

@@ -66,7 +66,7 @@ protected HTTPResponse sendRequest(HTTPRequest request, boolean checkOK, boolean
6666
if (withToken) {
6767
refreshToken();
6868
request.setAuthorization("Bearer " + token);
69-
log.debug("--> Header Authorization: Bearer {}", token);
69+
log.info("--> Header Authorization: Bearer {}", token);
7070
}
7171

7272
HTTPResponse r = request.send();
@@ -83,7 +83,7 @@ protected String encode(String str) {
8383
private void ensureScimAvailability() throws IOException {
8484
try {
8585
URL url = new URL(serverBase + "/jans-scim/sys/health-check");
86-
log.debug("Scim health-check url: {}", url);
86+
log.info("Scim health-check url: {}", url);
8787
HTTPRequest request = new HTTPRequest(HTTPRequest.Method.GET, url);
8888
sendRequest(request, true, false);
8989
} catch (Exception e) {
@@ -103,10 +103,10 @@ private void refreshToken() throws IOException {
103103
setTimeouts(request);
104104
request.setQuery(joiner.toString());
105105
request.setAuthorization(basicAuthnHeader);
106-
106+
log.info("request : " +request.getURI().toURL().toString());
107107
try {
108108
Map<String, Object> jobj = request.send().getContentAsJSONObject();
109-
109+
log.info("jobj : "+jobj)
110110
long exp = Long.parseLong(jobj.get("expires_in").toString()) * 1000;
111111
tokenExp = System.currentTimeMillis() + exp;
112112
token = jobj.get("access_token").toString();

lib/org/gluu/agama/passkey/authn/FidoValidator.java

+18-11
Original file line numberDiff line numberDiff line change
@@ -2,23 +2,26 @@
22

33
import io.jans.fido2.client.AssertionService;
44
import io.jans.fido2.client.Fido2ClientFactory;
5+
6+
import io.jans.fido2.model.assertion.AssertionOptions;
7+
import com.fasterxml.jackson.databind.ObjectMapper;
58
import jakarta.ws.rs.core.Response;
69
import net.minidev.json.JSONObject;
710
import org.gluu.agama.passkey.NetworkUtils;
811
import org.slf4j.Logger;
912
import org.slf4j.LoggerFactory;
10-
13+
import io.jans.fido2.model.assertion.AssertionResult;
1114
import java.io.IOException;
1215
import java.util.Map;
1316

1417
public class FidoValidator {
1518

1619
private static final Logger logger = LoggerFactory.getLogger(FidoValidator.class);
17-
20+
private static ObjectMapper mapper = new ObjectMapper();
1821
private final String metadataConfiguration;
1922

2023
public FidoValidator() throws IOException {
21-
logger.debug("Inspecting fido2 configuration discovery URL");
24+
logger.info("Inspecting fido2 configuration discovery URL");
2225
String metadataUri = NetworkUtils.urlBeforeContextPath() + "/.well-known/fido2-configuration";
2326

2427
try (Response response = Fido2ClientFactory.instance().createMetaDataConfigurationService(metadataUri).getMetadataConfiguration()) {
@@ -34,17 +37,20 @@ public FidoValidator() throws IOException {
3437
}
3538

3639
public String assertionRequest(String uid) throws IOException {
37-
logger.debug("Building an assertion request for {}", uid);
40+
logger.info("Building an assertion request for {}", uid);
3841
// Using assertionService as a private class field gives serialization trouble...
3942
AssertionService assertionService = Fido2ClientFactory.instance().createAssertionService(metadataConfiguration);
4043
String content;
44+
AssertionOptions assertionRequest = new AssertionOptions()
4145
if (uid == null) {
4246
content = JSONObject.toJSONString(Map.of("timeout", 90000));
4347
} else {
4448
content = JSONObject.toJSONString(Map.of("timeout", 90000, "username", uid));
49+
assertionRequest.setUsername(uid);
4550
}
46-
47-
try (Response response = (uid == null ? assertionService.generateAuthenticate(content) : assertionService.authenticate(content))) {
51+
52+
53+
try (Response response = ( assertionService.authenticate(assertionRequest))) {
4854
content = response.readEntity(String.class);
4955
int status = response.getStatus();
5056

@@ -58,24 +64,25 @@ public String assertionRequest(String uid) throws IOException {
5864
}
5965

6066
public String verify(String tokenResponse) throws IOException {
61-
logger.debug("Verifying fido token response");
67+
logger.info("Verifying fido token response : "+tokenResponse);
6268
AssertionService assertionService = Fido2ClientFactory.instance().createAssertionService(metadataConfiguration);
63-
64-
Response response = assertionService.verify(tokenResponse);
69+
AssertionResult assertionResult = mapper.readValue(tokenResponse, AssertionResult.class);
70+
Response response = assertionService.verify(assertionResult);
6571
int status = response.getStatus();
6672
if (status != Response.Status.OK.getStatusCode()) {
6773
org.json.JSONObject jsonNode = new org.json.JSONObject(response.readEntity(String.class));
6874
StringBuilder sb = new StringBuilder(String.format("Verification step failed, status: %s", status));
6975
if (jsonNode.has("error_description")) {
7076
sb.append(String.format(", description: %s", jsonNode.getString("error_description")));
7177
}
72-
logger.error(sb.toString());
78+
logger.info(sb.toString());
7379
throw new IOException(sb.toString());
7480
}
7581

7682
String resString = response.readEntity(String.class);
83+
logger.info("Response : "+resString)
7784
org.json.JSONObject jsonNode = new org.json.JSONObject(resString);
78-
logger.error("Status: {}, Response: {}", status, jsonNode);
85+
logger.info("Status: {}, Response: {}", status, jsonNode);
7986
if (jsonNode.has("username")) {
8087
String user = jsonNode.getString("username");
8188
logger.debug("User returned: {}", user);

lib/org/gluu/agama/passkey/enroll/FidoEnroller.java

+12-8
Original file line numberDiff line numberDiff line change
@@ -24,16 +24,19 @@ public String getAttestationMessage(String id) throws IOException {
2424
HTTPRequest request = new HTTPRequest(HTTPRequest.Method.GET, new URL(getApiBase() + "/enrollment/fido2/attestation"));
2525

2626
StringJoiner joiner = new StringJoiner("&");
27-
Map.of("userid", id, "platformAuthn", "false").forEach((k, v) -> joiner.add(k + "=" + encode(v)));
27+
// Map.of("userid", id, "platformAuthn", "false").forEach((k, v) -> joiner.add(k + "=" + encode(v)));
28+
29+
Map.of("userid", id).forEach((k, v) -> joiner.add(k + "=" + encode(v)));
2830
request.setQuery(joiner.toString());
2931

30-
log.debug("Generating an attestation message for {}", id);
32+
log.info("Generating an attestation message for {}", id);
3133
HTTPResponse response = sendRequest(request, false, true);
3234
String responseContent = response.getContent();
3335
int status = response.getStatusCode();
34-
36+
log.info("Status of attestation :"+ status);
37+
log.info("responseContent : "+ responseContent);
3538
if (status != 200) {
36-
log.error("Attestation response was: ({}) {}", status, responseContent);
39+
log.info("Attestation response was: ({}) {}", status, responseContent);
3740
throw new Exception(response.getContentAsJSONObject().get("code").toString());
3841
}
3942
return responseContent;
@@ -49,11 +52,12 @@ public String verifyRegistration(String id, String tokenResponse) throws IOExcep
4952
new URL(getApiBase() + "/enrollment/fido2/registration/" + encode(id)));
5053
request.setQuery(tokenResponse);
5154

52-
log.debug("Verifying registration");
55+
log.info("Verifying registration : "+id +":"+ tokenResponse);
5356
HTTPResponse response = sendRequest(request, false, true);
5457
int status = response.getStatusCode();
58+
log.info("Status : "+status)
5559
Map<String, Object> map = response.getContentAsJSONObject();
56-
60+
log.info("Map: "+map)
5761
if (status != 201) {
5862
log.error("Verification response was: ({}) {}", status, response.getContent());
5963
throw new Exception(map.get("code").toString());
@@ -75,11 +79,11 @@ public boolean nameIt(String id, String nickname, String key) throws IOException
7579
request.setContentType(APPLICATION_JSON);
7680
request.setQuery(body);
7781

78-
log.debug("Naming fido credential for {}", nickname);
82+
log.info("Naming fido credential for {}", nickname);
7983
HTTPResponse response = sendRequest(request, false, true);
8084
int status = response.getStatusCode();
8185

82-
log.debug("Response was ({}): {}", status, response.getContent());
86+
log.info("Response was ({}): {}", status, response.getContent());
8387
return status == 200;
8488
} catch (Exception e) {
8589
throw new IOException("Failed to name fido credential", e);

web/nickname.ftlh

+60-62
Original file line numberDiff line numberDiff line change
@@ -1,65 +1,63 @@
1-
<!doctype html>
2-
<html xmlns="http://www.w3.org/1999/xhtml" lang="en">
3-
<head>
4-
<title>Passkey nickname</title>
5-
<meta name="viewport" content="width=device-width, initial-scale=1">
6-
<link rel="icon" href="${webCtx.contextPath}/servlet/favicon" type="image/x-icon">
7-
<script src="https://cdn.tailwindcss.com"></script>
8-
<script>
9-
tailwind.config = {
10-
darkMode: 'class'
11-
}
12-
</script>
13-
</head>
1+
[#ftl output_format="HTML"]
2+
<!DOCTYPE html>
3+
<html lang="en">
4+
<head><meta name="viewport" content="width=device-width, initial-scale=1"><title>Passkey nickname
5+
</title><link rel="icon" href="${webCtx.contextPath}/servlet/favicon" type="image/x-icon"></head>
146
<body>
15-
<section class="bg-gray-100 dark:bg-gray-900">
16-
<div class="flex flex-col items-center justify-center px-6 py-8 mx-auto md:h-screen lg:py-0">
17-
<div class="flex flex-col items-center mb-6 text-2xl font-semibold text-gray-900 dark:text-white">
18-
<img class="w-40 mr-2"
19-
src="https://gluu.org/wp-content/uploads/2021/02/janssen-project-transparent-630px-182px-300x86.png"
20-
alt="logo">
21-
</div>
22-
<div class="w-full bg-white rounded-lg shadow-lg dark:border md:mt-0 sm:max-w-md xl:p-0 dark:bg-gray-800 dark:border-gray-700">
23-
<form method="post" enctype="application/x-www-form-urlencoded"
24-
class="p-6 space-y-4 md:space-y-6 sm:p-8">
25-
<div class="flex items-center justify-center">
26-
<svg height="32" aria-hidden="true" viewBox="0 0 24 24" version="1.1" width="32"
27-
data-view-component="true" class="!fill-green-700 w-8 h-8">
28-
<path d="M17.28 9.28a.75.75 0 0 0-1.06-1.06l-5.97 5.97-2.47-2.47a.75.75 0 0 0-1.06 1.06l3 3a.75.75 0 0 0 1.06 0l6.5-6.5Z"></path>
29-
<path d="M12 1c6.075 0 11 4.925 11 11s-4.925 11-11 11S1 18.075 1 12 5.925 1 12 1ZM2.5 12a9.5 9.5 0 0 0 9.5 9.5 9.5 9.5 0 0 0 9.5-9.5A9.5 9.5 0 0 0 12 2.5 9.5 9.5 0 0 0 2.5 12Z"></path>
30-
</svg>
31-
</div>
327

33-
<h1 class="text-xl text-center font-medium text-gray-900 md:text-2xl dark:text-white">
34-
Successfully added a passkey
35-
</h1>
36-
37-
<p class="text-gray-600 leading-tight">
38-
From now on, you can use this passkey to sign-in.
39-
</p>
40-
41-
<div class="w-full border-t border-gray-300"></div>
42-
43-
<div class="space-y-2">
44-
<label class="text-gray-800" for="nickname">
45-
Passkey nickname
46-
</label>
47-
<p class="text-gray-600 leading-tight">
48-
This is a hardware passkey - you may want to nickname it by its manufacturer or device model.
49-
</p>
50-
</div>
51-
52-
<input type="text" name="nickname" id="nickname"
53-
class="bg-gray-50 border border-gray-300 text-gray-900 sm:text-sm rounded-lg focus:ring-primary-600 focus:border-primary-600 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"
54-
placeholder="" required="" autocomplete="off">
55-
56-
<button type="submit"
57-
class="w-full text-white bg-green-600 hover:bg-green-700 font-medium rounded-lg text-sm px-5 py-2.5 text-center dark:bg-primary-600 dark:hover:bg-primary-700 dark:focus:ring-primary-800">
58-
Done
59-
</button>
60-
</form>
61-
</div>
62-
</div>
63-
</section>
8+
9+
10+
11+
12+
13+
14+
<section class="bg-gray-100 dark:bg-gray-900">
15+
<div class="flex flex-col items-center justify-center px-6 py-8 mx-auto md:h-screen lg:py-0">
16+
<div class="flex flex-col items-center mb-6 text-2xl font-semibold text-gray-900 dark:text-white">
17+
<img src="https://gluu.org/wp-content/uploads/2021/02/janssen-project-transparent-630px-182px-300x86.png" alt="logo" class="w-40 mr-2">
18+
</div>
19+
<div class="w-full bg-white rounded-lg shadow-lg dark:border md:mt-0 sm:max-w-md xl:p-0 dark:bg-gray-800 dark:border-gray-700">
20+
<form method="post" enctype="application/x-www-form-urlencoded" class="p-6 space-y-4 md:space-y-6 sm:p-8">
21+
<div class="flex items-center justify-center">
22+
<svg height="32" aria-hidden="true" viewBox="0 0 24 24" version="1.1" width="32" data-view-component="true" class="-fill-green-700 w-8 h-8">
23+
<path d="M17.28 9.28a.75.75 0 0 0-1.06-1.06l-5.97 5.97-2.47-2.47a.75.75 0 0 0-1.06 1.06l3 3a.75.75 0 0 0 1.06 0l6.5-6.5Z">
24+
</path>
25+
<path d="M12 1c6.075 0 11 4.925 11 11s-4.925 11-11 11S1 18.075 1 12 5.925 1 12 1ZM2.5 12a9.5 9.5 0 0 0 9.5 9.5 9.5 9.5 0 0 0 9.5-9.5A9.5 9.5 0 0 0 12 2.5 9.5 9.5 0 0 0 2.5 12Z">
26+
</path>
27+
</svg>
28+
</div>
29+
<h1 class="text-xl text-center font-medium text-gray-900 md:text-2xl dark:text-white">
30+
Successfully added a passkey
31+
</h1>
32+
<p class="text-gray-600 leading-tight">
33+
From now on, you can use this passkey to sign-in.
34+
</p>
35+
<div class="w-full border-t border-gray-300">
36+
</div>
37+
<div class="space-y-2">
38+
<label for="nickname" class="text-gray-800">
39+
Passkey nickname
40+
</label>
41+
<p class="text-gray-600 leading-tight">
42+
You may want to nickname it by its manufacturer or device model.
43+
</p>
44+
</div>
45+
<input type="text" name="nickname" id="nickname" placeholder="" required="" autocomplete="off" class="bg-gray-50 border border-gray-300 text-gray-900 sm:text-sm rounded-lg focus:ring-primary-600 focus:border-primary-600 block w-full p-2-5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500">
46+
<button type="submit" class="w-full text-white bg-green-600 hover:bg-green-700 font-medium rounded-lg text-sm px-5 py-2-5 text-center dark:bg-primary-600 dark:hover:bg-primary-700 dark:focus:ring-primary-800">
47+
Done
48+
</button>
49+
</form>
50+
</div>
51+
</div>
52+
</section>
53+
54+
55+
6456
</body>
65-
</html>
57+
58+
<script src="https://cdn.tailwindcss.com"></script><script>
59+
tailwind.config = {
60+
darkMode: 'class'
61+
}
62+
</script>
63+
</html>

0 commit comments

Comments
 (0)