mirror of
https://github.com/spring-projects/spring-security.git
synced 2025-10-22 18:28:51 +00:00
Enable Null checking in spring-security-webauthn via JSpecify
Closes gh-17839
This commit is contained in:
parent
3dbcf266e9
commit
0a991a91ce
@ -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");
|
||||
}
|
||||
|
||||
|
@ -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");
|
||||
|
@ -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;
|
@ -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 <a href=
|
||||
* "https://www.w3.org/TR/webauthn-3/#authenticatorassertionresponse">AuthenticatorAssertionResponse</a>
|
||||
@ -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</a> is empty, and MAY return one otherwise.
|
||||
* @return the <a href="https://www.w3.org/TR/webauthn-3/#user-handle">user handle</a>
|
||||
*/
|
||||
public Bytes getUserHandle() {
|
||||
public @Nullable Bytes getUserHandle() {
|
||||
return this.userHandle;
|
||||
}
|
||||
|
||||
@ -113,7 +117,7 @@ public final class AuthenticatorAssertionResponse extends AuthenticatorResponse
|
||||
* object</a>, 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);
|
||||
}
|
||||
|
@ -19,6 +19,8 @@ package org.springframework.security.web.webauthn.api;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* <a href=
|
||||
* "https://www.w3.org/TR/webauthn-3/#authenticatorattestationresponse">AuthenticatorAttestationResponse</a>
|
||||
@ -36,10 +38,10 @@ public final class AuthenticatorAttestationResponse extends AuthenticatorRespons
|
||||
|
||||
private final Bytes attestationObject;
|
||||
|
||||
private final List<AuthenticatorTransport> transports;
|
||||
private final @Nullable List<AuthenticatorTransport> transports;
|
||||
|
||||
private AuthenticatorAttestationResponse(Bytes clientDataJSON, Bytes attestationObject,
|
||||
List<AuthenticatorTransport> transports) {
|
||||
@Nullable List<AuthenticatorTransport> 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</a>
|
||||
* @return the transports
|
||||
*/
|
||||
public List<AuthenticatorTransport> getTransports() {
|
||||
public @Nullable List<AuthenticatorTransport> 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<AuthenticatorTransport> transports;
|
||||
private @Nullable List<AuthenticatorTransport> transports;
|
||||
|
||||
@SuppressWarnings("NullAway.Init")
|
||||
private Bytes clientDataJSON;
|
||||
|
||||
private AuthenticatorAttestationResponseBuilder() {
|
||||
|
@ -16,6 +16,8 @@
|
||||
|
||||
package org.springframework.security.web.webauthn.api;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* <a href=
|
||||
* "https://www.w3.org/TR/webauthn-3/#dictdef-authenticatorselectioncriteria">AuthenticatorAttachment</a>
|
||||
@ -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</a>).
|
||||
* @return the authenticator attachment
|
||||
*/
|
||||
public AuthenticatorAttachment getAuthenticatorAttachment() {
|
||||
public @Nullable AuthenticatorAttachment getAuthenticatorAttachment() {
|
||||
return this.authenticatorAttachment;
|
||||
}
|
||||
|
||||
@ -81,7 +83,7 @@ public final class AuthenticatorSelectionCriteria {
|
||||
* discoverable credential</a>.
|
||||
* @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() {
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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 <a href="https://www.w3.org/TR/webauthn-3/#credential-record">Credential
|
||||
* Record</a> 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</a>
|
||||
* @return
|
||||
*/
|
||||
PublicKeyCredentialType getCredentialType();
|
||||
@Nullable PublicKeyCredentialType getCredentialType();
|
||||
|
||||
/**
|
||||
* The <a href=
|
||||
@ -104,7 +106,7 @@ public interface CredentialRecord {
|
||||
* source was registered.
|
||||
* @return the attestationObject
|
||||
*/
|
||||
Bytes getAttestationObject();
|
||||
@Nullable Bytes getAttestationObject();
|
||||
|
||||
/**
|
||||
* The <a href=
|
||||
@ -113,7 +115,7 @@ public interface CredentialRecord {
|
||||
* source was registered.
|
||||
* @return
|
||||
*/
|
||||
Bytes getAttestationClientDataJSON();
|
||||
@Nullable Bytes getAttestationClientDataJSON();
|
||||
|
||||
/**
|
||||
* A human-readable label for this {@link CredentialRecord} assigned by the user.
|
||||
|
@ -19,6 +19,8 @@ package org.springframework.security.web.webauthn.api;
|
||||
import java.time.Instant;
|
||||
import java.util.Set;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* An immutable {@link CredentialRecord}.
|
||||
*
|
||||
@ -27,7 +29,7 @@ import java.util.Set;
|
||||
*/
|
||||
public final class ImmutableCredentialRecord implements CredentialRecord {
|
||||
|
||||
private final PublicKeyCredentialType credentialType;
|
||||
private final @Nullable PublicKeyCredentialType credentialType;
|
||||
|
||||
private final Bytes credentialId;
|
||||
|
||||
@ -45,9 +47,9 @@ public final class ImmutableCredentialRecord implements CredentialRecord {
|
||||
|
||||
private final boolean backupState;
|
||||
|
||||
private final Bytes attestationObject;
|
||||
private final @Nullable Bytes attestationObject;
|
||||
|
||||
private final Bytes attestationClientDataJSON;
|
||||
private final @Nullable Bytes attestationClientDataJSON;
|
||||
|
||||
private final Instant created;
|
||||
|
||||
@ -55,10 +57,10 @@ public final class ImmutableCredentialRecord implements CredentialRecord {
|
||||
|
||||
private final String label;
|
||||
|
||||
private ImmutableCredentialRecord(PublicKeyCredentialType credentialType, Bytes credentialId,
|
||||
private ImmutableCredentialRecord(@Nullable PublicKeyCredentialType credentialType, Bytes credentialId,
|
||||
Bytes userEntityUserId, PublicKeyCose publicKey, long signatureCount, boolean uvInitialized,
|
||||
Set<AuthenticatorTransport> 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<AuthenticatorTransport> 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;
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -18,6 +18,8 @@ package org.springframework.security.web.webauthn.api;
|
||||
|
||||
import java.io.Serial;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* <a href=
|
||||
* "https://www.w3.org/TR/webauthn-3/#dictdef-publickeycredentialuserentity">PublicKeyCredentialUserEntity</a>
|
||||
@ -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() {
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
/**
|
||||
* <a href="https://www.w3.org/TR/webauthn-3/#iface-pkcredential">PublicKeyCredential</a>
|
||||
* contains the attributes that are returned to the caller when a new credential is
|
||||
@ -40,13 +44,13 @@ public final class PublicKeyCredential<R extends AuthenticatorResponse> 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<R extends AuthenticatorResponse> 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<R extends AuthenticatorResponse> 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<R extends AuthenticatorResponse> implemen
|
||||
*/
|
||||
public static final class PublicKeyCredentialBuilder<R extends AuthenticatorResponse> {
|
||||
|
||||
@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<R extends AuthenticatorResponse> implemen
|
||||
* @return a new {@link PublicKeyCredential}
|
||||
*/
|
||||
public PublicKeyCredential<R> 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);
|
||||
}
|
||||
|
@ -22,6 +22,8 @@ import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* Represents the <a href=
|
||||
* "https://www.w3.org/TR/webauthn-3/#dictdef-publickeycredentialcreationoptions">PublicKeyCredentialCreationOptions</a>
|
||||
@ -42,21 +44,22 @@ public final class PublicKeyCredentialCreationOptions {
|
||||
|
||||
private final List<PublicKeyCredentialParameters> pubKeyCredParams;
|
||||
|
||||
private final Duration timeout;
|
||||
private final @Nullable Duration timeout;
|
||||
|
||||
private final List<PublicKeyCredentialDescriptor> 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<PublicKeyCredentialParameters> pubKeyCredParams, Duration timeout,
|
||||
Bytes challenge, List<PublicKeyCredentialParameters> pubKeyCredParams, @Nullable Duration timeout,
|
||||
List<PublicKeyCredentialDescriptor> 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<PublicKeyCredentialParameters> pubKeyCredParams = new ArrayList<>();
|
||||
|
||||
private Duration timeout;
|
||||
private @Nullable Duration timeout;
|
||||
|
||||
private List<PublicKeyCredentialDescriptor> 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() {
|
||||
}
|
||||
|
@ -20,6 +20,8 @@ import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
import java.util.Set;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* <a href=
|
||||
* "https://www.w3.org/TR/webauthn-3/#dictdef-publickeycredentialdescriptor">PublicKeyCredentialDescriptor</a>
|
||||
@ -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<AuthenticatorTransport> transports;
|
||||
private final @Nullable Set<AuthenticatorTransport> transports;
|
||||
|
||||
private PublicKeyCredentialDescriptor(PublicKeyCredentialType type, Bytes id,
|
||||
Set<AuthenticatorTransport> transports) {
|
||||
private PublicKeyCredentialDescriptor(PublicKeyCredentialType type, @Nullable Bytes id,
|
||||
@Nullable Set<AuthenticatorTransport> 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<AuthenticatorTransport> getTransports() {
|
||||
public @Nullable Set<AuthenticatorTransport> 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<AuthenticatorTransport> transports;
|
||||
private @Nullable Set<AuthenticatorTransport> transports;
|
||||
|
||||
private PublicKeyCredentialDescriptorBuilder() {
|
||||
}
|
||||
|
@ -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<PublicKeyCredentialDescriptor> allowCredentials;
|
||||
|
||||
private final UserVerificationRequirement userVerification;
|
||||
private final @Nullable UserVerificationRequirement userVerification;
|
||||
|
||||
private final AuthenticationExtensionsClientInputs extensions;
|
||||
|
||||
private PublicKeyCredentialRequestOptions(Bytes challenge, Duration timeout, String rpId,
|
||||
List<PublicKeyCredentialDescriptor> allowCredentials, UserVerificationRequirement userVerification,
|
||||
AuthenticationExtensionsClientInputs extensions) {
|
||||
private PublicKeyCredentialRequestOptions(Bytes challenge, Duration timeout, @Nullable String rpId,
|
||||
List<PublicKeyCredentialDescriptor> 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<PublicKeyCredentialDescriptor> allowCredentials = Collections.emptyList();
|
||||
|
||||
private UserVerificationRequirement userVerification;
|
||||
private @Nullable UserVerificationRequirement userVerification;
|
||||
|
||||
private AuthenticationExtensionsClientInputs extensions = new ImmutableAuthenticationExtensionsClientInputs(
|
||||
new ArrayList<>());
|
||||
|
@ -16,6 +16,10 @@
|
||||
|
||||
package org.springframework.security.web.webauthn.api;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* The <a href=
|
||||
* "https://www.w3.org/TR/webauthn-3/#dictdef-publickeycredentialrpentity">PublicKeyCredentialRpEntity</a>
|
||||
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
|
||||
}
|
||||
|
@ -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;
|
@ -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;
|
||||
|
@ -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);
|
||||
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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;
|
@ -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<AuthenticatorA
|
||||
}
|
||||
|
||||
@Override
|
||||
public AuthenticatorAttachment deserialize(JsonParser parser, DeserializationContext ctxt)
|
||||
public @Nullable AuthenticatorAttachment deserialize(JsonParser parser, DeserializationContext ctxt)
|
||||
throws IOException, JacksonException {
|
||||
String type = parser.readValueAs(String.class);
|
||||
for (AuthenticatorAttachment publicKeyCredentialType : AuthenticatorAttachment.values()) {
|
||||
|
@ -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.AuthenticatorTransport;
|
||||
|
||||
@ -39,7 +40,7 @@ class AuthenticatorTransportDeserializer extends StdDeserializer<AuthenticatorTr
|
||||
}
|
||||
|
||||
@Override
|
||||
public AuthenticatorTransport deserialize(JsonParser parser, DeserializationContext ctxt)
|
||||
public @Nullable AuthenticatorTransport deserialize(JsonParser parser, DeserializationContext ctxt)
|
||||
throws IOException, JacksonException {
|
||||
String transportValue = parser.readValueAs(String.class);
|
||||
for (AuthenticatorTransport transport : AuthenticatorTransport.values()) {
|
||||
|
@ -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.COSEAlgorithmIdentifier;
|
||||
|
||||
@ -39,7 +40,7 @@ class COSEAlgorithmIdentifierDeserializer extends StdDeserializer<COSEAlgorithmI
|
||||
}
|
||||
|
||||
@Override
|
||||
public COSEAlgorithmIdentifier deserialize(JsonParser parser, DeserializationContext ctxt)
|
||||
public @Nullable COSEAlgorithmIdentifier deserialize(JsonParser parser, DeserializationContext ctxt)
|
||||
throws IOException, JacksonException {
|
||||
Long transportValue = parser.readValueAs(Long.class);
|
||||
for (COSEAlgorithmIdentifier identifier : COSEAlgorithmIdentifier.values()) {
|
||||
|
@ -20,6 +20,7 @@ import java.time.Duration;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.security.web.webauthn.api.PublicKeyCredentialCreationOptions;
|
||||
|
||||
@ -33,6 +34,6 @@ import org.springframework.security.web.webauthn.api.PublicKeyCredentialCreation
|
||||
abstract class PublicKeyCredentialCreationOptionsMixin {
|
||||
|
||||
@JsonSerialize(using = DurationSerializer.class)
|
||||
private Duration timeout;
|
||||
private @Nullable Duration timeout;
|
||||
|
||||
}
|
||||
|
@ -20,6 +20,7 @@ import java.time.Duration;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.security.web.webauthn.api.PublicKeyCredentialRequestOptions;
|
||||
|
||||
@ -33,6 +34,6 @@ import org.springframework.security.web.webauthn.api.PublicKeyCredentialRequestO
|
||||
class PublicKeyCredentialRequestOptionsMixin {
|
||||
|
||||
@JsonSerialize(using = DurationSerializer.class)
|
||||
private final Duration timeout = null;
|
||||
private final @Nullable Duration timeout = null;
|
||||
|
||||
}
|
||||
|
@ -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 Jackson Support.
|
||||
*/
|
||||
@NullMarked
|
||||
package org.springframework.security.web.webauthn.jackson;
|
||||
|
||||
import org.jspecify.annotations.NullMarked;
|
@ -16,18 +16,20 @@
|
||||
|
||||
package org.springframework.security.web.webauthn.management;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.security.core.Authentication;
|
||||
|
||||
public class ImmutablePublicKeyCredentialRequestOptionsRequest implements PublicKeyCredentialRequestOptionsRequest {
|
||||
|
||||
private final Authentication authentication;
|
||||
private final @Nullable Authentication authentication;
|
||||
|
||||
public ImmutablePublicKeyCredentialRequestOptionsRequest(Authentication authentication) {
|
||||
public ImmutablePublicKeyCredentialRequestOptionsRequest(@Nullable Authentication authentication) {
|
||||
this.authentication = authentication;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Authentication getAuthentication() {
|
||||
public @Nullable Authentication getAuthentication() {
|
||||
return this.authentication;
|
||||
}
|
||||
|
||||
|
@ -16,6 +16,8 @@
|
||||
|
||||
package org.springframework.security.web.webauthn.management;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.security.web.webauthn.api.PublicKeyCredentialCreationOptions;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
@ -40,7 +42,7 @@ public class ImmutableRelyingPartyRegistrationRequest implements RelyingPartyReg
|
||||
* @param publicKey this is submitted by the client and if validated stored.
|
||||
*/
|
||||
public ImmutableRelyingPartyRegistrationRequest(PublicKeyCredentialCreationOptions options,
|
||||
RelyingPartyPublicKey publicKey) {
|
||||
@Nullable RelyingPartyPublicKey publicKey) {
|
||||
Assert.notNull(options, "options cannot be null");
|
||||
Assert.notNull(publicKey, "publicKey cannot be null");
|
||||
this.options = options;
|
||||
|
@ -23,6 +23,8 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
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;
|
||||
@ -105,7 +107,7 @@ public final class JdbcPublicKeyCredentialUserEntityRepository implements Public
|
||||
}
|
||||
|
||||
@Override
|
||||
public PublicKeyCredentialUserEntity findById(Bytes id) {
|
||||
public @Nullable PublicKeyCredentialUserEntity findById(Bytes id) {
|
||||
Assert.notNull(id, "id cannot be null");
|
||||
List<PublicKeyCredentialUserEntity> 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<PublicKeyCredentialUserEntity> result = this.jdbcOperations.query(FIND_USER_BY_NAME_SQL,
|
||||
this.userEntityRowMapper, username);
|
||||
|
@ -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<CredentialRecord> 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;
|
||||
}
|
||||
|
@ -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<Bytes, PublicKeyCredentialUserEntity> 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);
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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();
|
||||
|
||||
}
|
||||
|
@ -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.
|
||||
|
@ -16,6 +16,8 @@
|
||||
|
||||
package org.springframework.security.web.webauthn.management;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.security.web.webauthn.api.PublicKeyCredentialCreationOptions;
|
||||
|
||||
/**
|
||||
|
@ -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.
|
||||
|
@ -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;
|
||||
|
@ -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<RegistrationExtensionAuthenticatorOutput> authData = registrationData.getAttestationObject()
|
||||
AttestationObject wa4jAttestationObject = wa4jRegistrationData.getAttestationObject();
|
||||
Assert.notNull(wa4jAttestationObject, "attestationObject cannot be null");
|
||||
AuthenticatorData<RegistrationExtensionAuthenticatorOutput> 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<String> convertTransportsToString(AuthenticatorAttestationResponse response) {
|
||||
private static @Nullable Set<String> convertTransportsToString(AuthenticatorAttestationResponse response) {
|
||||
if (response.getTransports() == null) {
|
||||
return null;
|
||||
}
|
||||
@ -320,7 +327,7 @@ public class Webauthn4JRelyingPartyOperations implements WebAuthnRelyingPartyOpe
|
||||
}
|
||||
|
||||
private static Set<AuthenticatorTransport> convertTransports(
|
||||
Set<com.webauthn4j.data.AuthenticatorTransport> transports) {
|
||||
@Nullable Set<com.webauthn4j.data.AuthenticatorTransport> transports) {
|
||||
if (transports == null) {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
@ -344,7 +351,8 @@ public class Webauthn4JRelyingPartyOperations implements WebAuthnRelyingPartyOpe
|
||||
.build();
|
||||
}
|
||||
|
||||
private List<CredentialRecord> findCredentialRecords(Authentication authentication) {
|
||||
@NullUnmarked
|
||||
private List<CredentialRecord> 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<RegistrationExtensionAuthenticatorOutput> 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<RegistrationExtensionAuthenticatorOutput> 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<Origin> 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<AuthenticationExtensionAuthenticatorOutput> 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;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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;
|
@ -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;
|
||||
|
@ -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);
|
||||
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
Loading…
x
Reference in New Issue
Block a user