From 0a991a91ceffa92b64bb336d22689bf242b36cdd Mon Sep 17 00:00:00 2001
From: Rob Winch <362503+rwinch@users.noreply.github.com>
Date: Wed, 3 Sep 2025 12:04:09 -0500
Subject: [PATCH] Enable Null checking in spring-security-webauthn via JSpecify
Closes gh-17839
---
...icKeyCredentialUserEntityRuntimeHints.java | 4 +-
.../aot/UserCredentialRuntimeHints.java | 4 +-
.../web/webauthn/aot/package-info.java | 23 +++++++
.../api/AuthenticatorAssertionResponse.java | 27 +++++---
.../api/AuthenticatorAttestationResponse.java | 12 ++--
.../api/AuthenticatorSelectionCriteria.java | 24 ++++---
.../security/web/webauthn/api/Bytes.java | 4 +-
.../web/webauthn/api/CredentialRecord.java | 8 ++-
.../api/ImmutableCredentialRecord.java | 33 +++++----
.../webauthn/api/ImmutablePublicKeyCose.java | 7 +-
...mmutablePublicKeyCredentialUserEntity.java | 12 ++--
.../web/webauthn/api/PublicKeyCredential.java | 29 +++++---
.../PublicKeyCredentialCreationOptions.java | 31 +++++----
.../api/PublicKeyCredentialDescriptor.java | 18 ++---
.../PublicKeyCredentialRequestOptions.java | 22 +++---
.../api/PublicKeyCredentialRpEntity.java | 10 ++-
.../api/PublicKeyCredentialUserEntity.java | 4 +-
.../web/webauthn/api/package-info.java | 23 +++++++
...KeyCredentialRequestOptionsRepository.java | 5 +-
...KeyCredentialRequestOptionsRepository.java | 5 +-
.../WebAuthnAuthentication.java | 4 +-
.../WebAuthnAuthenticationRequestToken.java | 4 +-
.../webauthn/authentication/package-info.java | 23 +++++++
.../AuthenticatorAttachmentDeserializer.java | 3 +-
.../AuthenticatorTransportDeserializer.java | 3 +-
.../COSEAlgorithmIdentifierDeserializer.java | 3 +-
...blicKeyCredentialCreationOptionsMixin.java | 3 +-
...ublicKeyCredentialRequestOptionsMixin.java | 3 +-
.../web/webauthn/jackson/package-info.java | 23 +++++++
...licKeyCredentialRequestOptionsRequest.java | 8 ++-
...utableRelyingPartyRegistrationRequest.java | 4 +-
...blicKeyCredentialUserEntityRepository.java | 6 +-
.../JdbcUserCredentialRepository.java | 17 +++--
...blicKeyCredentialUserEntityRepository.java | 6 +-
.../MapUserCredentialRepository.java | 4 +-
...licKeyCredentialRequestOptionsRequest.java | 4 +-
...blicKeyCredentialUserEntityRepository.java | 6 +-
.../RelyingPartyRegistrationRequest.java | 2 +
.../management/UserCredentialRepository.java | 4 +-
.../WebAuthnRelyingPartyOperations.java | 2 +
.../Webauthn4JRelyingPartyOperations.java | 69 ++++++++++++-------
.../web/webauthn/management/package-info.java | 23 +++++++
...eyCredentialCreationOptionsRepository.java | 7 +-
...eyCredentialCreationOptionsRepository.java | 5 +-
.../WebAuthnRegistrationFilter.java | 9 +--
.../webauthn/registration/package-info.java | 23 +++++++
46 files changed, 421 insertions(+), 152 deletions(-)
create mode 100644 webauthn/src/main/java/org/springframework/security/web/webauthn/aot/package-info.java
create mode 100644 webauthn/src/main/java/org/springframework/security/web/webauthn/api/package-info.java
create mode 100644 webauthn/src/main/java/org/springframework/security/web/webauthn/authentication/package-info.java
create mode 100644 webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/package-info.java
create mode 100644 webauthn/src/main/java/org/springframework/security/web/webauthn/management/package-info.java
create mode 100644 webauthn/src/main/java/org/springframework/security/web/webauthn/registration/package-info.java
diff --git a/webauthn/src/main/java/org/springframework/security/web/webauthn/aot/PublicKeyCredentialUserEntityRuntimeHints.java b/webauthn/src/main/java/org/springframework/security/web/webauthn/aot/PublicKeyCredentialUserEntityRuntimeHints.java
index c1e6e94209..b57c182ee7 100644
--- a/webauthn/src/main/java/org/springframework/security/web/webauthn/aot/PublicKeyCredentialUserEntityRuntimeHints.java
+++ b/webauthn/src/main/java/org/springframework/security/web/webauthn/aot/PublicKeyCredentialUserEntityRuntimeHints.java
@@ -16,6 +16,8 @@
package org.springframework.security.web.webauthn.aot;
+import org.jspecify.annotations.Nullable;
+
import org.springframework.aot.hint.RuntimeHints;
import org.springframework.aot.hint.RuntimeHintsRegistrar;
import org.springframework.jdbc.core.JdbcOperations;
@@ -33,7 +35,7 @@ import org.springframework.security.web.webauthn.management.PublicKeyCredentialU
class PublicKeyCredentialUserEntityRuntimeHints implements RuntimeHintsRegistrar {
@Override
- public void registerHints(RuntimeHints hints, ClassLoader classLoader) {
+ public void registerHints(RuntimeHints hints, @Nullable ClassLoader classLoader) {
hints.resources().registerPattern("org/springframework/security/user-entities-schema.sql");
}
diff --git a/webauthn/src/main/java/org/springframework/security/web/webauthn/aot/UserCredentialRuntimeHints.java b/webauthn/src/main/java/org/springframework/security/web/webauthn/aot/UserCredentialRuntimeHints.java
index 50153db64a..9d503a80f9 100644
--- a/webauthn/src/main/java/org/springframework/security/web/webauthn/aot/UserCredentialRuntimeHints.java
+++ b/webauthn/src/main/java/org/springframework/security/web/webauthn/aot/UserCredentialRuntimeHints.java
@@ -16,6 +16,8 @@
package org.springframework.security.web.webauthn.aot;
+import org.jspecify.annotations.Nullable;
+
import org.springframework.aot.hint.RuntimeHints;
import org.springframework.aot.hint.RuntimeHintsRegistrar;
import org.springframework.jdbc.core.JdbcOperations;
@@ -33,7 +35,7 @@ import org.springframework.security.web.webauthn.management.UserCredentialReposi
class UserCredentialRuntimeHints implements RuntimeHintsRegistrar {
@Override
- public void registerHints(RuntimeHints hints, ClassLoader classLoader) {
+ public void registerHints(RuntimeHints hints, @Nullable ClassLoader classLoader) {
hints.resources()
.registerPattern("org/springframework/security/user-credentials-schema.sql")
.registerPattern("org/springframework/security/user-credentials-schema-postgres.sql");
diff --git a/webauthn/src/main/java/org/springframework/security/web/webauthn/aot/package-info.java b/webauthn/src/main/java/org/springframework/security/web/webauthn/aot/package-info.java
new file mode 100644
index 0000000000..66fe269f01
--- /dev/null
+++ b/webauthn/src/main/java/org/springframework/security/web/webauthn/aot/package-info.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2004-present the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * WebAuthn AOT support.
+ */
+@NullMarked
+package org.springframework.security.web.webauthn.aot;
+
+import org.jspecify.annotations.NullMarked;
diff --git a/webauthn/src/main/java/org/springframework/security/web/webauthn/api/AuthenticatorAssertionResponse.java b/webauthn/src/main/java/org/springframework/security/web/webauthn/api/AuthenticatorAssertionResponse.java
index d9af216306..e4b46e0ff3 100644
--- a/webauthn/src/main/java/org/springframework/security/web/webauthn/api/AuthenticatorAssertionResponse.java
+++ b/webauthn/src/main/java/org/springframework/security/web/webauthn/api/AuthenticatorAssertionResponse.java
@@ -18,6 +18,10 @@ package org.springframework.security.web.webauthn.api;
import java.io.Serial;
+import org.jspecify.annotations.Nullable;
+
+import org.springframework.util.Assert;
+
/**
* The AuthenticatorAssertionResponse
@@ -47,12 +51,12 @@ public final class AuthenticatorAssertionResponse extends AuthenticatorResponse
private final Bytes signature;
- private final Bytes userHandle;
+ private final @Nullable Bytes userHandle;
- private final Bytes attestationObject;
+ private final @Nullable Bytes attestationObject;
private AuthenticatorAssertionResponse(Bytes clientDataJSON, Bytes authenticatorData, Bytes signature,
- Bytes userHandle, Bytes attestationObject) {
+ @Nullable Bytes userHandle, @Nullable Bytes attestationObject) {
super(clientDataJSON);
this.authenticatorData = authenticatorData;
this.signature = signature;
@@ -101,7 +105,7 @@ public final class AuthenticatorAssertionResponse extends AuthenticatorResponse
* ceremony is empty, and MAY return one otherwise.
* @return the user handle
*/
- public Bytes getUserHandle() {
+ public @Nullable Bytes getUserHandle() {
return this.userHandle;
}
@@ -113,7 +117,7 @@ public final class AuthenticatorAssertionResponse extends AuthenticatorResponse
* object, if the authenticator supports attestation in assertions.
* @return the {@code attestationObject}
*/
- public Bytes getAttestationObject() {
+ public @Nullable Bytes getAttestationObject() {
return this.attestationObject;
}
@@ -133,15 +137,15 @@ public final class AuthenticatorAssertionResponse extends AuthenticatorResponse
*/
public static final class AuthenticatorAssertionResponseBuilder {
- private Bytes authenticatorData;
+ private @Nullable Bytes authenticatorData;
- private Bytes signature;
+ private @Nullable Bytes signature;
- private Bytes userHandle;
+ private @Nullable Bytes userHandle;
- private Bytes attestationObject;
+ private @Nullable Bytes attestationObject;
- private Bytes clientDataJSON;
+ private @Nullable Bytes clientDataJSON;
private AuthenticatorAssertionResponseBuilder() {
}
@@ -201,6 +205,9 @@ public final class AuthenticatorAssertionResponse extends AuthenticatorResponse
* @return the {@link AuthenticatorAssertionResponse}
*/
public AuthenticatorAssertionResponse build() {
+ Assert.notNull(this.clientDataJSON, "clientDataJSON cannot be null");
+ Assert.notNull(this.authenticatorData, "authenticatorData cannot be null");
+ Assert.notNull(this.signature, "signature cannot be null");
return new AuthenticatorAssertionResponse(this.clientDataJSON, this.authenticatorData, this.signature,
this.userHandle, this.attestationObject);
}
diff --git a/webauthn/src/main/java/org/springframework/security/web/webauthn/api/AuthenticatorAttestationResponse.java b/webauthn/src/main/java/org/springframework/security/web/webauthn/api/AuthenticatorAttestationResponse.java
index 75123cb88f..a711f411b8 100644
--- a/webauthn/src/main/java/org/springframework/security/web/webauthn/api/AuthenticatorAttestationResponse.java
+++ b/webauthn/src/main/java/org/springframework/security/web/webauthn/api/AuthenticatorAttestationResponse.java
@@ -19,6 +19,8 @@ package org.springframework.security.web.webauthn.api;
import java.util.Arrays;
import java.util.List;
+import org.jspecify.annotations.Nullable;
+
/**
* AuthenticatorAttestationResponse
@@ -36,10 +38,10 @@ public final class AuthenticatorAttestationResponse extends AuthenticatorRespons
private final Bytes attestationObject;
- private final List transports;
+ private final @Nullable List transports;
private AuthenticatorAttestationResponse(Bytes clientDataJSON, Bytes attestationObject,
- List transports) {
+ @Nullable List transports) {
super(clientDataJSON);
this.attestationObject = attestationObject;
this.transports = transports;
@@ -63,7 +65,7 @@ public final class AuthenticatorAttestationResponse extends AuthenticatorRespons
* "https://www.w3.org/TR/webauthn-3/#dom-authenticatorattestationresponse-transports-slot">transports
* @return the transports
*/
- public List getTransports() {
+ public @Nullable List getTransports() {
return this.transports;
}
@@ -83,10 +85,12 @@ public final class AuthenticatorAttestationResponse extends AuthenticatorRespons
*/
public static final class AuthenticatorAttestationResponseBuilder {
+ @SuppressWarnings("NullAway.Init")
private Bytes attestationObject;
- private List transports;
+ private @Nullable List transports;
+ @SuppressWarnings("NullAway.Init")
private Bytes clientDataJSON;
private AuthenticatorAttestationResponseBuilder() {
diff --git a/webauthn/src/main/java/org/springframework/security/web/webauthn/api/AuthenticatorSelectionCriteria.java b/webauthn/src/main/java/org/springframework/security/web/webauthn/api/AuthenticatorSelectionCriteria.java
index a1ec71c51d..d36ee1f6a6 100644
--- a/webauthn/src/main/java/org/springframework/security/web/webauthn/api/AuthenticatorSelectionCriteria.java
+++ b/webauthn/src/main/java/org/springframework/security/web/webauthn/api/AuthenticatorSelectionCriteria.java
@@ -16,6 +16,8 @@
package org.springframework.security.web.webauthn.api;
+import org.jspecify.annotations.Nullable;
+
/**
* AuthenticatorAttachment
@@ -33,11 +35,11 @@ package org.springframework.security.web.webauthn.api;
*/
public final class AuthenticatorSelectionCriteria {
- private final AuthenticatorAttachment authenticatorAttachment;
+ private final @Nullable AuthenticatorAttachment authenticatorAttachment;
- private final ResidentKeyRequirement residentKey;
+ private final @Nullable ResidentKeyRequirement residentKey;
- private final UserVerificationRequirement userVerification;
+ private final @Nullable UserVerificationRequirement userVerification;
// NOTE: There is no requireResidentKey property because it is only for backward
// compatibility with WebAuthn Level 1
@@ -48,8 +50,8 @@ public final class AuthenticatorSelectionCriteria {
* @param residentKey the resident key requirement
* @param userVerification the user verification
*/
- private AuthenticatorSelectionCriteria(AuthenticatorAttachment authenticatorAttachment,
- ResidentKeyRequirement residentKey, UserVerificationRequirement userVerification) {
+ private AuthenticatorSelectionCriteria(@Nullable AuthenticatorAttachment authenticatorAttachment,
+ @Nullable ResidentKeyRequirement residentKey, @Nullable UserVerificationRequirement userVerification) {
this.authenticatorAttachment = authenticatorAttachment;
this.residentKey = residentKey;
this.userVerification = userVerification;
@@ -67,7 +69,7 @@ public final class AuthenticatorSelectionCriteria {
* Authenticator Attachment Modality).
* @return the authenticator attachment
*/
- public AuthenticatorAttachment getAuthenticatorAttachment() {
+ public @Nullable AuthenticatorAttachment getAuthenticatorAttachment() {
return this.authenticatorAttachment;
}
@@ -81,7 +83,7 @@ public final class AuthenticatorSelectionCriteria {
* discoverable credential.
* @return the resident key requirement
*/
- public ResidentKeyRequirement getResidentKey() {
+ public @Nullable ResidentKeyRequirement getResidentKey() {
return this.residentKey;
}
@@ -96,7 +98,7 @@ public final class AuthenticatorSelectionCriteria {
* operation.
* @return the user verification requirement
*/
- public UserVerificationRequirement getUserVerification() {
+ public @Nullable UserVerificationRequirement getUserVerification() {
return this.userVerification;
}
@@ -116,11 +118,11 @@ public final class AuthenticatorSelectionCriteria {
*/
public static final class AuthenticatorSelectionCriteriaBuilder {
- private AuthenticatorAttachment authenticatorAttachment;
+ private @Nullable AuthenticatorAttachment authenticatorAttachment;
- private ResidentKeyRequirement residentKey;
+ private @Nullable ResidentKeyRequirement residentKey;
- private UserVerificationRequirement userVerification;
+ private @Nullable UserVerificationRequirement userVerification;
private AuthenticatorSelectionCriteriaBuilder() {
}
diff --git a/webauthn/src/main/java/org/springframework/security/web/webauthn/api/Bytes.java b/webauthn/src/main/java/org/springframework/security/web/webauthn/api/Bytes.java
index 519609e490..f3aa1a98e1 100644
--- a/webauthn/src/main/java/org/springframework/security/web/webauthn/api/Bytes.java
+++ b/webauthn/src/main/java/org/springframework/security/web/webauthn/api/Bytes.java
@@ -22,6 +22,8 @@ import java.security.SecureRandom;
import java.util.Arrays;
import java.util.Base64;
+import org.jspecify.annotations.Nullable;
+
import org.springframework.util.Assert;
/**
@@ -100,7 +102,7 @@ public final class Bytes implements Serializable {
* @param base64UrlString the base64 url string
* @return the {@link Bytes}
*/
- public static Bytes fromBase64(String base64UrlString) {
+ public static Bytes fromBase64(@Nullable String base64UrlString) {
byte[] bytes = DECODER.decode(base64UrlString);
return new Bytes(bytes);
}
diff --git a/webauthn/src/main/java/org/springframework/security/web/webauthn/api/CredentialRecord.java b/webauthn/src/main/java/org/springframework/security/web/webauthn/api/CredentialRecord.java
index cc8d3b8a88..21c769b17c 100644
--- a/webauthn/src/main/java/org/springframework/security/web/webauthn/api/CredentialRecord.java
+++ b/webauthn/src/main/java/org/springframework/security/web/webauthn/api/CredentialRecord.java
@@ -19,6 +19,8 @@ package org.springframework.security.web.webauthn.api;
import java.time.Instant;
import java.util.Set;
+import org.jspecify.annotations.Nullable;
+
/**
* Represents a Credential
* Record that is stored by the Relying Party
@@ -35,7 +37,7 @@ public interface CredentialRecord {
* "https://www.w3.org/TR/webauthn-3/#abstract-opdef-credential-record-type">credential.type
* @return
*/
- PublicKeyCredentialType getCredentialType();
+ @Nullable PublicKeyCredentialType getCredentialType();
/**
* The transports, boolean backupEligible, boolean backupState,
- Bytes attestationObject, Bytes attestationClientDataJSON, Instant created, Instant lastUsed, String label) {
+ @Nullable Bytes attestationObject, @Nullable Bytes attestationClientDataJSON, Instant created, Instant lastUsed, String label) {
this.credentialType = credentialType;
this.credentialId = credentialId;
this.userEntityUserId = userEntityUserId;
@@ -76,7 +78,7 @@ public final class ImmutableCredentialRecord implements CredentialRecord {
}
@Override
- public PublicKeyCredentialType getCredentialType() {
+ public @Nullable PublicKeyCredentialType getCredentialType() {
return this.credentialType;
}
@@ -121,12 +123,12 @@ public final class ImmutableCredentialRecord implements CredentialRecord {
}
@Override
- public Bytes getAttestationObject() {
+ public @Nullable Bytes getAttestationObject() {
return this.attestationObject;
}
@Override
- public Bytes getAttestationClientDataJSON() {
+ public @Nullable Bytes getAttestationClientDataJSON() {
return this.attestationClientDataJSON;
}
@@ -155,32 +157,37 @@ public final class ImmutableCredentialRecord implements CredentialRecord {
public static final class ImmutableCredentialRecordBuilder {
- private PublicKeyCredentialType credentialType;
+ private @Nullable PublicKeyCredentialType credentialType;
+ @SuppressWarnings("NullAway.Init")
private Bytes credentialId;
+ @SuppressWarnings("NullAway.Init")
private Bytes userEntityUserId;
+ @SuppressWarnings("NullAway.Init")
private PublicKeyCose publicKey;
private long signatureCount;
private boolean uvInitialized;
+ @SuppressWarnings("NullAway.Init")
private Set transports;
private boolean backupEligible;
private boolean backupState;
- private Bytes attestationObject;
+ private @Nullable Bytes attestationObject;
- private Bytes attestationClientDataJSON;
+ private @Nullable Bytes attestationClientDataJSON;
private Instant created = Instant.now();
private Instant lastUsed = this.created;
+ @SuppressWarnings("NullAway.Init")
private String label;
private ImmutableCredentialRecordBuilder() {
@@ -248,12 +255,12 @@ public final class ImmutableCredentialRecord implements CredentialRecord {
return this;
}
- public ImmutableCredentialRecordBuilder attestationObject(Bytes attestationObject) {
+ public ImmutableCredentialRecordBuilder attestationObject(@Nullable Bytes attestationObject) {
this.attestationObject = attestationObject;
return this;
}
- public ImmutableCredentialRecordBuilder attestationClientDataJSON(Bytes attestationClientDataJSON) {
+ public ImmutableCredentialRecordBuilder attestationClientDataJSON(@Nullable Bytes attestationClientDataJSON) {
this.attestationClientDataJSON = attestationClientDataJSON;
return this;
}
diff --git a/webauthn/src/main/java/org/springframework/security/web/webauthn/api/ImmutablePublicKeyCose.java b/webauthn/src/main/java/org/springframework/security/web/webauthn/api/ImmutablePublicKeyCose.java
index 364da3f61c..97f29acdc7 100644
--- a/webauthn/src/main/java/org/springframework/security/web/webauthn/api/ImmutablePublicKeyCose.java
+++ b/webauthn/src/main/java/org/springframework/security/web/webauthn/api/ImmutablePublicKeyCose.java
@@ -19,6 +19,10 @@ package org.springframework.security.web.webauthn.api;
import java.util.Arrays;
import java.util.Base64;
+import org.jspecify.annotations.Nullable;
+
+import org.springframework.util.Assert;
+
/**
* An immutable {@link PublicKeyCose}
*
@@ -33,7 +37,8 @@ public class ImmutablePublicKeyCose implements PublicKeyCose {
* Creates a new instance.
* @param bytes the raw bytes of the public key.
*/
- public ImmutablePublicKeyCose(byte[] bytes) {
+ public ImmutablePublicKeyCose(byte @Nullable [] bytes) {
+ Assert.notNull(bytes, "bytes cannot be null");
this.bytes = Arrays.copyOf(bytes, bytes.length);
}
diff --git a/webauthn/src/main/java/org/springframework/security/web/webauthn/api/ImmutablePublicKeyCredentialUserEntity.java b/webauthn/src/main/java/org/springframework/security/web/webauthn/api/ImmutablePublicKeyCredentialUserEntity.java
index d4722cddd8..2ca0fd3a7d 100644
--- a/webauthn/src/main/java/org/springframework/security/web/webauthn/api/ImmutablePublicKeyCredentialUserEntity.java
+++ b/webauthn/src/main/java/org/springframework/security/web/webauthn/api/ImmutablePublicKeyCredentialUserEntity.java
@@ -18,6 +18,8 @@ package org.springframework.security.web.webauthn.api;
import java.io.Serial;
+import org.jspecify.annotations.Nullable;
+
/**
* PublicKeyCredentialUserEntity
@@ -102,9 +104,9 @@ public final class ImmutablePublicKeyCredentialUserEntity implements PublicKeyCr
* fits within 64 bytes. See 6.4.1 String Truncation about truncation and other
* considerations.
*/
- private final String displayName;
+ private final @Nullable String displayName;
- private ImmutablePublicKeyCredentialUserEntity(String name, Bytes id, String displayName) {
+ private ImmutablePublicKeyCredentialUserEntity(String name, Bytes id, @Nullable String displayName) {
this.name = name;
this.id = id;
this.displayName = displayName;
@@ -121,7 +123,7 @@ public final class ImmutablePublicKeyCredentialUserEntity implements PublicKeyCr
}
@Override
- public String getDisplayName() {
+ public @Nullable String getDisplayName() {
return this.displayName;
}
@@ -141,11 +143,13 @@ public final class ImmutablePublicKeyCredentialUserEntity implements PublicKeyCr
*/
public static final class PublicKeyCredentialUserEntityBuilder {
+ @SuppressWarnings("NullAway.Init")
private String name;
+ @SuppressWarnings("NullAway.Init")
private Bytes id;
- private String displayName;
+ private @Nullable String displayName;
private PublicKeyCredentialUserEntityBuilder() {
}
diff --git a/webauthn/src/main/java/org/springframework/security/web/webauthn/api/PublicKeyCredential.java b/webauthn/src/main/java/org/springframework/security/web/webauthn/api/PublicKeyCredential.java
index baa7249691..aa2c76230e 100644
--- a/webauthn/src/main/java/org/springframework/security/web/webauthn/api/PublicKeyCredential.java
+++ b/webauthn/src/main/java/org/springframework/security/web/webauthn/api/PublicKeyCredential.java
@@ -19,6 +19,10 @@ package org.springframework.security.web.webauthn.api;
import java.io.Serial;
import java.io.Serializable;
+import org.jspecify.annotations.Nullable;
+
+import org.springframework.util.Assert;
+
/**
* PublicKeyCredential
* contains the attributes that are returned to the caller when a new credential is
@@ -40,13 +44,13 @@ public final class PublicKeyCredential implemen
private final R response;
- private final AuthenticatorAttachment authenticatorAttachment;
+ private final @Nullable AuthenticatorAttachment authenticatorAttachment;
- private final AuthenticationExtensionsClientOutputs clientExtensionResults;
+ private final @Nullable AuthenticationExtensionsClientOutputs clientExtensionResults;
private PublicKeyCredential(String id, PublicKeyCredentialType type, Bytes rawId, R response,
- AuthenticatorAttachment authenticatorAttachment,
- AuthenticationExtensionsClientOutputs clientExtensionResults) {
+ @Nullable AuthenticatorAttachment authenticatorAttachment,
+ @Nullable AuthenticationExtensionsClientOutputs clientExtensionResults) {
this.id = id;
this.type = type;
this.rawId = rawId;
@@ -107,7 +111,7 @@ public final class PublicKeyCredential implemen
* navigator.credentials.get() methods successfully complete.
* @return the authenticator attachment
*/
- public AuthenticatorAttachment getAuthenticatorAttachment() {
+ public @Nullable AuthenticatorAttachment getAuthenticatorAttachment() {
return this.authenticatorAttachment;
}
@@ -117,7 +121,7 @@ public final class PublicKeyCredential implemen
* is a mapping of extension identifier to client extension output.
* @return the extension results
*/
- public AuthenticationExtensionsClientOutputs getClientExtensionResults() {
+ public @Nullable AuthenticationExtensionsClientOutputs getClientExtensionResults() {
return this.clientExtensionResults;
}
@@ -139,17 +143,20 @@ public final class PublicKeyCredential implemen
*/
public static final class PublicKeyCredentialBuilder {
+ @SuppressWarnings("NullAway.Init")
private String id;
- private PublicKeyCredentialType type;
+ private @Nullable PublicKeyCredentialType type;
+ @SuppressWarnings("NullAway.Init")
private Bytes rawId;
+ @SuppressWarnings("NullAway.Init")
private R response;
- private AuthenticatorAttachment authenticatorAttachment;
+ private @Nullable AuthenticatorAttachment authenticatorAttachment;
- private AuthenticationExtensionsClientOutputs clientExtensionResults;
+ private @Nullable AuthenticationExtensionsClientOutputs clientExtensionResults;
private PublicKeyCredentialBuilder() {
}
@@ -220,6 +227,10 @@ public final class PublicKeyCredential implemen
* @return a new {@link PublicKeyCredential}
*/
public PublicKeyCredential build() {
+ Assert.notNull(this.id, "id cannot be null");
+ Assert.notNull(this.type, "type cannot be null");
+ Assert.notNull(this.rawId, "rawId cannot be null");
+ Assert.notNull(this.response, "response cannot be null");
return new PublicKeyCredential(this.id, this.type, this.rawId, this.response, this.authenticatorAttachment,
this.clientExtensionResults);
}
diff --git a/webauthn/src/main/java/org/springframework/security/web/webauthn/api/PublicKeyCredentialCreationOptions.java b/webauthn/src/main/java/org/springframework/security/web/webauthn/api/PublicKeyCredentialCreationOptions.java
index d2ff095a76..5e5c0ccacb 100644
--- a/webauthn/src/main/java/org/springframework/security/web/webauthn/api/PublicKeyCredentialCreationOptions.java
+++ b/webauthn/src/main/java/org/springframework/security/web/webauthn/api/PublicKeyCredentialCreationOptions.java
@@ -22,6 +22,8 @@ import java.util.Arrays;
import java.util.List;
import java.util.function.Consumer;
+import org.jspecify.annotations.Nullable;
+
/**
* Represents the PublicKeyCredentialCreationOptions
@@ -42,21 +44,22 @@ public final class PublicKeyCredentialCreationOptions {
private final List pubKeyCredParams;
- private final Duration timeout;
+ private final @Nullable Duration timeout;
private final List excludeCredentials;
private final AuthenticatorSelectionCriteria authenticatorSelection;
- private final AttestationConveyancePreference attestation;
+ private final @Nullable AttestationConveyancePreference attestation;
- private final AuthenticationExtensionsClientInputs extensions;
+ private final @Nullable AuthenticationExtensionsClientInputs extensions;
private PublicKeyCredentialCreationOptions(PublicKeyCredentialRpEntity rp, PublicKeyCredentialUserEntity user,
- Bytes challenge, List pubKeyCredParams, Duration timeout,
+ Bytes challenge, List pubKeyCredParams, @Nullable Duration timeout,
List excludeCredentials,
- AuthenticatorSelectionCriteria authenticatorSelection, AttestationConveyancePreference attestation,
- AuthenticationExtensionsClientInputs extensions) {
+ AuthenticatorSelectionCriteria authenticatorSelection,
+ @Nullable AttestationConveyancePreference attestation,
+ @Nullable AuthenticationExtensionsClientInputs extensions) {
this.rp = rp;
this.user = user;
this.challenge = challenge;
@@ -117,7 +120,7 @@ public final class PublicKeyCredentialCreationOptions {
* wait for the call to complete.
* @return the timeout
*/
- public Duration getTimeout() {
+ public @Nullable Duration getTimeout() {
return this.timeout;
}
@@ -150,7 +153,7 @@ public final class PublicKeyCredentialCreationOptions {
* regarding attestation conveyance.
* @return the attestation preference
*/
- public AttestationConveyancePreference getAttestation() {
+ public @Nullable AttestationConveyancePreference getAttestation() {
return this.attestation;
}
@@ -161,7 +164,7 @@ public final class PublicKeyCredentialCreationOptions {
* extension inputs requesting additional processing by the client and authenticator.
* @return the extensions
*/
- public AuthenticationExtensionsClientInputs getExtensions() {
+ public @Nullable AuthenticationExtensionsClientInputs getExtensions() {
return this.extensions;
}
@@ -181,23 +184,27 @@ public final class PublicKeyCredentialCreationOptions {
*/
public static final class PublicKeyCredentialCreationOptionsBuilder {
+ @SuppressWarnings("NullAway.Init")
private PublicKeyCredentialRpEntity rp;
+ @SuppressWarnings("NullAway.Init")
private PublicKeyCredentialUserEntity user;
+ @SuppressWarnings("NullAway.Init")
private Bytes challenge;
private List pubKeyCredParams = new ArrayList<>();
- private Duration timeout;
+ private @Nullable Duration timeout;
private List excludeCredentials = new ArrayList<>();
+ @SuppressWarnings("NullAway.Init")
private AuthenticatorSelectionCriteria authenticatorSelection;
- private AttestationConveyancePreference attestation;
+ private @Nullable AttestationConveyancePreference attestation;
- private AuthenticationExtensionsClientInputs extensions;
+ private @Nullable AuthenticationExtensionsClientInputs extensions;
private PublicKeyCredentialCreationOptionsBuilder() {
}
diff --git a/webauthn/src/main/java/org/springframework/security/web/webauthn/api/PublicKeyCredentialDescriptor.java b/webauthn/src/main/java/org/springframework/security/web/webauthn/api/PublicKeyCredentialDescriptor.java
index 18153a6f3c..afa3802c73 100644
--- a/webauthn/src/main/java/org/springframework/security/web/webauthn/api/PublicKeyCredentialDescriptor.java
+++ b/webauthn/src/main/java/org/springframework/security/web/webauthn/api/PublicKeyCredentialDescriptor.java
@@ -20,6 +20,8 @@ import java.io.Serial;
import java.io.Serializable;
import java.util.Set;
+import org.jspecify.annotations.Nullable;
+
/**
* PublicKeyCredentialDescriptor
@@ -38,12 +40,12 @@ public final class PublicKeyCredentialDescriptor implements Serializable {
private final PublicKeyCredentialType type;
- private final Bytes id;
+ private final @Nullable Bytes id;
- private final Set transports;
+ private final @Nullable Set transports;
- private PublicKeyCredentialDescriptor(PublicKeyCredentialType type, Bytes id,
- Set transports) {
+ private PublicKeyCredentialDescriptor(PublicKeyCredentialType type, @Nullable Bytes id,
+ @Nullable Set transports) {
this.type = type;
this.id = id;
this.transports = transports;
@@ -66,7 +68,7 @@ public final class PublicKeyCredentialDescriptor implements Serializable {
* referring to.
* @return the id
*/
- public Bytes getId() {
+ public @Nullable Bytes getId() {
return this.id;
}
@@ -78,7 +80,7 @@ public final class PublicKeyCredentialDescriptor implements Serializable {
* is referring to.
* @return the transports
*/
- public Set getTransports() {
+ public @Nullable Set getTransports() {
return this.transports;
}
@@ -100,9 +102,9 @@ public final class PublicKeyCredentialDescriptor implements Serializable {
private PublicKeyCredentialType type = PublicKeyCredentialType.PUBLIC_KEY;
- private Bytes id;
+ private @Nullable Bytes id;
- private Set transports;
+ private @Nullable Set transports;
private PublicKeyCredentialDescriptorBuilder() {
}
diff --git a/webauthn/src/main/java/org/springframework/security/web/webauthn/api/PublicKeyCredentialRequestOptions.java b/webauthn/src/main/java/org/springframework/security/web/webauthn/api/PublicKeyCredentialRequestOptions.java
index 4bb861dd18..efb32c1e24 100644
--- a/webauthn/src/main/java/org/springframework/security/web/webauthn/api/PublicKeyCredentialRequestOptions.java
+++ b/webauthn/src/main/java/org/springframework/security/web/webauthn/api/PublicKeyCredentialRequestOptions.java
@@ -24,6 +24,8 @@ import java.util.Collections;
import java.util.List;
import java.util.function.Consumer;
+import org.jspecify.annotations.Nullable;
+
import org.springframework.util.Assert;
/**
@@ -43,17 +45,17 @@ public final class PublicKeyCredentialRequestOptions implements Serializable {
private final Duration timeout;
- private final String rpId;
+ private final @Nullable String rpId;
private final List allowCredentials;
- private final UserVerificationRequirement userVerification;
+ private final @Nullable UserVerificationRequirement userVerification;
private final AuthenticationExtensionsClientInputs extensions;
- private PublicKeyCredentialRequestOptions(Bytes challenge, Duration timeout, String rpId,
- List allowCredentials, UserVerificationRequirement userVerification,
- AuthenticationExtensionsClientInputs extensions) {
+ private PublicKeyCredentialRequestOptions(Bytes challenge, Duration timeout, @Nullable String rpId,
+ List allowCredentials,
+ @Nullable UserVerificationRequirement userVerification, AuthenticationExtensionsClientInputs extensions) {
Assert.notNull(challenge, "challenge cannot be null");
Assert.hasText(rpId, "rpId cannot be empty");
this.challenge = challenge;
@@ -93,7 +95,7 @@ public final class PublicKeyCredentialRequestOptions implements Serializable {
* MUST verify that the Relying Party's origin matches the scope of this RP ID.
* @return the relying party id
*/
- public String getRpId() {
+ public @Nullable String getRpId() {
return this.rpId;
}
@@ -115,7 +117,7 @@ public final class PublicKeyCredentialRequestOptions implements Serializable {
* user verification for the get() operation.
* @return the user verification
*/
- public UserVerificationRequirement getUserVerification() {
+ public @Nullable UserVerificationRequirement getUserVerification() {
return this.userVerification;
}
@@ -146,15 +148,15 @@ public final class PublicKeyCredentialRequestOptions implements Serializable {
*/
public static final class PublicKeyCredentialRequestOptionsBuilder {
- private Bytes challenge;
+ private @Nullable Bytes challenge;
private Duration timeout = Duration.ofMinutes(5);
- private String rpId;
+ private @Nullable String rpId;
private List allowCredentials = Collections.emptyList();
- private UserVerificationRequirement userVerification;
+ private @Nullable UserVerificationRequirement userVerification;
private AuthenticationExtensionsClientInputs extensions = new ImmutableAuthenticationExtensionsClientInputs(
new ArrayList<>());
diff --git a/webauthn/src/main/java/org/springframework/security/web/webauthn/api/PublicKeyCredentialRpEntity.java b/webauthn/src/main/java/org/springframework/security/web/webauthn/api/PublicKeyCredentialRpEntity.java
index 6d22ec45ce..4caf4f6eee 100644
--- a/webauthn/src/main/java/org/springframework/security/web/webauthn/api/PublicKeyCredentialRpEntity.java
+++ b/webauthn/src/main/java/org/springframework/security/web/webauthn/api/PublicKeyCredentialRpEntity.java
@@ -16,6 +16,10 @@
package org.springframework.security.web.webauthn.api;
+import org.jspecify.annotations.Nullable;
+
+import org.springframework.util.Assert;
+
/**
* The PublicKeyCredentialRpEntity
@@ -75,9 +79,9 @@ public final class PublicKeyCredentialRpEntity {
*/
public static final class PublicKeyCredentialRpEntityBuilder {
- private String name;
+ private @Nullable String name;
- private String id;
+ private @Nullable String id;
private PublicKeyCredentialRpEntityBuilder() {
}
@@ -107,6 +111,8 @@ public final class PublicKeyCredentialRpEntity {
* @return a new {@link PublicKeyCredentialRpEntity}.
*/
public PublicKeyCredentialRpEntity build() {
+ Assert.notNull(this.name, "name cannot be null");
+ Assert.notNull(this.id, "id cannot be null");
return new PublicKeyCredentialRpEntity(this.name, this.id);
}
diff --git a/webauthn/src/main/java/org/springframework/security/web/webauthn/api/PublicKeyCredentialUserEntity.java b/webauthn/src/main/java/org/springframework/security/web/webauthn/api/PublicKeyCredentialUserEntity.java
index bce1e8d649..269cc750e5 100644
--- a/webauthn/src/main/java/org/springframework/security/web/webauthn/api/PublicKeyCredentialUserEntity.java
+++ b/webauthn/src/main/java/org/springframework/security/web/webauthn/api/PublicKeyCredentialUserEntity.java
@@ -18,6 +18,8 @@ package org.springframework.security.web.webauthn.api;
import java.io.Serializable;
+import org.jspecify.annotations.Nullable;
+
import org.springframework.security.web.webauthn.management.RelyingPartyAuthenticationRequest;
import org.springframework.security.web.webauthn.management.WebAuthnRelyingPartyOperations;
@@ -57,6 +59,6 @@ public interface PublicKeyCredentialUserEntity extends Serializable {
* is a human-palatable name for the user account, intended only for display.
* @return the display name
*/
- String getDisplayName();
+ @Nullable String getDisplayName();
}
diff --git a/webauthn/src/main/java/org/springframework/security/web/webauthn/api/package-info.java b/webauthn/src/main/java/org/springframework/security/web/webauthn/api/package-info.java
new file mode 100644
index 0000000000..9679301c55
--- /dev/null
+++ b/webauthn/src/main/java/org/springframework/security/web/webauthn/api/package-info.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2004-present the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * WebAuthn APIs.
+ */
+@NullMarked
+package org.springframework.security.web.webauthn.api;
+
+import org.jspecify.annotations.NullMarked;
diff --git a/webauthn/src/main/java/org/springframework/security/web/webauthn/authentication/HttpSessionPublicKeyCredentialRequestOptionsRepository.java b/webauthn/src/main/java/org/springframework/security/web/webauthn/authentication/HttpSessionPublicKeyCredentialRequestOptionsRepository.java
index c67a6b9387..0d19f14811 100644
--- a/webauthn/src/main/java/org/springframework/security/web/webauthn/authentication/HttpSessionPublicKeyCredentialRequestOptionsRepository.java
+++ b/webauthn/src/main/java/org/springframework/security/web/webauthn/authentication/HttpSessionPublicKeyCredentialRequestOptionsRepository.java
@@ -19,6 +19,7 @@ package org.springframework.security.web.webauthn.authentication;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession;
+import org.jspecify.annotations.Nullable;
import org.springframework.security.web.webauthn.api.PublicKeyCredentialRequestOptions;
import org.springframework.util.Assert;
@@ -41,13 +42,13 @@ public class HttpSessionPublicKeyCredentialRequestOptionsRepository
@Override
public void save(HttpServletRequest request, HttpServletResponse response,
- PublicKeyCredentialRequestOptions options) {
+ @Nullable PublicKeyCredentialRequestOptions options) {
HttpSession session = request.getSession();
session.setAttribute(this.attrName, options);
}
@Override
- public PublicKeyCredentialRequestOptions load(HttpServletRequest request) {
+ public @Nullable PublicKeyCredentialRequestOptions load(HttpServletRequest request) {
HttpSession session = request.getSession(false);
if (session == null) {
return null;
diff --git a/webauthn/src/main/java/org/springframework/security/web/webauthn/authentication/PublicKeyCredentialRequestOptionsRepository.java b/webauthn/src/main/java/org/springframework/security/web/webauthn/authentication/PublicKeyCredentialRequestOptionsRepository.java
index 9df9d507ca..cd91b69fd6 100644
--- a/webauthn/src/main/java/org/springframework/security/web/webauthn/authentication/PublicKeyCredentialRequestOptionsRepository.java
+++ b/webauthn/src/main/java/org/springframework/security/web/webauthn/authentication/PublicKeyCredentialRequestOptionsRepository.java
@@ -18,6 +18,7 @@ package org.springframework.security.web.webauthn.authentication;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
+import org.jspecify.annotations.Nullable;
import org.springframework.security.web.webauthn.api.PublicKeyCredentialRequestOptions;
@@ -38,7 +39,7 @@ public interface PublicKeyCredentialRequestOptionsRepository {
* @param options the {@link PublicKeyCredentialRequestOptions} to save or null if an
* existing {@link PublicKeyCredentialRequestOptions} should be removed.
*/
- void save(HttpServletRequest request, HttpServletResponse response, PublicKeyCredentialRequestOptions options);
+ void save(HttpServletRequest request, HttpServletResponse response, @Nullable PublicKeyCredentialRequestOptions options);
/**
* Gets a saved {@link PublicKeyCredentialRequestOptions} if it exists, otherwise
@@ -47,6 +48,6 @@ public interface PublicKeyCredentialRequestOptionsRepository {
* @return the {@link PublicKeyCredentialRequestOptions} that was saved, otherwise
* null.
*/
- PublicKeyCredentialRequestOptions load(HttpServletRequest request);
+ @Nullable PublicKeyCredentialRequestOptions load(HttpServletRequest request);
}
diff --git a/webauthn/src/main/java/org/springframework/security/web/webauthn/authentication/WebAuthnAuthentication.java b/webauthn/src/main/java/org/springframework/security/web/webauthn/authentication/WebAuthnAuthentication.java
index c44e648402..42007f9ece 100644
--- a/webauthn/src/main/java/org/springframework/security/web/webauthn/authentication/WebAuthnAuthentication.java
+++ b/webauthn/src/main/java/org/springframework/security/web/webauthn/authentication/WebAuthnAuthentication.java
@@ -19,6 +19,8 @@ package org.springframework.security.web.webauthn.authentication;
import java.io.Serial;
import java.util.Collection;
+import org.jspecify.annotations.Nullable;
+
import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.web.webauthn.api.PublicKeyCredentialUserEntity;
@@ -53,7 +55,7 @@ public class WebAuthnAuthentication extends AbstractAuthenticationToken {
}
@Override
- public Object getCredentials() {
+ public @Nullable Object getCredentials() {
return null;
}
diff --git a/webauthn/src/main/java/org/springframework/security/web/webauthn/authentication/WebAuthnAuthenticationRequestToken.java b/webauthn/src/main/java/org/springframework/security/web/webauthn/authentication/WebAuthnAuthenticationRequestToken.java
index 281f44fc41..e10aba38f8 100644
--- a/webauthn/src/main/java/org/springframework/security/web/webauthn/authentication/WebAuthnAuthenticationRequestToken.java
+++ b/webauthn/src/main/java/org/springframework/security/web/webauthn/authentication/WebAuthnAuthenticationRequestToken.java
@@ -18,6 +18,8 @@ package org.springframework.security.web.webauthn.authentication;
import java.io.Serial;
+import org.jspecify.annotations.Nullable;
+
import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.web.webauthn.management.RelyingPartyAuthenticationRequest;
@@ -68,7 +70,7 @@ public class WebAuthnAuthenticationRequestToken extends AbstractAuthenticationTo
}
@Override
- public Object getPrincipal() {
+ public @Nullable Object getPrincipal() {
return this.webAuthnRequest.getPublicKey().getResponse().getUserHandle();
}
diff --git a/webauthn/src/main/java/org/springframework/security/web/webauthn/authentication/package-info.java b/webauthn/src/main/java/org/springframework/security/web/webauthn/authentication/package-info.java
new file mode 100644
index 0000000000..ce8ae90ac3
--- /dev/null
+++ b/webauthn/src/main/java/org/springframework/security/web/webauthn/authentication/package-info.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2004-present the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * WebAuthn Authentication support.
+ */
+@NullMarked
+package org.springframework.security.web.webauthn.authentication;
+
+import org.jspecify.annotations.NullMarked;
diff --git a/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/AuthenticatorAttachmentDeserializer.java b/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/AuthenticatorAttachmentDeserializer.java
index a66c798e17..895b594af6 100644
--- a/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/AuthenticatorAttachmentDeserializer.java
+++ b/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/AuthenticatorAttachmentDeserializer.java
@@ -22,6 +22,7 @@ import com.fasterxml.jackson.core.JacksonException;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
+import org.jspecify.annotations.Nullable;
import org.springframework.security.web.webauthn.api.AuthenticatorAttachment;
@@ -39,7 +40,7 @@ class AuthenticatorAttachmentDeserializer extends StdDeserializer result = this.jdbcOperations.query(FIND_USER_BY_ID_SQL,
this.userEntityRowMapper, id.toBase64UrlString());
@@ -113,7 +115,7 @@ public final class JdbcPublicKeyCredentialUserEntityRepository implements Public
}
@Override
- public PublicKeyCredentialUserEntity findByUsername(String username) {
+ public @Nullable PublicKeyCredentialUserEntity findByUsername(String username) {
Assert.hasText(username, "name cannot be null or empty");
List result = this.jdbcOperations.query(FIND_USER_BY_NAME_SQL,
this.userEntityRowMapper, username);
diff --git a/webauthn/src/main/java/org/springframework/security/web/webauthn/management/JdbcUserCredentialRepository.java b/webauthn/src/main/java/org/springframework/security/web/webauthn/management/JdbcUserCredentialRepository.java
index 12195138f6..ee850a429c 100644
--- a/webauthn/src/main/java/org/springframework/security/web/webauthn/management/JdbcUserCredentialRepository.java
+++ b/webauthn/src/main/java/org/springframework/security/web/webauthn/management/JdbcUserCredentialRepository.java
@@ -28,6 +28,8 @@ import java.util.List;
import java.util.Set;
import java.util.function.Function;
+import org.jspecify.annotations.Nullable;
+
import org.springframework.jdbc.core.ArgumentPreparedStatementSetter;
import org.springframework.jdbc.core.JdbcOperations;
import org.springframework.jdbc.core.PreparedStatementSetter;
@@ -171,7 +173,7 @@ public final class JdbcUserCredentialRepository implements UserCredentialReposit
}
@Override
- public CredentialRecord findByCredentialId(Bytes credentialId) {
+ public @Nullable CredentialRecord findByCredentialId(Bytes credentialId) {
Assert.notNull(credentialId, "credentialId cannot be null");
List result = this.jdbcOperations.query(FIND_CREDENTIAL_RECORD_BY_ID_SQL,
this.credentialRecordRowMapper, credentialId.toBase64UrlString());
@@ -238,7 +240,7 @@ public final class JdbcUserCredentialRepository implements UserCredentialReposit
return parameters;
}
- private Timestamp fromInstant(Instant instant) {
+ private @Nullable Timestamp fromInstant(Instant instant) {
if (instant == null) {
return null;
}
@@ -249,13 +251,13 @@ public final class JdbcUserCredentialRepository implements UserCredentialReposit
private interface SetBytes {
- void setBytes(PreparedStatement ps, int index, byte[] bytes) throws SQLException;
+ void setBytes(PreparedStatement ps, int index, byte @Nullable [] bytes) throws SQLException;
}
private interface GetBytes {
- byte[] getBytes(ResultSet rs, String columnName) throws SQLException;
+ byte @Nullable [] getBytes(ResultSet rs, String columnName) throws SQLException;
}
@@ -269,7 +271,8 @@ public final class JdbcUserCredentialRepository implements UserCredentialReposit
}
@Override
- protected void doSetValue(PreparedStatement ps, int parameterPosition, Object argValue) throws SQLException {
+ protected void doSetValue(PreparedStatement ps, int parameterPosition, @Nullable Object argValue)
+ throws SQLException {
if (argValue instanceof SqlParameterValue paramValue) {
if (paramValue.getSqlType() == Types.BLOB) {
if (paramValue.getValue() != null) {
@@ -327,6 +330,8 @@ public final class JdbcUserCredentialRepository implements UserCredentialReposit
for (String transport : transports) {
authenticatorTransports.add(AuthenticatorTransport.valueOf(transport));
}
+ Assert.notNull(lastUsed, "last_used cannot be null");
+ Assert.notNull(created, "created cannot be null");
return ImmutableCredentialRecord.builder()
.credentialId(credentialId)
.userEntityUserId(userEntityUserId)
@@ -345,7 +350,7 @@ public final class JdbcUserCredentialRepository implements UserCredentialReposit
.build();
}
- private Instant fromTimestamp(Timestamp timestamp) {
+ private @Nullable Instant fromTimestamp(Timestamp timestamp) {
if (timestamp == null) {
return null;
}
diff --git a/webauthn/src/main/java/org/springframework/security/web/webauthn/management/MapPublicKeyCredentialUserEntityRepository.java b/webauthn/src/main/java/org/springframework/security/web/webauthn/management/MapPublicKeyCredentialUserEntityRepository.java
index 865fd4ebd6..022eca16c7 100644
--- a/webauthn/src/main/java/org/springframework/security/web/webauthn/management/MapPublicKeyCredentialUserEntityRepository.java
+++ b/webauthn/src/main/java/org/springframework/security/web/webauthn/management/MapPublicKeyCredentialUserEntityRepository.java
@@ -19,6 +19,8 @@ package org.springframework.security.web.webauthn.management;
import java.util.HashMap;
import java.util.Map;
+import org.jspecify.annotations.Nullable;
+
import org.springframework.security.web.webauthn.api.Bytes;
import org.springframework.security.web.webauthn.api.PublicKeyCredentialUserEntity;
import org.springframework.util.Assert;
@@ -36,13 +38,13 @@ public class MapPublicKeyCredentialUserEntityRepository implements PublicKeyCred
private final Map idToUserEntity = new HashMap<>();
@Override
- public PublicKeyCredentialUserEntity findById(Bytes id) {
+ public @Nullable PublicKeyCredentialUserEntity findById(Bytes id) {
Assert.notNull(id, "id cannot be null");
return this.idToUserEntity.get(id);
}
@Override
- public PublicKeyCredentialUserEntity findByUsername(String username) {
+ public @Nullable PublicKeyCredentialUserEntity findByUsername(String username) {
Assert.notNull(username, "username cannot be null");
return this.usernameToUserEntity.get(username);
}
diff --git a/webauthn/src/main/java/org/springframework/security/web/webauthn/management/MapUserCredentialRepository.java b/webauthn/src/main/java/org/springframework/security/web/webauthn/management/MapUserCredentialRepository.java
index 4ac32c93eb..59009dc4ab 100644
--- a/webauthn/src/main/java/org/springframework/security/web/webauthn/management/MapUserCredentialRepository.java
+++ b/webauthn/src/main/java/org/springframework/security/web/webauthn/management/MapUserCredentialRepository.java
@@ -24,6 +24,8 @@ import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
+import org.jspecify.annotations.Nullable;
+
import org.springframework.security.web.webauthn.api.Bytes;
import org.springframework.security.web.webauthn.api.CredentialRecord;
import org.springframework.util.Assert;
@@ -62,7 +64,7 @@ public class MapUserCredentialRepository implements UserCredentialRepository {
}
@Override
- public CredentialRecord findByCredentialId(Bytes credentialId) {
+ public @Nullable CredentialRecord findByCredentialId(Bytes credentialId) {
Assert.notNull(credentialId, "credentialId cannot be null");
return this.credentialIdToUserCredential.get(credentialId);
}
diff --git a/webauthn/src/main/java/org/springframework/security/web/webauthn/management/PublicKeyCredentialRequestOptionsRequest.java b/webauthn/src/main/java/org/springframework/security/web/webauthn/management/PublicKeyCredentialRequestOptionsRequest.java
index 5f92903a35..8c6d70c074 100644
--- a/webauthn/src/main/java/org/springframework/security/web/webauthn/management/PublicKeyCredentialRequestOptionsRequest.java
+++ b/webauthn/src/main/java/org/springframework/security/web/webauthn/management/PublicKeyCredentialRequestOptionsRequest.java
@@ -16,6 +16,8 @@
package org.springframework.security.web.webauthn.management;
+import org.jspecify.annotations.Nullable;
+
import org.springframework.security.core.Authentication;
public interface PublicKeyCredentialRequestOptionsRequest {
@@ -24,6 +26,6 @@ public interface PublicKeyCredentialRequestOptionsRequest {
* The current {@link Authentication}. Possibly null or an anonymous.
* @return the current {@link Authentication}
*/
- Authentication getAuthentication();
+ @Nullable Authentication getAuthentication();
}
diff --git a/webauthn/src/main/java/org/springframework/security/web/webauthn/management/PublicKeyCredentialUserEntityRepository.java b/webauthn/src/main/java/org/springframework/security/web/webauthn/management/PublicKeyCredentialUserEntityRepository.java
index b43c019398..28d9384e50 100644
--- a/webauthn/src/main/java/org/springframework/security/web/webauthn/management/PublicKeyCredentialUserEntityRepository.java
+++ b/webauthn/src/main/java/org/springframework/security/web/webauthn/management/PublicKeyCredentialUserEntityRepository.java
@@ -16,6 +16,8 @@
package org.springframework.security.web.webauthn.management;
+import org.jspecify.annotations.Nullable;
+
import org.springframework.security.web.webauthn.api.Bytes;
import org.springframework.security.web.webauthn.api.PublicKeyCredentialUserEntity;
@@ -33,14 +35,14 @@ public interface PublicKeyCredentialUserEntityRepository {
* @param id the id to lookup the username by
* @return the username or null if not found.
*/
- PublicKeyCredentialUserEntity findById(Bytes id);
+ @Nullable PublicKeyCredentialUserEntity findById(Bytes id);
/**
* Finds the {@link PublicKeyCredentialUserEntity} by the username.
* @param username the username to lookup the {@link PublicKeyCredentialUserEntity}
* @return the {@link PublicKeyCredentialUserEntity} or null if not found.
*/
- PublicKeyCredentialUserEntity findByUsername(String username);
+ @Nullable PublicKeyCredentialUserEntity findByUsername(String username);
/**
* Saves the {@link PublicKeyCredentialUserEntity} to the associated username.
diff --git a/webauthn/src/main/java/org/springframework/security/web/webauthn/management/RelyingPartyRegistrationRequest.java b/webauthn/src/main/java/org/springframework/security/web/webauthn/management/RelyingPartyRegistrationRequest.java
index 363ca0a0eb..2229ffa8b2 100644
--- a/webauthn/src/main/java/org/springframework/security/web/webauthn/management/RelyingPartyRegistrationRequest.java
+++ b/webauthn/src/main/java/org/springframework/security/web/webauthn/management/RelyingPartyRegistrationRequest.java
@@ -16,6 +16,8 @@
package org.springframework.security.web.webauthn.management;
+import org.jspecify.annotations.Nullable;
+
import org.springframework.security.web.webauthn.api.PublicKeyCredentialCreationOptions;
/**
diff --git a/webauthn/src/main/java/org/springframework/security/web/webauthn/management/UserCredentialRepository.java b/webauthn/src/main/java/org/springframework/security/web/webauthn/management/UserCredentialRepository.java
index f4acc8ee20..5749ed536a 100644
--- a/webauthn/src/main/java/org/springframework/security/web/webauthn/management/UserCredentialRepository.java
+++ b/webauthn/src/main/java/org/springframework/security/web/webauthn/management/UserCredentialRepository.java
@@ -18,6 +18,8 @@ package org.springframework.security.web.webauthn.management;
import java.util.List;
+import org.jspecify.annotations.Nullable;
+
import org.springframework.security.web.webauthn.api.Bytes;
import org.springframework.security.web.webauthn.api.CredentialRecord;
import org.springframework.security.web.webauthn.api.PublicKeyCredentialUserEntity;
@@ -47,7 +49,7 @@ public interface UserCredentialRepository {
* @param credentialId {@link CredentialRecord#getCredentialId()}
* @return the {@link CredentialRecord} or null if not found.
*/
- CredentialRecord findByCredentialId(Bytes credentialId);
+ @Nullable CredentialRecord findByCredentialId(Bytes credentialId);
/**
* Finds all {@link CredentialRecord} instances for a specific user.
diff --git a/webauthn/src/main/java/org/springframework/security/web/webauthn/management/WebAuthnRelyingPartyOperations.java b/webauthn/src/main/java/org/springframework/security/web/webauthn/management/WebAuthnRelyingPartyOperations.java
index 0e8222dbd9..fda3ddf560 100644
--- a/webauthn/src/main/java/org/springframework/security/web/webauthn/management/WebAuthnRelyingPartyOperations.java
+++ b/webauthn/src/main/java/org/springframework/security/web/webauthn/management/WebAuthnRelyingPartyOperations.java
@@ -16,6 +16,8 @@
package org.springframework.security.web.webauthn.management;
+import org.jspecify.annotations.Nullable;
+
import org.springframework.security.core.Authentication;
import org.springframework.security.web.webauthn.api.CredentialRecord;
import org.springframework.security.web.webauthn.api.PublicKeyCredentialCreationOptions;
diff --git a/webauthn/src/main/java/org/springframework/security/web/webauthn/management/Webauthn4JRelyingPartyOperations.java b/webauthn/src/main/java/org/springframework/security/web/webauthn/management/Webauthn4JRelyingPartyOperations.java
index d645118a58..f476bee8e5 100644
--- a/webauthn/src/main/java/org/springframework/security/web/webauthn/management/Webauthn4JRelyingPartyOperations.java
+++ b/webauthn/src/main/java/org/springframework/security/web/webauthn/management/Webauthn4JRelyingPartyOperations.java
@@ -43,8 +43,11 @@ import com.webauthn4j.data.attestation.authenticator.COSEKey;
import com.webauthn4j.data.client.Origin;
import com.webauthn4j.data.client.challenge.Challenge;
import com.webauthn4j.data.client.challenge.DefaultChallenge;
+import com.webauthn4j.data.extension.authenticator.AuthenticationExtensionAuthenticatorOutput;
import com.webauthn4j.data.extension.authenticator.RegistrationExtensionAuthenticatorOutput;
import com.webauthn4j.server.ServerProperty;
+import org.jspecify.annotations.NullUnmarked;
+import org.jspecify.annotations.Nullable;
import org.springframework.security.authentication.AuthenticationTrustResolver;
import org.springframework.security.authentication.AuthenticationTrustResolverImpl;
@@ -260,24 +263,28 @@ public class Webauthn4JRelyingPartyOperations implements WebAuthnRelyingPartyOpe
transports);
RegistrationParameters registrationParameters = new RegistrationParameters(serverProperty, pubKeyCredParams,
userVerificationRequired, userPresenceRequired);
- RegistrationData registrationData = this.webAuthnManager.validate(webauthn4jRegistrationRequest,
+ RegistrationData wa4jRegistrationData = this.webAuthnManager.validate(webauthn4jRegistrationRequest,
registrationParameters);
- AuthenticatorData authData = registrationData.getAttestationObject()
+ AttestationObject wa4jAttestationObject = wa4jRegistrationData.getAttestationObject();
+ Assert.notNull(wa4jAttestationObject, "attestationObject cannot be null");
+ AuthenticatorData wa4jAuthData = wa4jAttestationObject
.getAuthenticatorData();
CborConverter cborConverter = this.objectConverter.getCborConverter();
- COSEKey coseKey = authData.getAttestedCredentialData().getCOSEKey();
+ AttestedCredentialData wa4jCredData = wa4jAuthData.getAttestedCredentialData();
+ Assert.notNull(wa4jCredData, "attestedCredentialData cannot be null");
+ COSEKey coseKey = wa4jCredData.getCOSEKey();
byte[] rawCoseKey = cborConverter.writeValueAsBytes(coseKey);
ImmutableCredentialRecord userCredential = ImmutableCredentialRecord.builder()
.userEntityUserId(creationOptions.getUser().getId())
.credentialType(credential.getType())
.credentialId(credential.getRawId())
.publicKey(new ImmutablePublicKeyCose(rawCoseKey))
- .signatureCount(authData.getSignCount())
- .uvInitialized(authData.isFlagUV())
- .transports(convertTransports(registrationData.getTransports()))
- .backupEligible(authData.isFlagBE())
- .backupState(authData.isFlagBS())
+ .signatureCount(wa4jAuthData.getSignCount())
+ .uvInitialized(wa4jAuthData.isFlagUV())
+ .transports(convertTransports(wa4jRegistrationData.getTransports()))
+ .backupEligible(wa4jAuthData.isFlagBE())
+ .backupState(wa4jAuthData.isFlagBS())
.label(publicKey.getLabel())
.attestationClientDataJSON(credential.getResponse().getClientDataJSON())
.attestationObject(credential.getResponse().getAttestationObject())
@@ -286,7 +293,7 @@ public class Webauthn4JRelyingPartyOperations implements WebAuthnRelyingPartyOpe
return userCredential;
}
- private static Set convertTransportsToString(AuthenticatorAttestationResponse response) {
+ private static @Nullable Set convertTransportsToString(AuthenticatorAttestationResponse response) {
if (response.getTransports() == null) {
return null;
}
@@ -320,7 +327,7 @@ public class Webauthn4JRelyingPartyOperations implements WebAuthnRelyingPartyOpe
}
private static Set convertTransports(
- Set transports) {
+ @Nullable Set transports) {
if (transports == null) {
return Collections.emptySet();
}
@@ -344,7 +351,8 @@ public class Webauthn4JRelyingPartyOperations implements WebAuthnRelyingPartyOpe
.build();
}
- private List findCredentialRecords(Authentication authentication) {
+ @NullUnmarked
+ private List findCredentialRecords(@Nullable Authentication authentication) {
if (!this.trustResolver.isAuthenticated(authentication)) {
return Collections.emptyList();
}
@@ -361,22 +369,30 @@ public class Webauthn4JRelyingPartyOperations implements WebAuthnRelyingPartyOpe
AuthenticatorAssertionResponse assertionResponse = request.getPublicKey().getResponse();
Bytes keyId = request.getPublicKey().getRawId();
CredentialRecord credentialRecord = this.userCredentials.findByCredentialId(keyId);
-
+ if (credentialRecord == null) {
+ throw new IllegalArgumentException("Unable to find CredentialRecord with id " + keyId);
+ }
CborConverter cborConverter = this.objectConverter.getCborConverter();
- AttestationObject attestationObject = cborConverter
- .readValue(credentialRecord.getAttestationObject().getBytes(), AttestationObject.class);
+ Bytes attestationObject = credentialRecord.getAttestationObject();
+ Assert.notNull(attestationObject, "attestationObject cannot be null");
+ AttestationObject wa4jAttestationObject = cborConverter
+ .readValue(attestationObject.getBytes(), AttestationObject.class);
+ Assert.notNull(wa4jAttestationObject, "attestationObject cannot be null");
+ AuthenticatorData wa4jAuthData = wa4jAttestationObject.getAuthenticatorData();
+ AttestedCredentialData wa4jCredData = wa4jAuthData.getAttestedCredentialData();
+ Assert.notNull(wa4jCredData, "attestedCredentialData cannot be null");
+ AttestedCredentialData data = new AttestedCredentialData(wa4jCredData.getAaguid(),
+ keyId.getBytes(), wa4jCredData.getCOSEKey());
- AuthenticatorData authData = attestationObject.getAuthenticatorData();
- AttestedCredentialData data = new AttestedCredentialData(authData.getAttestedCredentialData().getAaguid(),
- keyId.getBytes(), authData.getAttestedCredentialData().getCOSEKey());
-
- Authenticator authenticator = new AuthenticatorImpl(data, attestationObject.getAttestationStatement(),
+ Authenticator authenticator = new AuthenticatorImpl(data, wa4jAttestationObject.getAttestationStatement(),
credentialRecord.getSignatureCount());
Set origins = toOrigins();
Challenge challenge = new DefaultChallenge(requestOptions.getChallenge().getBytes());
// FIXME: should populate this
byte[] tokenBindingId = null /* set tokenBindingId */;
- ServerProperty serverProperty = new ServerProperty(origins, requestOptions.getRpId(), challenge,
+ String rpId = requestOptions.getRpId();
+ Assert.notNull(rpId, "rpId cannot be null");
+ ServerProperty serverProperty = new ServerProperty(origins, rpId, challenge,
tokenBindingId);
boolean userVerificationRequired = request.getRequestOptions()
.getUserVerification() == UserVerificationRequirement.REQUIRED;
@@ -387,17 +403,24 @@ public class Webauthn4JRelyingPartyOperations implements WebAuthnRelyingPartyOpe
AuthenticationParameters authenticationParameters = new AuthenticationParameters(serverProperty, authenticator,
userVerificationRequired);
- AuthenticationData authenticationData = this.webAuthnManager.validate(authenticationRequest,
+ AuthenticationData wa4jAuthenticationData = this.webAuthnManager.validate(authenticationRequest,
authenticationParameters);
- long updatedSignCount = authenticationData.getAuthenticatorData().getSignCount();
+ AuthenticatorData wa4jValidatedAuthData = wa4jAuthenticationData.getAuthenticatorData();
+ Assert.notNull(wa4jValidatedAuthData, "authenticatorData cannot be null");
+ long updatedSignCount = wa4jValidatedAuthData.getSignCount();
ImmutableCredentialRecord updatedRecord = ImmutableCredentialRecord.fromCredentialRecord(credentialRecord)
.lastUsed(Instant.now())
.signatureCount(updatedSignCount)
.build();
this.userCredentials.save(updatedRecord);
- return this.userEntities.findById(credentialRecord.getUserEntityUserId());
+ PublicKeyCredentialUserEntity userEntity = this.userEntities.findById(
+ credentialRecord.getUserEntityUserId());
+ if (userEntity == null) {
+ throw new IllegalArgumentException("Unable to find UserEntity with id " + credentialRecord.getUserEntityUserId() + " for " + request);
+ }
+ return userEntity;
}
}
diff --git a/webauthn/src/main/java/org/springframework/security/web/webauthn/management/package-info.java b/webauthn/src/main/java/org/springframework/security/web/webauthn/management/package-info.java
new file mode 100644
index 0000000000..c24a1a0615
--- /dev/null
+++ b/webauthn/src/main/java/org/springframework/security/web/webauthn/management/package-info.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2004-present the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Management of the WebAuthn APIs.
+ */
+@NullMarked
+package org.springframework.security.web.webauthn.management;
+
+import org.jspecify.annotations.NullMarked;
diff --git a/webauthn/src/main/java/org/springframework/security/web/webauthn/registration/HttpSessionPublicKeyCredentialCreationOptionsRepository.java b/webauthn/src/main/java/org/springframework/security/web/webauthn/registration/HttpSessionPublicKeyCredentialCreationOptionsRepository.java
index 393d241177..6ee8d66d3d 100644
--- a/webauthn/src/main/java/org/springframework/security/web/webauthn/registration/HttpSessionPublicKeyCredentialCreationOptionsRepository.java
+++ b/webauthn/src/main/java/org/springframework/security/web/webauthn/registration/HttpSessionPublicKeyCredentialCreationOptionsRepository.java
@@ -19,8 +19,10 @@ package org.springframework.security.web.webauthn.registration;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession;
+import org.jspecify.annotations.Nullable;
import org.springframework.security.web.webauthn.api.PublicKeyCredentialCreationOptions;
+import org.springframework.security.web.webauthn.api.PublicKeyCredentialRequestOptions;
import org.springframework.util.Assert;
public class HttpSessionPublicKeyCredentialCreationOptionsRepository
@@ -32,11 +34,12 @@ public class HttpSessionPublicKeyCredentialCreationOptionsRepository
@Override
public void save(HttpServletRequest request, HttpServletResponse response,
- PublicKeyCredentialCreationOptions options) {
+ @Nullable PublicKeyCredentialCreationOptions options) {
request.getSession().setAttribute(this.attrName, options);
}
- public PublicKeyCredentialCreationOptions load(HttpServletRequest request) {
+
+ public @Nullable PublicKeyCredentialCreationOptions load(HttpServletRequest request) {
HttpSession session = request.getSession(false);
if (session == null) {
return null;
diff --git a/webauthn/src/main/java/org/springframework/security/web/webauthn/registration/PublicKeyCredentialCreationOptionsRepository.java b/webauthn/src/main/java/org/springframework/security/web/webauthn/registration/PublicKeyCredentialCreationOptionsRepository.java
index 6aaeb88eb0..5165e55010 100644
--- a/webauthn/src/main/java/org/springframework/security/web/webauthn/registration/PublicKeyCredentialCreationOptionsRepository.java
+++ b/webauthn/src/main/java/org/springframework/security/web/webauthn/registration/PublicKeyCredentialCreationOptionsRepository.java
@@ -18,6 +18,7 @@ package org.springframework.security.web.webauthn.registration;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
+import org.jspecify.annotations.Nullable;
import org.springframework.security.web.webauthn.api.PublicKeyCredentialCreationOptions;
@@ -38,7 +39,7 @@ public interface PublicKeyCredentialCreationOptionsRepository {
* @param options the {@link PublicKeyCredentialCreationOptions} to save or null if an
* existing {@link PublicKeyCredentialCreationOptions} should be removed.
*/
- void save(HttpServletRequest request, HttpServletResponse response, PublicKeyCredentialCreationOptions options);
+ void save(HttpServletRequest request, HttpServletResponse response, @Nullable PublicKeyCredentialCreationOptions options);
/**
* Gets a saved {@link PublicKeyCredentialCreationOptions} if it exists, otherwise
@@ -47,6 +48,6 @@ public interface PublicKeyCredentialCreationOptionsRepository {
* @return the {@link PublicKeyCredentialCreationOptions} that was saved, otherwise
* null.
*/
- PublicKeyCredentialCreationOptions load(HttpServletRequest request);
+ @Nullable PublicKeyCredentialCreationOptions load(HttpServletRequest request);
}
diff --git a/webauthn/src/main/java/org/springframework/security/web/webauthn/registration/WebAuthnRegistrationFilter.java b/webauthn/src/main/java/org/springframework/security/web/webauthn/registration/WebAuthnRegistrationFilter.java
index 46694fca03..c6359d0c4c 100644
--- a/webauthn/src/main/java/org/springframework/security/web/webauthn/registration/WebAuthnRegistrationFilter.java
+++ b/webauthn/src/main/java/org/springframework/security/web/webauthn/registration/WebAuthnRegistrationFilter.java
@@ -25,6 +25,7 @@ import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
+import org.jspecify.annotations.Nullable;
import org.springframework.http.HttpInputMessage;
import org.springframework.http.HttpMethod;
@@ -190,7 +191,7 @@ public class WebAuthnRegistrationFilter extends OncePerRequestFilter {
this.converter.write(registrationResponse, MediaType.APPLICATION_JSON, outputMessage);
}
- private WebAuthnRegistrationRequest readRegistrationRequest(HttpServletRequest request) {
+ private @Nullable WebAuthnRegistrationRequest readRegistrationRequest(HttpServletRequest request) {
HttpInputMessage inputMessage = new ServletServerHttpRequest(request);
try {
return (WebAuthnRegistrationRequest) this.converter.read(WebAuthnRegistrationRequest.class, inputMessage);
@@ -201,7 +202,7 @@ public class WebAuthnRegistrationFilter extends OncePerRequestFilter {
}
}
- private void removeCredential(HttpServletRequest request, HttpServletResponse response, String id)
+ private void removeCredential(HttpServletRequest request, HttpServletResponse response, @Nullable String id)
throws IOException {
this.userCredentials.delete(Bytes.fromBase64(id));
response.setStatus(HttpStatus.NO_CONTENT.value());
@@ -209,9 +210,9 @@ public class WebAuthnRegistrationFilter extends OncePerRequestFilter {
static class WebAuthnRegistrationRequest {
- private RelyingPartyPublicKey publicKey;
+ private @Nullable RelyingPartyPublicKey publicKey;
- RelyingPartyPublicKey getPublicKey() {
+ @Nullable RelyingPartyPublicKey getPublicKey() {
return this.publicKey;
}
diff --git a/webauthn/src/main/java/org/springframework/security/web/webauthn/registration/package-info.java b/webauthn/src/main/java/org/springframework/security/web/webauthn/registration/package-info.java
new file mode 100644
index 0000000000..639a9aefa9
--- /dev/null
+++ b/webauthn/src/main/java/org/springframework/security/web/webauthn/registration/package-info.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2004-present the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * WebAuthn Registration support.
+ */
+@NullMarked
+package org.springframework.security.web.webauthn.registration;
+
+import org.jspecify.annotations.NullMarked;