Skip to content

Commit c2dde38

Browse files
committed
Try out RelyingPartyV2 in demo
1 parent ec0841a commit c2dde38

File tree

3 files changed

+83
-57
lines changed

3 files changed

+83
-57
lines changed

webauthn-server-demo/src/main/java/demo/webauthn/InMemoryRegistrationStorage.java

Lines changed: 41 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,9 @@
2626

2727
import com.google.common.cache.Cache;
2828
import com.google.common.cache.CacheBuilder;
29-
import com.yubico.internal.util.CollectionUtil;
30-
import com.yubico.webauthn.AssertionResult;
31-
import com.yubico.webauthn.CredentialRepository;
32-
import com.yubico.webauthn.RegisteredCredential;
29+
import com.yubico.webauthn.AssertionResultV2;
30+
import com.yubico.webauthn.CredentialRepositoryV2;
31+
import com.yubico.webauthn.UsernameRepository;
3332
import com.yubico.webauthn.data.ByteArray;
3433
import com.yubico.webauthn.data.PublicKeyCredentialDescriptor;
3534
import demo.webauthn.data.CredentialRegistration;
@@ -44,20 +43,21 @@
4443
import org.slf4j.Logger;
4544
import org.slf4j.LoggerFactory;
4645

47-
public class InMemoryRegistrationStorage implements CredentialRepository {
46+
public class InMemoryRegistrationStorage
47+
implements CredentialRepositoryV2<CredentialRegistration>, UsernameRepository {
4848

4949
private final Cache<String, Set<CredentialRegistration>> storage =
5050
CacheBuilder.newBuilder().maximumSize(1000).expireAfterAccess(1, TimeUnit.DAYS).build();
5151

5252
private static final Logger logger = LoggerFactory.getLogger(InMemoryRegistrationStorage.class);
5353

5454
////////////////////////////////////////////////////////////////////////////////
55-
// The following methods are required by the CredentialRepository interface.
55+
// The following methods are required by the CredentialRepositoryV2 interface.
5656
////////////////////////////////////////////////////////////////////////////////
5757

5858
@Override
59-
public Set<PublicKeyCredentialDescriptor> getCredentialIdsForUsername(String username) {
60-
return getRegistrationsByUsername(username).stream()
59+
public Set<PublicKeyCredentialDescriptor> getCredentialIdsForUserHandle(ByteArray userHandle) {
60+
return getRegistrationsByUserHandle(userHandle).stream()
6161
.map(
6262
registration ->
6363
PublicKeyCredentialDescriptor.builder()
@@ -68,63 +68,53 @@ public Set<PublicKeyCredentialDescriptor> getCredentialIdsForUsername(String use
6868
}
6969

7070
@Override
71-
public Optional<String> getUsernameForUserHandle(ByteArray userHandle) {
72-
return getRegistrationsByUserHandle(userHandle).stream()
73-
.findAny()
74-
.map(CredentialRegistration::getUsername);
75-
}
76-
77-
@Override
78-
public Optional<ByteArray> getUserHandleForUsername(String username) {
79-
return getRegistrationsByUsername(username).stream()
80-
.findAny()
81-
.map(reg -> reg.getUserIdentity().getId());
82-
}
83-
84-
@Override
85-
public Optional<RegisteredCredential> lookup(ByteArray credentialId, ByteArray userHandle) {
71+
public Optional<CredentialRegistration> lookup(ByteArray credentialId, ByteArray userHandle) {
8672
Optional<CredentialRegistration> registrationMaybe =
8773
storage.asMap().values().stream()
8874
.flatMap(Collection::stream)
89-
.filter(credReg -> credentialId.equals(credReg.getCredential().getCredentialId()))
75+
.filter(
76+
credReg ->
77+
credentialId.equals(credReg.getCredential().getCredentialId())
78+
&& userHandle.equals(credReg.getUserHandle()))
9079
.findAny();
9180

9281
logger.debug(
9382
"lookup credential ID: {}, user handle: {}; result: {}",
9483
credentialId,
9584
userHandle,
9685
registrationMaybe);
97-
return registrationMaybe.map(
98-
registration ->
99-
RegisteredCredential.builder()
100-
.credentialId(registration.getCredential().getCredentialId())
101-
.userHandle(registration.getUserIdentity().getId())
102-
.publicKeyCose(registration.getCredential().getPublicKeyCose())
103-
.signatureCount(registration.getCredential().getSignatureCount())
104-
.build());
86+
87+
return registrationMaybe;
10588
}
10689

10790
@Override
108-
public Set<RegisteredCredential> lookupAll(ByteArray credentialId) {
109-
return CollectionUtil.immutableSet(
110-
storage.asMap().values().stream()
111-
.flatMap(Collection::stream)
112-
.filter(reg -> reg.getCredential().getCredentialId().equals(credentialId))
113-
.map(
114-
reg ->
115-
RegisteredCredential.builder()
116-
.credentialId(reg.getCredential().getCredentialId())
117-
.userHandle(reg.getUserIdentity().getId())
118-
.publicKeyCose(reg.getCredential().getPublicKeyCose())
119-
.signatureCount(reg.getCredential().getSignatureCount())
120-
.build())
121-
.collect(Collectors.toSet()));
91+
public boolean credentialIdExists(ByteArray credentialId) {
92+
return storage.asMap().values().stream()
93+
.flatMap(Collection::stream)
94+
.anyMatch(reg -> reg.getCredential().getCredentialId().equals(credentialId));
95+
}
96+
97+
////////////////////////////////////////////////////////////////////////////////
98+
// The following methods are required by the UsernameRepository interface.
99+
////////////////////////////////////////////////////////////////////////////////
100+
101+
@Override
102+
public Optional<ByteArray> getUserHandleForUsername(String username) {
103+
return getRegistrationsByUsername(username).stream()
104+
.findAny()
105+
.map(reg -> reg.getUserIdentity().getId());
122106
}
123107

124108
////////////////////////////////////////////////////////////////////////////////
125109
// The following methods are specific to this demo application.
126110
////////////////////////////////////////////////////////////////////////////////
127111

112+
public Optional<String> getUsernameForUserHandle(ByteArray userHandle) {
113+
return getRegistrationsByUserHandle(userHandle).stream()
114+
.findAny()
115+
.map(CredentialRegistration::getUsername);
116+
}
117+
128118
public boolean addRegistrationByUsername(String username, CredentialRegistration reg) {
129119
try {
130120
return storage.get(username, HashSet::new).add(reg);
@@ -152,18 +142,19 @@ public Collection<CredentialRegistration> getRegistrationsByUserHandle(ByteArray
152142
.collect(Collectors.toList());
153143
}
154144

155-
public void updateSignatureCount(AssertionResult result) {
145+
public void updateSignatureCount(AssertionResultV2<CredentialRegistration> result) {
156146
CredentialRegistration registration =
157147
getRegistrationByUsernameAndCredentialId(
158-
result.getUsername(), result.getCredential().getCredentialId())
148+
result.getCredential().getUsername(), result.getCredential().getCredentialId())
159149
.orElseThrow(
160150
() ->
161151
new NoSuchElementException(
162152
String.format(
163153
"Credential \"%s\" is not registered to user \"%s\"",
164-
result.getCredential().getCredentialId(), result.getUsername())));
154+
result.getCredential().getCredentialId(),
155+
result.getCredential().getUsername())));
165156

166-
Set<CredentialRegistration> regs = storage.getIfPresent(result.getUsername());
157+
Set<CredentialRegistration> regs = storage.getIfPresent(result.getCredential().getUsername());
167158
regs.remove(registration);
168159
regs.add(
169160
registration.withCredential(

webauthn-server-demo/src/main/java/demo/webauthn/WebAuthnServer.java

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -39,12 +39,13 @@
3939
import com.yubico.internal.util.CertificateParser;
4040
import com.yubico.internal.util.JacksonCodecs;
4141
import com.yubico.util.Either;
42-
import com.yubico.webauthn.AssertionResult;
42+
import com.yubico.webauthn.AssertionResultV2;
4343
import com.yubico.webauthn.FinishAssertionOptions;
4444
import com.yubico.webauthn.FinishRegistrationOptions;
4545
import com.yubico.webauthn.RegisteredCredential;
4646
import com.yubico.webauthn.RegistrationResult;
4747
import com.yubico.webauthn.RelyingParty;
48+
import com.yubico.webauthn.RelyingPartyV2;
4849
import com.yubico.webauthn.StartAssertionOptions;
4950
import com.yubico.webauthn.StartRegistrationOptions;
5051
import com.yubico.webauthn.attestation.YubicoJsonMetadataService;
@@ -144,7 +145,7 @@ private static MetadataService getMetadataService()
144145
private final Clock clock = Clock.systemDefaultZone();
145146
private final ObjectMapper jsonMapper = JacksonCodecs.json();
146147

147-
private final RelyingParty rp;
148+
private final RelyingPartyV2<CredentialRegistration> rp;
148149

149150
public WebAuthnServer()
150151
throws CertificateException,
@@ -191,6 +192,7 @@ public WebAuthnServer(
191192
RelyingParty.builder()
192193
.identity(rpIdentity)
193194
.credentialRepository(this.userStorage)
195+
.usernameRepository(this.userStorage)
194196
.origins(origins)
195197
.attestationConveyancePreference(Optional.of(AttestationConveyancePreference.DIRECT))
196198
.attestationTrustSource(metadataService)
@@ -488,7 +490,7 @@ public Either<List<String>, SuccessfulAuthenticationResult> finishAuthentication
488490
return Either.left(Arrays.asList("Assertion failed!", "No such assertion in progress."));
489491
} else {
490492
try {
491-
AssertionResult result =
493+
AssertionResultV2<CredentialRegistration> result =
492494
rp.finishAssertion(
493495
FinishAssertionOptions.builder()
494496
.request(request.getRequest())
@@ -501,7 +503,7 @@ public Either<List<String>, SuccessfulAuthenticationResult> finishAuthentication
501503
} catch (Exception e) {
502504
logger.error(
503505
"Failed to update signature count for user \"{}\", credential \"{}\"",
504-
result.getUsername(),
506+
result.getCredential().getUsername(),
505507
response.getCredential().getId(),
506508
e);
507509
}
@@ -510,8 +512,8 @@ public Either<List<String>, SuccessfulAuthenticationResult> finishAuthentication
510512
new SuccessfulAuthenticationResult(
511513
request,
512514
response,
513-
userStorage.getRegistrationsByUsername(result.getUsername()),
514-
result.getUsername(),
515+
userStorage.getRegistrationsByUsername(result.getCredential().getUsername()),
516+
result.getCredential().getUsername(),
515517
sessions.createSession(result.getCredential().getUserHandle())));
516518
} else {
517519
return Either.left(Collections.singletonList("Assertion failed: Invalid assertion."));

webauthn-server-demo/src/main/java/demo/webauthn/data/CredentialRegistration.java

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,20 +26,23 @@
2626

2727
import com.fasterxml.jackson.annotation.JsonIgnore;
2828
import com.fasterxml.jackson.annotation.JsonProperty;
29+
import com.yubico.webauthn.CredentialRecord;
2930
import com.yubico.webauthn.RegisteredCredential;
3031
import com.yubico.webauthn.data.AuthenticatorTransport;
32+
import com.yubico.webauthn.data.ByteArray;
3133
import com.yubico.webauthn.data.UserIdentity;
3234
import java.time.Instant;
3335
import java.util.Optional;
3436
import java.util.SortedSet;
3537
import lombok.Builder;
38+
import lombok.NonNull;
3639
import lombok.Value;
3740
import lombok.With;
3841

3942
@Value
4043
@Builder
4144
@With
42-
public class CredentialRegistration {
45+
public class CredentialRegistration implements CredentialRecord {
4346

4447
UserIdentity userIdentity;
4548
Optional<String> credentialNickname;
@@ -58,4 +61,34 @@ public String getRegistrationTimestamp() {
5861
public String getUsername() {
5962
return userIdentity.getName();
6063
}
64+
65+
@Override
66+
public @NonNull ByteArray getCredentialId() {
67+
return credential.getCredentialId();
68+
}
69+
70+
@Override
71+
public @NonNull ByteArray getUserHandle() {
72+
return userIdentity.getId();
73+
}
74+
75+
@Override
76+
public @NonNull ByteArray getPublicKeyCose() {
77+
return credential.getPublicKeyCose();
78+
}
79+
80+
@Override
81+
public long getSignatureCount() {
82+
return credential.getSignatureCount();
83+
}
84+
85+
@Override
86+
public Optional<Boolean> isBackupEligible() {
87+
return credential.isBackupEligible();
88+
}
89+
90+
@Override
91+
public Optional<Boolean> isBackedUp() {
92+
return credential.isBackedUp();
93+
}
6194
}

0 commit comments

Comments
 (0)