mirror of
https://github.com/spring-projects/spring-security.git
synced 2025-10-23 02:38:48 +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;
|
package org.springframework.security.web.webauthn.aot;
|
||||||
|
|
||||||
|
import org.jspecify.annotations.Nullable;
|
||||||
|
|
||||||
import org.springframework.aot.hint.RuntimeHints;
|
import org.springframework.aot.hint.RuntimeHints;
|
||||||
import org.springframework.aot.hint.RuntimeHintsRegistrar;
|
import org.springframework.aot.hint.RuntimeHintsRegistrar;
|
||||||
import org.springframework.jdbc.core.JdbcOperations;
|
import org.springframework.jdbc.core.JdbcOperations;
|
||||||
@ -33,7 +35,7 @@ import org.springframework.security.web.webauthn.management.PublicKeyCredentialU
|
|||||||
class PublicKeyCredentialUserEntityRuntimeHints implements RuntimeHintsRegistrar {
|
class PublicKeyCredentialUserEntityRuntimeHints implements RuntimeHintsRegistrar {
|
||||||
|
|
||||||
@Override
|
@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");
|
hints.resources().registerPattern("org/springframework/security/user-entities-schema.sql");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -16,6 +16,8 @@
|
|||||||
|
|
||||||
package org.springframework.security.web.webauthn.aot;
|
package org.springframework.security.web.webauthn.aot;
|
||||||
|
|
||||||
|
import org.jspecify.annotations.Nullable;
|
||||||
|
|
||||||
import org.springframework.aot.hint.RuntimeHints;
|
import org.springframework.aot.hint.RuntimeHints;
|
||||||
import org.springframework.aot.hint.RuntimeHintsRegistrar;
|
import org.springframework.aot.hint.RuntimeHintsRegistrar;
|
||||||
import org.springframework.jdbc.core.JdbcOperations;
|
import org.springframework.jdbc.core.JdbcOperations;
|
||||||
@ -33,7 +35,7 @@ import org.springframework.security.web.webauthn.management.UserCredentialReposi
|
|||||||
class UserCredentialRuntimeHints implements RuntimeHintsRegistrar {
|
class UserCredentialRuntimeHints implements RuntimeHintsRegistrar {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void registerHints(RuntimeHints hints, ClassLoader classLoader) {
|
public void registerHints(RuntimeHints hints, @Nullable ClassLoader classLoader) {
|
||||||
hints.resources()
|
hints.resources()
|
||||||
.registerPattern("org/springframework/security/user-credentials-schema.sql")
|
.registerPattern("org/springframework/security/user-credentials-schema.sql")
|
||||||
.registerPattern("org/springframework/security/user-credentials-schema-postgres.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 java.io.Serial;
|
||||||
|
|
||||||
|
import org.jspecify.annotations.Nullable;
|
||||||
|
|
||||||
|
import org.springframework.util.Assert;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The <a href=
|
* The <a href=
|
||||||
* "https://www.w3.org/TR/webauthn-3/#authenticatorassertionresponse">AuthenticatorAssertionResponse</a>
|
* "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 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,
|
private AuthenticatorAssertionResponse(Bytes clientDataJSON, Bytes authenticatorData, Bytes signature,
|
||||||
Bytes userHandle, Bytes attestationObject) {
|
@Nullable Bytes userHandle, @Nullable Bytes attestationObject) {
|
||||||
super(clientDataJSON);
|
super(clientDataJSON);
|
||||||
this.authenticatorData = authenticatorData;
|
this.authenticatorData = authenticatorData;
|
||||||
this.signature = signature;
|
this.signature = signature;
|
||||||
@ -101,7 +105,7 @@ public final class AuthenticatorAssertionResponse extends AuthenticatorResponse
|
|||||||
* ceremony</a> is empty, and MAY return one otherwise.
|
* 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>
|
* @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;
|
return this.userHandle;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -113,7 +117,7 @@ public final class AuthenticatorAssertionResponse extends AuthenticatorResponse
|
|||||||
* object</a>, if the authenticator supports attestation in assertions.
|
* object</a>, if the authenticator supports attestation in assertions.
|
||||||
* @return the {@code attestationObject}
|
* @return the {@code attestationObject}
|
||||||
*/
|
*/
|
||||||
public Bytes getAttestationObject() {
|
public @Nullable Bytes getAttestationObject() {
|
||||||
return this.attestationObject;
|
return this.attestationObject;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -133,15 +137,15 @@ public final class AuthenticatorAssertionResponse extends AuthenticatorResponse
|
|||||||
*/
|
*/
|
||||||
public static final class AuthenticatorAssertionResponseBuilder {
|
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() {
|
private AuthenticatorAssertionResponseBuilder() {
|
||||||
}
|
}
|
||||||
@ -201,6 +205,9 @@ public final class AuthenticatorAssertionResponse extends AuthenticatorResponse
|
|||||||
* @return the {@link AuthenticatorAssertionResponse}
|
* @return the {@link AuthenticatorAssertionResponse}
|
||||||
*/
|
*/
|
||||||
public AuthenticatorAssertionResponse build() {
|
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,
|
return new AuthenticatorAssertionResponse(this.clientDataJSON, this.authenticatorData, this.signature,
|
||||||
this.userHandle, this.attestationObject);
|
this.userHandle, this.attestationObject);
|
||||||
}
|
}
|
||||||
|
@ -19,6 +19,8 @@ package org.springframework.security.web.webauthn.api;
|
|||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.jspecify.annotations.Nullable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <a href=
|
* <a href=
|
||||||
* "https://www.w3.org/TR/webauthn-3/#authenticatorattestationresponse">AuthenticatorAttestationResponse</a>
|
* "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 Bytes attestationObject;
|
||||||
|
|
||||||
private final List<AuthenticatorTransport> transports;
|
private final @Nullable List<AuthenticatorTransport> transports;
|
||||||
|
|
||||||
private AuthenticatorAttestationResponse(Bytes clientDataJSON, Bytes attestationObject,
|
private AuthenticatorAttestationResponse(Bytes clientDataJSON, Bytes attestationObject,
|
||||||
List<AuthenticatorTransport> transports) {
|
@Nullable List<AuthenticatorTransport> transports) {
|
||||||
super(clientDataJSON);
|
super(clientDataJSON);
|
||||||
this.attestationObject = attestationObject;
|
this.attestationObject = attestationObject;
|
||||||
this.transports = transports;
|
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>
|
* "https://www.w3.org/TR/webauthn-3/#dom-authenticatorattestationresponse-transports-slot">transports</a>
|
||||||
* @return the transports
|
* @return the transports
|
||||||
*/
|
*/
|
||||||
public List<AuthenticatorTransport> getTransports() {
|
public @Nullable List<AuthenticatorTransport> getTransports() {
|
||||||
return this.transports;
|
return this.transports;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -83,10 +85,12 @@ public final class AuthenticatorAttestationResponse extends AuthenticatorRespons
|
|||||||
*/
|
*/
|
||||||
public static final class AuthenticatorAttestationResponseBuilder {
|
public static final class AuthenticatorAttestationResponseBuilder {
|
||||||
|
|
||||||
|
@SuppressWarnings("NullAway.Init")
|
||||||
private Bytes attestationObject;
|
private Bytes attestationObject;
|
||||||
|
|
||||||
private List<AuthenticatorTransport> transports;
|
private @Nullable List<AuthenticatorTransport> transports;
|
||||||
|
|
||||||
|
@SuppressWarnings("NullAway.Init")
|
||||||
private Bytes clientDataJSON;
|
private Bytes clientDataJSON;
|
||||||
|
|
||||||
private AuthenticatorAttestationResponseBuilder() {
|
private AuthenticatorAttestationResponseBuilder() {
|
||||||
|
@ -16,6 +16,8 @@
|
|||||||
|
|
||||||
package org.springframework.security.web.webauthn.api;
|
package org.springframework.security.web.webauthn.api;
|
||||||
|
|
||||||
|
import org.jspecify.annotations.Nullable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <a href=
|
* <a href=
|
||||||
* "https://www.w3.org/TR/webauthn-3/#dictdef-authenticatorselectioncriteria">AuthenticatorAttachment</a>
|
* "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 {
|
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
|
// NOTE: There is no requireResidentKey property because it is only for backward
|
||||||
// compatibility with WebAuthn Level 1
|
// compatibility with WebAuthn Level 1
|
||||||
@ -48,8 +50,8 @@ public final class AuthenticatorSelectionCriteria {
|
|||||||
* @param residentKey the resident key requirement
|
* @param residentKey the resident key requirement
|
||||||
* @param userVerification the user verification
|
* @param userVerification the user verification
|
||||||
*/
|
*/
|
||||||
private AuthenticatorSelectionCriteria(AuthenticatorAttachment authenticatorAttachment,
|
private AuthenticatorSelectionCriteria(@Nullable AuthenticatorAttachment authenticatorAttachment,
|
||||||
ResidentKeyRequirement residentKey, UserVerificationRequirement userVerification) {
|
@Nullable ResidentKeyRequirement residentKey, @Nullable UserVerificationRequirement userVerification) {
|
||||||
this.authenticatorAttachment = authenticatorAttachment;
|
this.authenticatorAttachment = authenticatorAttachment;
|
||||||
this.residentKey = residentKey;
|
this.residentKey = residentKey;
|
||||||
this.userVerification = userVerification;
|
this.userVerification = userVerification;
|
||||||
@ -67,7 +69,7 @@ public final class AuthenticatorSelectionCriteria {
|
|||||||
* Authenticator Attachment Modality</a>).
|
* Authenticator Attachment Modality</a>).
|
||||||
* @return the authenticator attachment
|
* @return the authenticator attachment
|
||||||
*/
|
*/
|
||||||
public AuthenticatorAttachment getAuthenticatorAttachment() {
|
public @Nullable AuthenticatorAttachment getAuthenticatorAttachment() {
|
||||||
return this.authenticatorAttachment;
|
return this.authenticatorAttachment;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -81,7 +83,7 @@ public final class AuthenticatorSelectionCriteria {
|
|||||||
* discoverable credential</a>.
|
* discoverable credential</a>.
|
||||||
* @return the resident key requirement
|
* @return the resident key requirement
|
||||||
*/
|
*/
|
||||||
public ResidentKeyRequirement getResidentKey() {
|
public @Nullable ResidentKeyRequirement getResidentKey() {
|
||||||
return this.residentKey;
|
return this.residentKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -96,7 +98,7 @@ public final class AuthenticatorSelectionCriteria {
|
|||||||
* operation.
|
* operation.
|
||||||
* @return the user verification requirement
|
* @return the user verification requirement
|
||||||
*/
|
*/
|
||||||
public UserVerificationRequirement getUserVerification() {
|
public @Nullable UserVerificationRequirement getUserVerification() {
|
||||||
return this.userVerification;
|
return this.userVerification;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -116,11 +118,11 @@ public final class AuthenticatorSelectionCriteria {
|
|||||||
*/
|
*/
|
||||||
public static final class AuthenticatorSelectionCriteriaBuilder {
|
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() {
|
private AuthenticatorSelectionCriteriaBuilder() {
|
||||||
}
|
}
|
||||||
|
@ -22,6 +22,8 @@ import java.security.SecureRandom;
|
|||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Base64;
|
import java.util.Base64;
|
||||||
|
|
||||||
|
import org.jspecify.annotations.Nullable;
|
||||||
|
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -100,7 +102,7 @@ public final class Bytes implements Serializable {
|
|||||||
* @param base64UrlString the base64 url string
|
* @param base64UrlString the base64 url string
|
||||||
* @return the {@link Bytes}
|
* @return the {@link Bytes}
|
||||||
*/
|
*/
|
||||||
public static Bytes fromBase64(String base64UrlString) {
|
public static Bytes fromBase64(@Nullable String base64UrlString) {
|
||||||
byte[] bytes = DECODER.decode(base64UrlString);
|
byte[] bytes = DECODER.decode(base64UrlString);
|
||||||
return new Bytes(bytes);
|
return new Bytes(bytes);
|
||||||
}
|
}
|
||||||
|
@ -19,6 +19,8 @@ package org.springframework.security.web.webauthn.api;
|
|||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
|
import org.jspecify.annotations.Nullable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a <a href="https://www.w3.org/TR/webauthn-3/#credential-record">Credential
|
* Represents a <a href="https://www.w3.org/TR/webauthn-3/#credential-record">Credential
|
||||||
* Record</a> that is stored by the Relying Party
|
* 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>
|
* "https://www.w3.org/TR/webauthn-3/#abstract-opdef-credential-record-type">credential.type</a>
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
PublicKeyCredentialType getCredentialType();
|
@Nullable PublicKeyCredentialType getCredentialType();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The <a href=
|
* The <a href=
|
||||||
@ -104,7 +106,7 @@ public interface CredentialRecord {
|
|||||||
* source was registered.
|
* source was registered.
|
||||||
* @return the attestationObject
|
* @return the attestationObject
|
||||||
*/
|
*/
|
||||||
Bytes getAttestationObject();
|
@Nullable Bytes getAttestationObject();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The <a href=
|
* The <a href=
|
||||||
@ -113,7 +115,7 @@ public interface CredentialRecord {
|
|||||||
* source was registered.
|
* source was registered.
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
Bytes getAttestationClientDataJSON();
|
@Nullable Bytes getAttestationClientDataJSON();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A human-readable label for this {@link CredentialRecord} assigned by the user.
|
* 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.time.Instant;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
|
import org.jspecify.annotations.Nullable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An immutable {@link CredentialRecord}.
|
* An immutable {@link CredentialRecord}.
|
||||||
*
|
*
|
||||||
@ -27,7 +29,7 @@ import java.util.Set;
|
|||||||
*/
|
*/
|
||||||
public final class ImmutableCredentialRecord implements CredentialRecord {
|
public final class ImmutableCredentialRecord implements CredentialRecord {
|
||||||
|
|
||||||
private final PublicKeyCredentialType credentialType;
|
private final @Nullable PublicKeyCredentialType credentialType;
|
||||||
|
|
||||||
private final Bytes credentialId;
|
private final Bytes credentialId;
|
||||||
|
|
||||||
@ -45,9 +47,9 @@ public final class ImmutableCredentialRecord implements CredentialRecord {
|
|||||||
|
|
||||||
private final boolean backupState;
|
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;
|
private final Instant created;
|
||||||
|
|
||||||
@ -55,10 +57,10 @@ public final class ImmutableCredentialRecord implements CredentialRecord {
|
|||||||
|
|
||||||
private final String label;
|
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,
|
Bytes userEntityUserId, PublicKeyCose publicKey, long signatureCount, boolean uvInitialized,
|
||||||
Set<AuthenticatorTransport> transports, boolean backupEligible, boolean backupState,
|
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.credentialType = credentialType;
|
||||||
this.credentialId = credentialId;
|
this.credentialId = credentialId;
|
||||||
this.userEntityUserId = userEntityUserId;
|
this.userEntityUserId = userEntityUserId;
|
||||||
@ -76,7 +78,7 @@ public final class ImmutableCredentialRecord implements CredentialRecord {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public PublicKeyCredentialType getCredentialType() {
|
public @Nullable PublicKeyCredentialType getCredentialType() {
|
||||||
return this.credentialType;
|
return this.credentialType;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -121,12 +123,12 @@ public final class ImmutableCredentialRecord implements CredentialRecord {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Bytes getAttestationObject() {
|
public @Nullable Bytes getAttestationObject() {
|
||||||
return this.attestationObject;
|
return this.attestationObject;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Bytes getAttestationClientDataJSON() {
|
public @Nullable Bytes getAttestationClientDataJSON() {
|
||||||
return this.attestationClientDataJSON;
|
return this.attestationClientDataJSON;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -155,32 +157,37 @@ public final class ImmutableCredentialRecord implements CredentialRecord {
|
|||||||
|
|
||||||
public static final class ImmutableCredentialRecordBuilder {
|
public static final class ImmutableCredentialRecordBuilder {
|
||||||
|
|
||||||
private PublicKeyCredentialType credentialType;
|
private @Nullable PublicKeyCredentialType credentialType;
|
||||||
|
|
||||||
|
@SuppressWarnings("NullAway.Init")
|
||||||
private Bytes credentialId;
|
private Bytes credentialId;
|
||||||
|
|
||||||
|
@SuppressWarnings("NullAway.Init")
|
||||||
private Bytes userEntityUserId;
|
private Bytes userEntityUserId;
|
||||||
|
|
||||||
|
@SuppressWarnings("NullAway.Init")
|
||||||
private PublicKeyCose publicKey;
|
private PublicKeyCose publicKey;
|
||||||
|
|
||||||
private long signatureCount;
|
private long signatureCount;
|
||||||
|
|
||||||
private boolean uvInitialized;
|
private boolean uvInitialized;
|
||||||
|
|
||||||
|
@SuppressWarnings("NullAway.Init")
|
||||||
private Set<AuthenticatorTransport> transports;
|
private Set<AuthenticatorTransport> transports;
|
||||||
|
|
||||||
private boolean backupEligible;
|
private boolean backupEligible;
|
||||||
|
|
||||||
private boolean backupState;
|
private boolean backupState;
|
||||||
|
|
||||||
private Bytes attestationObject;
|
private @Nullable Bytes attestationObject;
|
||||||
|
|
||||||
private Bytes attestationClientDataJSON;
|
private @Nullable Bytes attestationClientDataJSON;
|
||||||
|
|
||||||
private Instant created = Instant.now();
|
private Instant created = Instant.now();
|
||||||
|
|
||||||
private Instant lastUsed = this.created;
|
private Instant lastUsed = this.created;
|
||||||
|
|
||||||
|
@SuppressWarnings("NullAway.Init")
|
||||||
private String label;
|
private String label;
|
||||||
|
|
||||||
private ImmutableCredentialRecordBuilder() {
|
private ImmutableCredentialRecordBuilder() {
|
||||||
@ -248,12 +255,12 @@ public final class ImmutableCredentialRecord implements CredentialRecord {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ImmutableCredentialRecordBuilder attestationObject(Bytes attestationObject) {
|
public ImmutableCredentialRecordBuilder attestationObject(@Nullable Bytes attestationObject) {
|
||||||
this.attestationObject = attestationObject;
|
this.attestationObject = attestationObject;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ImmutableCredentialRecordBuilder attestationClientDataJSON(Bytes attestationClientDataJSON) {
|
public ImmutableCredentialRecordBuilder attestationClientDataJSON(@Nullable Bytes attestationClientDataJSON) {
|
||||||
this.attestationClientDataJSON = attestationClientDataJSON;
|
this.attestationClientDataJSON = attestationClientDataJSON;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
@ -19,6 +19,10 @@ package org.springframework.security.web.webauthn.api;
|
|||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Base64;
|
import java.util.Base64;
|
||||||
|
|
||||||
|
import org.jspecify.annotations.Nullable;
|
||||||
|
|
||||||
|
import org.springframework.util.Assert;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An immutable {@link PublicKeyCose}
|
* An immutable {@link PublicKeyCose}
|
||||||
*
|
*
|
||||||
@ -33,7 +37,8 @@ public class ImmutablePublicKeyCose implements PublicKeyCose {
|
|||||||
* Creates a new instance.
|
* Creates a new instance.
|
||||||
* @param bytes the raw bytes of the public key.
|
* @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);
|
this.bytes = Arrays.copyOf(bytes, bytes.length);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -18,6 +18,8 @@ package org.springframework.security.web.webauthn.api;
|
|||||||
|
|
||||||
import java.io.Serial;
|
import java.io.Serial;
|
||||||
|
|
||||||
|
import org.jspecify.annotations.Nullable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <a href=
|
* <a href=
|
||||||
* "https://www.w3.org/TR/webauthn-3/#dictdef-publickeycredentialuserentity">PublicKeyCredentialUserEntity</a>
|
* "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
|
* fits within 64 bytes. See 6.4.1 String Truncation about truncation and other
|
||||||
* considerations.
|
* 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.name = name;
|
||||||
this.id = id;
|
this.id = id;
|
||||||
this.displayName = displayName;
|
this.displayName = displayName;
|
||||||
@ -121,7 +123,7 @@ public final class ImmutablePublicKeyCredentialUserEntity implements PublicKeyCr
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getDisplayName() {
|
public @Nullable String getDisplayName() {
|
||||||
return this.displayName;
|
return this.displayName;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -141,11 +143,13 @@ public final class ImmutablePublicKeyCredentialUserEntity implements PublicKeyCr
|
|||||||
*/
|
*/
|
||||||
public static final class PublicKeyCredentialUserEntityBuilder {
|
public static final class PublicKeyCredentialUserEntityBuilder {
|
||||||
|
|
||||||
|
@SuppressWarnings("NullAway.Init")
|
||||||
private String name;
|
private String name;
|
||||||
|
|
||||||
|
@SuppressWarnings("NullAway.Init")
|
||||||
private Bytes id;
|
private Bytes id;
|
||||||
|
|
||||||
private String displayName;
|
private @Nullable String displayName;
|
||||||
|
|
||||||
private PublicKeyCredentialUserEntityBuilder() {
|
private PublicKeyCredentialUserEntityBuilder() {
|
||||||
}
|
}
|
||||||
|
@ -19,6 +19,10 @@ package org.springframework.security.web.webauthn.api;
|
|||||||
import java.io.Serial;
|
import java.io.Serial;
|
||||||
import java.io.Serializable;
|
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>
|
* <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
|
* 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 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,
|
private PublicKeyCredential(String id, PublicKeyCredentialType type, Bytes rawId, R response,
|
||||||
AuthenticatorAttachment authenticatorAttachment,
|
@Nullable AuthenticatorAttachment authenticatorAttachment,
|
||||||
AuthenticationExtensionsClientOutputs clientExtensionResults) {
|
@Nullable AuthenticationExtensionsClientOutputs clientExtensionResults) {
|
||||||
this.id = id;
|
this.id = id;
|
||||||
this.type = type;
|
this.type = type;
|
||||||
this.rawId = rawId;
|
this.rawId = rawId;
|
||||||
@ -107,7 +111,7 @@ public final class PublicKeyCredential<R extends AuthenticatorResponse> implemen
|
|||||||
* navigator.credentials.get() methods successfully complete.
|
* navigator.credentials.get() methods successfully complete.
|
||||||
* @return the authenticator attachment
|
* @return the authenticator attachment
|
||||||
*/
|
*/
|
||||||
public AuthenticatorAttachment getAuthenticatorAttachment() {
|
public @Nullable AuthenticatorAttachment getAuthenticatorAttachment() {
|
||||||
return this.authenticatorAttachment;
|
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.
|
* is a mapping of extension identifier to client extension output.
|
||||||
* @return the extension results
|
* @return the extension results
|
||||||
*/
|
*/
|
||||||
public AuthenticationExtensionsClientOutputs getClientExtensionResults() {
|
public @Nullable AuthenticationExtensionsClientOutputs getClientExtensionResults() {
|
||||||
return this.clientExtensionResults;
|
return this.clientExtensionResults;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -139,17 +143,20 @@ public final class PublicKeyCredential<R extends AuthenticatorResponse> implemen
|
|||||||
*/
|
*/
|
||||||
public static final class PublicKeyCredentialBuilder<R extends AuthenticatorResponse> {
|
public static final class PublicKeyCredentialBuilder<R extends AuthenticatorResponse> {
|
||||||
|
|
||||||
|
@SuppressWarnings("NullAway.Init")
|
||||||
private String id;
|
private String id;
|
||||||
|
|
||||||
private PublicKeyCredentialType type;
|
private @Nullable PublicKeyCredentialType type;
|
||||||
|
|
||||||
|
@SuppressWarnings("NullAway.Init")
|
||||||
private Bytes rawId;
|
private Bytes rawId;
|
||||||
|
|
||||||
|
@SuppressWarnings("NullAway.Init")
|
||||||
private R response;
|
private R response;
|
||||||
|
|
||||||
private AuthenticatorAttachment authenticatorAttachment;
|
private @Nullable AuthenticatorAttachment authenticatorAttachment;
|
||||||
|
|
||||||
private AuthenticationExtensionsClientOutputs clientExtensionResults;
|
private @Nullable AuthenticationExtensionsClientOutputs clientExtensionResults;
|
||||||
|
|
||||||
private PublicKeyCredentialBuilder() {
|
private PublicKeyCredentialBuilder() {
|
||||||
}
|
}
|
||||||
@ -220,6 +227,10 @@ public final class PublicKeyCredential<R extends AuthenticatorResponse> implemen
|
|||||||
* @return a new {@link PublicKeyCredential}
|
* @return a new {@link PublicKeyCredential}
|
||||||
*/
|
*/
|
||||||
public PublicKeyCredential<R> build() {
|
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,
|
return new PublicKeyCredential(this.id, this.type, this.rawId, this.response, this.authenticatorAttachment,
|
||||||
this.clientExtensionResults);
|
this.clientExtensionResults);
|
||||||
}
|
}
|
||||||
|
@ -22,6 +22,8 @@ import java.util.Arrays;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
import org.jspecify.annotations.Nullable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents the <a href=
|
* Represents the <a href=
|
||||||
* "https://www.w3.org/TR/webauthn-3/#dictdef-publickeycredentialcreationoptions">PublicKeyCredentialCreationOptions</a>
|
* "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 List<PublicKeyCredentialParameters> pubKeyCredParams;
|
||||||
|
|
||||||
private final Duration timeout;
|
private final @Nullable Duration timeout;
|
||||||
|
|
||||||
private final List<PublicKeyCredentialDescriptor> excludeCredentials;
|
private final List<PublicKeyCredentialDescriptor> excludeCredentials;
|
||||||
|
|
||||||
private final AuthenticatorSelectionCriteria authenticatorSelection;
|
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,
|
private PublicKeyCredentialCreationOptions(PublicKeyCredentialRpEntity rp, PublicKeyCredentialUserEntity user,
|
||||||
Bytes challenge, List<PublicKeyCredentialParameters> pubKeyCredParams, Duration timeout,
|
Bytes challenge, List<PublicKeyCredentialParameters> pubKeyCredParams, @Nullable Duration timeout,
|
||||||
List<PublicKeyCredentialDescriptor> excludeCredentials,
|
List<PublicKeyCredentialDescriptor> excludeCredentials,
|
||||||
AuthenticatorSelectionCriteria authenticatorSelection, AttestationConveyancePreference attestation,
|
AuthenticatorSelectionCriteria authenticatorSelection,
|
||||||
AuthenticationExtensionsClientInputs extensions) {
|
@Nullable AttestationConveyancePreference attestation,
|
||||||
|
@Nullable AuthenticationExtensionsClientInputs extensions) {
|
||||||
this.rp = rp;
|
this.rp = rp;
|
||||||
this.user = user;
|
this.user = user;
|
||||||
this.challenge = challenge;
|
this.challenge = challenge;
|
||||||
@ -117,7 +120,7 @@ public final class PublicKeyCredentialCreationOptions {
|
|||||||
* wait for the call to complete.
|
* wait for the call to complete.
|
||||||
* @return the timeout
|
* @return the timeout
|
||||||
*/
|
*/
|
||||||
public Duration getTimeout() {
|
public @Nullable Duration getTimeout() {
|
||||||
return this.timeout;
|
return this.timeout;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -150,7 +153,7 @@ public final class PublicKeyCredentialCreationOptions {
|
|||||||
* regarding attestation conveyance.
|
* regarding attestation conveyance.
|
||||||
* @return the attestation preference
|
* @return the attestation preference
|
||||||
*/
|
*/
|
||||||
public AttestationConveyancePreference getAttestation() {
|
public @Nullable AttestationConveyancePreference getAttestation() {
|
||||||
return this.attestation;
|
return this.attestation;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -161,7 +164,7 @@ public final class PublicKeyCredentialCreationOptions {
|
|||||||
* extension inputs requesting additional processing by the client and authenticator.
|
* extension inputs requesting additional processing by the client and authenticator.
|
||||||
* @return the extensions
|
* @return the extensions
|
||||||
*/
|
*/
|
||||||
public AuthenticationExtensionsClientInputs getExtensions() {
|
public @Nullable AuthenticationExtensionsClientInputs getExtensions() {
|
||||||
return this.extensions;
|
return this.extensions;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -181,23 +184,27 @@ public final class PublicKeyCredentialCreationOptions {
|
|||||||
*/
|
*/
|
||||||
public static final class PublicKeyCredentialCreationOptionsBuilder {
|
public static final class PublicKeyCredentialCreationOptionsBuilder {
|
||||||
|
|
||||||
|
@SuppressWarnings("NullAway.Init")
|
||||||
private PublicKeyCredentialRpEntity rp;
|
private PublicKeyCredentialRpEntity rp;
|
||||||
|
|
||||||
|
@SuppressWarnings("NullAway.Init")
|
||||||
private PublicKeyCredentialUserEntity user;
|
private PublicKeyCredentialUserEntity user;
|
||||||
|
|
||||||
|
@SuppressWarnings("NullAway.Init")
|
||||||
private Bytes challenge;
|
private Bytes challenge;
|
||||||
|
|
||||||
private List<PublicKeyCredentialParameters> pubKeyCredParams = new ArrayList<>();
|
private List<PublicKeyCredentialParameters> pubKeyCredParams = new ArrayList<>();
|
||||||
|
|
||||||
private Duration timeout;
|
private @Nullable Duration timeout;
|
||||||
|
|
||||||
private List<PublicKeyCredentialDescriptor> excludeCredentials = new ArrayList<>();
|
private List<PublicKeyCredentialDescriptor> excludeCredentials = new ArrayList<>();
|
||||||
|
|
||||||
|
@SuppressWarnings("NullAway.Init")
|
||||||
private AuthenticatorSelectionCriteria authenticatorSelection;
|
private AuthenticatorSelectionCriteria authenticatorSelection;
|
||||||
|
|
||||||
private AttestationConveyancePreference attestation;
|
private @Nullable AttestationConveyancePreference attestation;
|
||||||
|
|
||||||
private AuthenticationExtensionsClientInputs extensions;
|
private @Nullable AuthenticationExtensionsClientInputs extensions;
|
||||||
|
|
||||||
private PublicKeyCredentialCreationOptionsBuilder() {
|
private PublicKeyCredentialCreationOptionsBuilder() {
|
||||||
}
|
}
|
||||||
|
@ -20,6 +20,8 @@ import java.io.Serial;
|
|||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
|
import org.jspecify.annotations.Nullable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <a href=
|
* <a href=
|
||||||
* "https://www.w3.org/TR/webauthn-3/#dictdef-publickeycredentialdescriptor">PublicKeyCredentialDescriptor</a>
|
* "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 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,
|
private PublicKeyCredentialDescriptor(PublicKeyCredentialType type, @Nullable Bytes id,
|
||||||
Set<AuthenticatorTransport> transports) {
|
@Nullable Set<AuthenticatorTransport> transports) {
|
||||||
this.type = type;
|
this.type = type;
|
||||||
this.id = id;
|
this.id = id;
|
||||||
this.transports = transports;
|
this.transports = transports;
|
||||||
@ -66,7 +68,7 @@ public final class PublicKeyCredentialDescriptor implements Serializable {
|
|||||||
* referring to.
|
* referring to.
|
||||||
* @return the id
|
* @return the id
|
||||||
*/
|
*/
|
||||||
public Bytes getId() {
|
public @Nullable Bytes getId() {
|
||||||
return this.id;
|
return this.id;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -78,7 +80,7 @@ public final class PublicKeyCredentialDescriptor implements Serializable {
|
|||||||
* is referring to.
|
* is referring to.
|
||||||
* @return the transports
|
* @return the transports
|
||||||
*/
|
*/
|
||||||
public Set<AuthenticatorTransport> getTransports() {
|
public @Nullable Set<AuthenticatorTransport> getTransports() {
|
||||||
return this.transports;
|
return this.transports;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -100,9 +102,9 @@ public final class PublicKeyCredentialDescriptor implements Serializable {
|
|||||||
|
|
||||||
private PublicKeyCredentialType type = PublicKeyCredentialType.PUBLIC_KEY;
|
private PublicKeyCredentialType type = PublicKeyCredentialType.PUBLIC_KEY;
|
||||||
|
|
||||||
private Bytes id;
|
private @Nullable Bytes id;
|
||||||
|
|
||||||
private Set<AuthenticatorTransport> transports;
|
private @Nullable Set<AuthenticatorTransport> transports;
|
||||||
|
|
||||||
private PublicKeyCredentialDescriptorBuilder() {
|
private PublicKeyCredentialDescriptorBuilder() {
|
||||||
}
|
}
|
||||||
|
@ -24,6 +24,8 @@ import java.util.Collections;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
import org.jspecify.annotations.Nullable;
|
||||||
|
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -43,17 +45,17 @@ public final class PublicKeyCredentialRequestOptions implements Serializable {
|
|||||||
|
|
||||||
private final Duration timeout;
|
private final Duration timeout;
|
||||||
|
|
||||||
private final String rpId;
|
private final @Nullable String rpId;
|
||||||
|
|
||||||
private final List<PublicKeyCredentialDescriptor> allowCredentials;
|
private final List<PublicKeyCredentialDescriptor> allowCredentials;
|
||||||
|
|
||||||
private final UserVerificationRequirement userVerification;
|
private final @Nullable UserVerificationRequirement userVerification;
|
||||||
|
|
||||||
private final AuthenticationExtensionsClientInputs extensions;
|
private final AuthenticationExtensionsClientInputs extensions;
|
||||||
|
|
||||||
private PublicKeyCredentialRequestOptions(Bytes challenge, Duration timeout, String rpId,
|
private PublicKeyCredentialRequestOptions(Bytes challenge, Duration timeout, @Nullable String rpId,
|
||||||
List<PublicKeyCredentialDescriptor> allowCredentials, UserVerificationRequirement userVerification,
|
List<PublicKeyCredentialDescriptor> allowCredentials,
|
||||||
AuthenticationExtensionsClientInputs extensions) {
|
@Nullable UserVerificationRequirement userVerification, AuthenticationExtensionsClientInputs extensions) {
|
||||||
Assert.notNull(challenge, "challenge cannot be null");
|
Assert.notNull(challenge, "challenge cannot be null");
|
||||||
Assert.hasText(rpId, "rpId cannot be empty");
|
Assert.hasText(rpId, "rpId cannot be empty");
|
||||||
this.challenge = challenge;
|
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.
|
* MUST verify that the Relying Party's origin matches the scope of this RP ID.
|
||||||
* @return the relying party id
|
* @return the relying party id
|
||||||
*/
|
*/
|
||||||
public String getRpId() {
|
public @Nullable String getRpId() {
|
||||||
return this.rpId;
|
return this.rpId;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -115,7 +117,7 @@ public final class PublicKeyCredentialRequestOptions implements Serializable {
|
|||||||
* user verification for the get() operation.
|
* user verification for the get() operation.
|
||||||
* @return the user verification
|
* @return the user verification
|
||||||
*/
|
*/
|
||||||
public UserVerificationRequirement getUserVerification() {
|
public @Nullable UserVerificationRequirement getUserVerification() {
|
||||||
return this.userVerification;
|
return this.userVerification;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -146,15 +148,15 @@ public final class PublicKeyCredentialRequestOptions implements Serializable {
|
|||||||
*/
|
*/
|
||||||
public static final class PublicKeyCredentialRequestOptionsBuilder {
|
public static final class PublicKeyCredentialRequestOptionsBuilder {
|
||||||
|
|
||||||
private Bytes challenge;
|
private @Nullable Bytes challenge;
|
||||||
|
|
||||||
private Duration timeout = Duration.ofMinutes(5);
|
private Duration timeout = Duration.ofMinutes(5);
|
||||||
|
|
||||||
private String rpId;
|
private @Nullable String rpId;
|
||||||
|
|
||||||
private List<PublicKeyCredentialDescriptor> allowCredentials = Collections.emptyList();
|
private List<PublicKeyCredentialDescriptor> allowCredentials = Collections.emptyList();
|
||||||
|
|
||||||
private UserVerificationRequirement userVerification;
|
private @Nullable UserVerificationRequirement userVerification;
|
||||||
|
|
||||||
private AuthenticationExtensionsClientInputs extensions = new ImmutableAuthenticationExtensionsClientInputs(
|
private AuthenticationExtensionsClientInputs extensions = new ImmutableAuthenticationExtensionsClientInputs(
|
||||||
new ArrayList<>());
|
new ArrayList<>());
|
||||||
|
@ -16,6 +16,10 @@
|
|||||||
|
|
||||||
package org.springframework.security.web.webauthn.api;
|
package org.springframework.security.web.webauthn.api;
|
||||||
|
|
||||||
|
import org.jspecify.annotations.Nullable;
|
||||||
|
|
||||||
|
import org.springframework.util.Assert;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The <a href=
|
* The <a href=
|
||||||
* "https://www.w3.org/TR/webauthn-3/#dictdef-publickeycredentialrpentity">PublicKeyCredentialRpEntity</a>
|
* "https://www.w3.org/TR/webauthn-3/#dictdef-publickeycredentialrpentity">PublicKeyCredentialRpEntity</a>
|
||||||
@ -75,9 +79,9 @@ public final class PublicKeyCredentialRpEntity {
|
|||||||
*/
|
*/
|
||||||
public static final class PublicKeyCredentialRpEntityBuilder {
|
public static final class PublicKeyCredentialRpEntityBuilder {
|
||||||
|
|
||||||
private String name;
|
private @Nullable String name;
|
||||||
|
|
||||||
private String id;
|
private @Nullable String id;
|
||||||
|
|
||||||
private PublicKeyCredentialRpEntityBuilder() {
|
private PublicKeyCredentialRpEntityBuilder() {
|
||||||
}
|
}
|
||||||
@ -107,6 +111,8 @@ public final class PublicKeyCredentialRpEntity {
|
|||||||
* @return a new {@link PublicKeyCredentialRpEntity}.
|
* @return a new {@link PublicKeyCredentialRpEntity}.
|
||||||
*/
|
*/
|
||||||
public PublicKeyCredentialRpEntity build() {
|
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);
|
return new PublicKeyCredentialRpEntity(this.name, this.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -18,6 +18,8 @@ package org.springframework.security.web.webauthn.api;
|
|||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
import org.jspecify.annotations.Nullable;
|
||||||
|
|
||||||
import org.springframework.security.web.webauthn.management.RelyingPartyAuthenticationRequest;
|
import org.springframework.security.web.webauthn.management.RelyingPartyAuthenticationRequest;
|
||||||
import org.springframework.security.web.webauthn.management.WebAuthnRelyingPartyOperations;
|
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.
|
* is a human-palatable name for the user account, intended only for display.
|
||||||
* @return the display name
|
* @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.HttpServletRequest;
|
||||||
import jakarta.servlet.http.HttpServletResponse;
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
import jakarta.servlet.http.HttpSession;
|
import jakarta.servlet.http.HttpSession;
|
||||||
|
import org.jspecify.annotations.Nullable;
|
||||||
|
|
||||||
import org.springframework.security.web.webauthn.api.PublicKeyCredentialRequestOptions;
|
import org.springframework.security.web.webauthn.api.PublicKeyCredentialRequestOptions;
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
@ -41,13 +42,13 @@ public class HttpSessionPublicKeyCredentialRequestOptionsRepository
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void save(HttpServletRequest request, HttpServletResponse response,
|
public void save(HttpServletRequest request, HttpServletResponse response,
|
||||||
PublicKeyCredentialRequestOptions options) {
|
@Nullable PublicKeyCredentialRequestOptions options) {
|
||||||
HttpSession session = request.getSession();
|
HttpSession session = request.getSession();
|
||||||
session.setAttribute(this.attrName, options);
|
session.setAttribute(this.attrName, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public PublicKeyCredentialRequestOptions load(HttpServletRequest request) {
|
public @Nullable PublicKeyCredentialRequestOptions load(HttpServletRequest request) {
|
||||||
HttpSession session = request.getSession(false);
|
HttpSession session = request.getSession(false);
|
||||||
if (session == null) {
|
if (session == null) {
|
||||||
return null;
|
return null;
|
||||||
|
@ -18,6 +18,7 @@ package org.springframework.security.web.webauthn.authentication;
|
|||||||
|
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
import jakarta.servlet.http.HttpServletResponse;
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
|
import org.jspecify.annotations.Nullable;
|
||||||
|
|
||||||
import org.springframework.security.web.webauthn.api.PublicKeyCredentialRequestOptions;
|
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
|
* @param options the {@link PublicKeyCredentialRequestOptions} to save or null if an
|
||||||
* existing {@link PublicKeyCredentialRequestOptions} should be removed.
|
* 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
|
* Gets a saved {@link PublicKeyCredentialRequestOptions} if it exists, otherwise
|
||||||
@ -47,6 +48,6 @@ public interface PublicKeyCredentialRequestOptionsRepository {
|
|||||||
* @return the {@link PublicKeyCredentialRequestOptions} that was saved, otherwise
|
* @return the {@link PublicKeyCredentialRequestOptions} that was saved, otherwise
|
||||||
* null.
|
* 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.io.Serial;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
|
||||||
|
import org.jspecify.annotations.Nullable;
|
||||||
|
|
||||||
import org.springframework.security.authentication.AbstractAuthenticationToken;
|
import org.springframework.security.authentication.AbstractAuthenticationToken;
|
||||||
import org.springframework.security.core.GrantedAuthority;
|
import org.springframework.security.core.GrantedAuthority;
|
||||||
import org.springframework.security.web.webauthn.api.PublicKeyCredentialUserEntity;
|
import org.springframework.security.web.webauthn.api.PublicKeyCredentialUserEntity;
|
||||||
@ -53,7 +55,7 @@ public class WebAuthnAuthentication extends AbstractAuthenticationToken {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object getCredentials() {
|
public @Nullable Object getCredentials() {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -18,6 +18,8 @@ package org.springframework.security.web.webauthn.authentication;
|
|||||||
|
|
||||||
import java.io.Serial;
|
import java.io.Serial;
|
||||||
|
|
||||||
|
import org.jspecify.annotations.Nullable;
|
||||||
|
|
||||||
import org.springframework.security.authentication.AbstractAuthenticationToken;
|
import org.springframework.security.authentication.AbstractAuthenticationToken;
|
||||||
import org.springframework.security.core.authority.AuthorityUtils;
|
import org.springframework.security.core.authority.AuthorityUtils;
|
||||||
import org.springframework.security.web.webauthn.management.RelyingPartyAuthenticationRequest;
|
import org.springframework.security.web.webauthn.management.RelyingPartyAuthenticationRequest;
|
||||||
@ -68,7 +70,7 @@ public class WebAuthnAuthenticationRequestToken extends AbstractAuthenticationTo
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object getPrincipal() {
|
public @Nullable Object getPrincipal() {
|
||||||
return this.webAuthnRequest.getPublicKey().getResponse().getUserHandle();
|
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.core.JsonParser;
|
||||||
import com.fasterxml.jackson.databind.DeserializationContext;
|
import com.fasterxml.jackson.databind.DeserializationContext;
|
||||||
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
|
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
|
||||||
|
import org.jspecify.annotations.Nullable;
|
||||||
|
|
||||||
import org.springframework.security.web.webauthn.api.AuthenticatorAttachment;
|
import org.springframework.security.web.webauthn.api.AuthenticatorAttachment;
|
||||||
|
|
||||||
@ -39,7 +40,7 @@ class AuthenticatorAttachmentDeserializer extends StdDeserializer<AuthenticatorA
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public AuthenticatorAttachment deserialize(JsonParser parser, DeserializationContext ctxt)
|
public @Nullable AuthenticatorAttachment deserialize(JsonParser parser, DeserializationContext ctxt)
|
||||||
throws IOException, JacksonException {
|
throws IOException, JacksonException {
|
||||||
String type = parser.readValueAs(String.class);
|
String type = parser.readValueAs(String.class);
|
||||||
for (AuthenticatorAttachment publicKeyCredentialType : AuthenticatorAttachment.values()) {
|
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.core.JsonParser;
|
||||||
import com.fasterxml.jackson.databind.DeserializationContext;
|
import com.fasterxml.jackson.databind.DeserializationContext;
|
||||||
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
|
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
|
||||||
|
import org.jspecify.annotations.Nullable;
|
||||||
|
|
||||||
import org.springframework.security.web.webauthn.api.AuthenticatorTransport;
|
import org.springframework.security.web.webauthn.api.AuthenticatorTransport;
|
||||||
|
|
||||||
@ -39,7 +40,7 @@ class AuthenticatorTransportDeserializer extends StdDeserializer<AuthenticatorTr
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public AuthenticatorTransport deserialize(JsonParser parser, DeserializationContext ctxt)
|
public @Nullable AuthenticatorTransport deserialize(JsonParser parser, DeserializationContext ctxt)
|
||||||
throws IOException, JacksonException {
|
throws IOException, JacksonException {
|
||||||
String transportValue = parser.readValueAs(String.class);
|
String transportValue = parser.readValueAs(String.class);
|
||||||
for (AuthenticatorTransport transport : AuthenticatorTransport.values()) {
|
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.core.JsonParser;
|
||||||
import com.fasterxml.jackson.databind.DeserializationContext;
|
import com.fasterxml.jackson.databind.DeserializationContext;
|
||||||
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
|
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
|
||||||
|
import org.jspecify.annotations.Nullable;
|
||||||
|
|
||||||
import org.springframework.security.web.webauthn.api.COSEAlgorithmIdentifier;
|
import org.springframework.security.web.webauthn.api.COSEAlgorithmIdentifier;
|
||||||
|
|
||||||
@ -39,7 +40,7 @@ class COSEAlgorithmIdentifierDeserializer extends StdDeserializer<COSEAlgorithmI
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public COSEAlgorithmIdentifier deserialize(JsonParser parser, DeserializationContext ctxt)
|
public @Nullable COSEAlgorithmIdentifier deserialize(JsonParser parser, DeserializationContext ctxt)
|
||||||
throws IOException, JacksonException {
|
throws IOException, JacksonException {
|
||||||
Long transportValue = parser.readValueAs(Long.class);
|
Long transportValue = parser.readValueAs(Long.class);
|
||||||
for (COSEAlgorithmIdentifier identifier : COSEAlgorithmIdentifier.values()) {
|
for (COSEAlgorithmIdentifier identifier : COSEAlgorithmIdentifier.values()) {
|
||||||
|
@ -20,6 +20,7 @@ import java.time.Duration;
|
|||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||||
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
||||||
|
import org.jspecify.annotations.Nullable;
|
||||||
|
|
||||||
import org.springframework.security.web.webauthn.api.PublicKeyCredentialCreationOptions;
|
import org.springframework.security.web.webauthn.api.PublicKeyCredentialCreationOptions;
|
||||||
|
|
||||||
@ -33,6 +34,6 @@ import org.springframework.security.web.webauthn.api.PublicKeyCredentialCreation
|
|||||||
abstract class PublicKeyCredentialCreationOptionsMixin {
|
abstract class PublicKeyCredentialCreationOptionsMixin {
|
||||||
|
|
||||||
@JsonSerialize(using = DurationSerializer.class)
|
@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.annotation.JsonInclude;
|
||||||
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
||||||
|
import org.jspecify.annotations.Nullable;
|
||||||
|
|
||||||
import org.springframework.security.web.webauthn.api.PublicKeyCredentialRequestOptions;
|
import org.springframework.security.web.webauthn.api.PublicKeyCredentialRequestOptions;
|
||||||
|
|
||||||
@ -33,6 +34,6 @@ import org.springframework.security.web.webauthn.api.PublicKeyCredentialRequestO
|
|||||||
class PublicKeyCredentialRequestOptionsMixin {
|
class PublicKeyCredentialRequestOptionsMixin {
|
||||||
|
|
||||||
@JsonSerialize(using = DurationSerializer.class)
|
@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;
|
package org.springframework.security.web.webauthn.management;
|
||||||
|
|
||||||
|
import org.jspecify.annotations.Nullable;
|
||||||
|
|
||||||
import org.springframework.security.core.Authentication;
|
import org.springframework.security.core.Authentication;
|
||||||
|
|
||||||
public class ImmutablePublicKeyCredentialRequestOptionsRequest implements PublicKeyCredentialRequestOptionsRequest {
|
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;
|
this.authentication = authentication;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Authentication getAuthentication() {
|
public @Nullable Authentication getAuthentication() {
|
||||||
return this.authentication;
|
return this.authentication;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -16,6 +16,8 @@
|
|||||||
|
|
||||||
package org.springframework.security.web.webauthn.management;
|
package org.springframework.security.web.webauthn.management;
|
||||||
|
|
||||||
|
import org.jspecify.annotations.Nullable;
|
||||||
|
|
||||||
import org.springframework.security.web.webauthn.api.PublicKeyCredentialCreationOptions;
|
import org.springframework.security.web.webauthn.api.PublicKeyCredentialCreationOptions;
|
||||||
import org.springframework.util.Assert;
|
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.
|
* @param publicKey this is submitted by the client and if validated stored.
|
||||||
*/
|
*/
|
||||||
public ImmutableRelyingPartyRegistrationRequest(PublicKeyCredentialCreationOptions options,
|
public ImmutableRelyingPartyRegistrationRequest(PublicKeyCredentialCreationOptions options,
|
||||||
RelyingPartyPublicKey publicKey) {
|
@Nullable RelyingPartyPublicKey publicKey) {
|
||||||
Assert.notNull(options, "options cannot be null");
|
Assert.notNull(options, "options cannot be null");
|
||||||
Assert.notNull(publicKey, "publicKey cannot be null");
|
Assert.notNull(publicKey, "publicKey cannot be null");
|
||||||
this.options = options;
|
this.options = options;
|
||||||
|
@ -23,6 +23,8 @@ import java.util.ArrayList;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
import org.jspecify.annotations.Nullable;
|
||||||
|
|
||||||
import org.springframework.jdbc.core.ArgumentPreparedStatementSetter;
|
import org.springframework.jdbc.core.ArgumentPreparedStatementSetter;
|
||||||
import org.springframework.jdbc.core.JdbcOperations;
|
import org.springframework.jdbc.core.JdbcOperations;
|
||||||
import org.springframework.jdbc.core.PreparedStatementSetter;
|
import org.springframework.jdbc.core.PreparedStatementSetter;
|
||||||
@ -105,7 +107,7 @@ public final class JdbcPublicKeyCredentialUserEntityRepository implements Public
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public PublicKeyCredentialUserEntity findById(Bytes id) {
|
public @Nullable PublicKeyCredentialUserEntity findById(Bytes id) {
|
||||||
Assert.notNull(id, "id cannot be null");
|
Assert.notNull(id, "id cannot be null");
|
||||||
List<PublicKeyCredentialUserEntity> result = this.jdbcOperations.query(FIND_USER_BY_ID_SQL,
|
List<PublicKeyCredentialUserEntity> result = this.jdbcOperations.query(FIND_USER_BY_ID_SQL,
|
||||||
this.userEntityRowMapper, id.toBase64UrlString());
|
this.userEntityRowMapper, id.toBase64UrlString());
|
||||||
@ -113,7 +115,7 @@ public final class JdbcPublicKeyCredentialUserEntityRepository implements Public
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public PublicKeyCredentialUserEntity findByUsername(String username) {
|
public @Nullable PublicKeyCredentialUserEntity findByUsername(String username) {
|
||||||
Assert.hasText(username, "name cannot be null or empty");
|
Assert.hasText(username, "name cannot be null or empty");
|
||||||
List<PublicKeyCredentialUserEntity> result = this.jdbcOperations.query(FIND_USER_BY_NAME_SQL,
|
List<PublicKeyCredentialUserEntity> result = this.jdbcOperations.query(FIND_USER_BY_NAME_SQL,
|
||||||
this.userEntityRowMapper, username);
|
this.userEntityRowMapper, username);
|
||||||
|
@ -28,6 +28,8 @@ import java.util.List;
|
|||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
import org.jspecify.annotations.Nullable;
|
||||||
|
|
||||||
import org.springframework.jdbc.core.ArgumentPreparedStatementSetter;
|
import org.springframework.jdbc.core.ArgumentPreparedStatementSetter;
|
||||||
import org.springframework.jdbc.core.JdbcOperations;
|
import org.springframework.jdbc.core.JdbcOperations;
|
||||||
import org.springframework.jdbc.core.PreparedStatementSetter;
|
import org.springframework.jdbc.core.PreparedStatementSetter;
|
||||||
@ -171,7 +173,7 @@ public final class JdbcUserCredentialRepository implements UserCredentialReposit
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CredentialRecord findByCredentialId(Bytes credentialId) {
|
public @Nullable CredentialRecord findByCredentialId(Bytes credentialId) {
|
||||||
Assert.notNull(credentialId, "credentialId cannot be null");
|
Assert.notNull(credentialId, "credentialId cannot be null");
|
||||||
List<CredentialRecord> result = this.jdbcOperations.query(FIND_CREDENTIAL_RECORD_BY_ID_SQL,
|
List<CredentialRecord> result = this.jdbcOperations.query(FIND_CREDENTIAL_RECORD_BY_ID_SQL,
|
||||||
this.credentialRecordRowMapper, credentialId.toBase64UrlString());
|
this.credentialRecordRowMapper, credentialId.toBase64UrlString());
|
||||||
@ -238,7 +240,7 @@ public final class JdbcUserCredentialRepository implements UserCredentialReposit
|
|||||||
return parameters;
|
return parameters;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Timestamp fromInstant(Instant instant) {
|
private @Nullable Timestamp fromInstant(Instant instant) {
|
||||||
if (instant == null) {
|
if (instant == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -249,13 +251,13 @@ public final class JdbcUserCredentialRepository implements UserCredentialReposit
|
|||||||
|
|
||||||
private interface SetBytes {
|
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 {
|
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
|
@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 (argValue instanceof SqlParameterValue paramValue) {
|
||||||
if (paramValue.getSqlType() == Types.BLOB) {
|
if (paramValue.getSqlType() == Types.BLOB) {
|
||||||
if (paramValue.getValue() != null) {
|
if (paramValue.getValue() != null) {
|
||||||
@ -327,6 +330,8 @@ public final class JdbcUserCredentialRepository implements UserCredentialReposit
|
|||||||
for (String transport : transports) {
|
for (String transport : transports) {
|
||||||
authenticatorTransports.add(AuthenticatorTransport.valueOf(transport));
|
authenticatorTransports.add(AuthenticatorTransport.valueOf(transport));
|
||||||
}
|
}
|
||||||
|
Assert.notNull(lastUsed, "last_used cannot be null");
|
||||||
|
Assert.notNull(created, "created cannot be null");
|
||||||
return ImmutableCredentialRecord.builder()
|
return ImmutableCredentialRecord.builder()
|
||||||
.credentialId(credentialId)
|
.credentialId(credentialId)
|
||||||
.userEntityUserId(userEntityUserId)
|
.userEntityUserId(userEntityUserId)
|
||||||
@ -345,7 +350,7 @@ public final class JdbcUserCredentialRepository implements UserCredentialReposit
|
|||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
private Instant fromTimestamp(Timestamp timestamp) {
|
private @Nullable Instant fromTimestamp(Timestamp timestamp) {
|
||||||
if (timestamp == null) {
|
if (timestamp == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -19,6 +19,8 @@ package org.springframework.security.web.webauthn.management;
|
|||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.jspecify.annotations.Nullable;
|
||||||
|
|
||||||
import org.springframework.security.web.webauthn.api.Bytes;
|
import org.springframework.security.web.webauthn.api.Bytes;
|
||||||
import org.springframework.security.web.webauthn.api.PublicKeyCredentialUserEntity;
|
import org.springframework.security.web.webauthn.api.PublicKeyCredentialUserEntity;
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
@ -36,13 +38,13 @@ public class MapPublicKeyCredentialUserEntityRepository implements PublicKeyCred
|
|||||||
private final Map<Bytes, PublicKeyCredentialUserEntity> idToUserEntity = new HashMap<>();
|
private final Map<Bytes, PublicKeyCredentialUserEntity> idToUserEntity = new HashMap<>();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public PublicKeyCredentialUserEntity findById(Bytes id) {
|
public @Nullable PublicKeyCredentialUserEntity findById(Bytes id) {
|
||||||
Assert.notNull(id, "id cannot be null");
|
Assert.notNull(id, "id cannot be null");
|
||||||
return this.idToUserEntity.get(id);
|
return this.idToUserEntity.get(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public PublicKeyCredentialUserEntity findByUsername(String username) {
|
public @Nullable PublicKeyCredentialUserEntity findByUsername(String username) {
|
||||||
Assert.notNull(username, "username cannot be null");
|
Assert.notNull(username, "username cannot be null");
|
||||||
return this.usernameToUserEntity.get(username);
|
return this.usernameToUserEntity.get(username);
|
||||||
}
|
}
|
||||||
|
@ -24,6 +24,8 @@ import java.util.Map;
|
|||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.stream.Collectors;
|
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.Bytes;
|
||||||
import org.springframework.security.web.webauthn.api.CredentialRecord;
|
import org.springframework.security.web.webauthn.api.CredentialRecord;
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
@ -62,7 +64,7 @@ public class MapUserCredentialRepository implements UserCredentialRepository {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CredentialRecord findByCredentialId(Bytes credentialId) {
|
public @Nullable CredentialRecord findByCredentialId(Bytes credentialId) {
|
||||||
Assert.notNull(credentialId, "credentialId cannot be null");
|
Assert.notNull(credentialId, "credentialId cannot be null");
|
||||||
return this.credentialIdToUserCredential.get(credentialId);
|
return this.credentialIdToUserCredential.get(credentialId);
|
||||||
}
|
}
|
||||||
|
@ -16,6 +16,8 @@
|
|||||||
|
|
||||||
package org.springframework.security.web.webauthn.management;
|
package org.springframework.security.web.webauthn.management;
|
||||||
|
|
||||||
|
import org.jspecify.annotations.Nullable;
|
||||||
|
|
||||||
import org.springframework.security.core.Authentication;
|
import org.springframework.security.core.Authentication;
|
||||||
|
|
||||||
public interface PublicKeyCredentialRequestOptionsRequest {
|
public interface PublicKeyCredentialRequestOptionsRequest {
|
||||||
@ -24,6 +26,6 @@ public interface PublicKeyCredentialRequestOptionsRequest {
|
|||||||
* The current {@link Authentication}. Possibly null or an anonymous.
|
* The current {@link Authentication}. Possibly null or an anonymous.
|
||||||
* @return the current {@link Authentication}
|
* @return the current {@link Authentication}
|
||||||
*/
|
*/
|
||||||
Authentication getAuthentication();
|
@Nullable Authentication getAuthentication();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -16,6 +16,8 @@
|
|||||||
|
|
||||||
package org.springframework.security.web.webauthn.management;
|
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.Bytes;
|
||||||
import org.springframework.security.web.webauthn.api.PublicKeyCredentialUserEntity;
|
import org.springframework.security.web.webauthn.api.PublicKeyCredentialUserEntity;
|
||||||
|
|
||||||
@ -33,14 +35,14 @@ public interface PublicKeyCredentialUserEntityRepository {
|
|||||||
* @param id the id to lookup the username by
|
* @param id the id to lookup the username by
|
||||||
* @return the username or null if not found.
|
* @return the username or null if not found.
|
||||||
*/
|
*/
|
||||||
PublicKeyCredentialUserEntity findById(Bytes id);
|
@Nullable PublicKeyCredentialUserEntity findById(Bytes id);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Finds the {@link PublicKeyCredentialUserEntity} by the username.
|
* Finds the {@link PublicKeyCredentialUserEntity} by the username.
|
||||||
* @param username the username to lookup the {@link PublicKeyCredentialUserEntity}
|
* @param username the username to lookup the {@link PublicKeyCredentialUserEntity}
|
||||||
* @return the {@link PublicKeyCredentialUserEntity} or null if not found.
|
* @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.
|
* Saves the {@link PublicKeyCredentialUserEntity} to the associated username.
|
||||||
|
@ -16,6 +16,8 @@
|
|||||||
|
|
||||||
package org.springframework.security.web.webauthn.management;
|
package org.springframework.security.web.webauthn.management;
|
||||||
|
|
||||||
|
import org.jspecify.annotations.Nullable;
|
||||||
|
|
||||||
import org.springframework.security.web.webauthn.api.PublicKeyCredentialCreationOptions;
|
import org.springframework.security.web.webauthn.api.PublicKeyCredentialCreationOptions;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -18,6 +18,8 @@ package org.springframework.security.web.webauthn.management;
|
|||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.jspecify.annotations.Nullable;
|
||||||
|
|
||||||
import org.springframework.security.web.webauthn.api.Bytes;
|
import org.springframework.security.web.webauthn.api.Bytes;
|
||||||
import org.springframework.security.web.webauthn.api.CredentialRecord;
|
import org.springframework.security.web.webauthn.api.CredentialRecord;
|
||||||
import org.springframework.security.web.webauthn.api.PublicKeyCredentialUserEntity;
|
import org.springframework.security.web.webauthn.api.PublicKeyCredentialUserEntity;
|
||||||
@ -47,7 +49,7 @@ public interface UserCredentialRepository {
|
|||||||
* @param credentialId {@link CredentialRecord#getCredentialId()}
|
* @param credentialId {@link CredentialRecord#getCredentialId()}
|
||||||
* @return the {@link CredentialRecord} or null if not found.
|
* @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.
|
* Finds all {@link CredentialRecord} instances for a specific user.
|
||||||
|
@ -16,6 +16,8 @@
|
|||||||
|
|
||||||
package org.springframework.security.web.webauthn.management;
|
package org.springframework.security.web.webauthn.management;
|
||||||
|
|
||||||
|
import org.jspecify.annotations.Nullable;
|
||||||
|
|
||||||
import org.springframework.security.core.Authentication;
|
import org.springframework.security.core.Authentication;
|
||||||
import org.springframework.security.web.webauthn.api.CredentialRecord;
|
import org.springframework.security.web.webauthn.api.CredentialRecord;
|
||||||
import org.springframework.security.web.webauthn.api.PublicKeyCredentialCreationOptions;
|
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.Origin;
|
||||||
import com.webauthn4j.data.client.challenge.Challenge;
|
import com.webauthn4j.data.client.challenge.Challenge;
|
||||||
import com.webauthn4j.data.client.challenge.DefaultChallenge;
|
import com.webauthn4j.data.client.challenge.DefaultChallenge;
|
||||||
|
import com.webauthn4j.data.extension.authenticator.AuthenticationExtensionAuthenticatorOutput;
|
||||||
import com.webauthn4j.data.extension.authenticator.RegistrationExtensionAuthenticatorOutput;
|
import com.webauthn4j.data.extension.authenticator.RegistrationExtensionAuthenticatorOutput;
|
||||||
import com.webauthn4j.server.ServerProperty;
|
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.AuthenticationTrustResolver;
|
||||||
import org.springframework.security.authentication.AuthenticationTrustResolverImpl;
|
import org.springframework.security.authentication.AuthenticationTrustResolverImpl;
|
||||||
@ -260,24 +263,28 @@ public class Webauthn4JRelyingPartyOperations implements WebAuthnRelyingPartyOpe
|
|||||||
transports);
|
transports);
|
||||||
RegistrationParameters registrationParameters = new RegistrationParameters(serverProperty, pubKeyCredParams,
|
RegistrationParameters registrationParameters = new RegistrationParameters(serverProperty, pubKeyCredParams,
|
||||||
userVerificationRequired, userPresenceRequired);
|
userVerificationRequired, userPresenceRequired);
|
||||||
RegistrationData registrationData = this.webAuthnManager.validate(webauthn4jRegistrationRequest,
|
RegistrationData wa4jRegistrationData = this.webAuthnManager.validate(webauthn4jRegistrationRequest,
|
||||||
registrationParameters);
|
registrationParameters);
|
||||||
AuthenticatorData<RegistrationExtensionAuthenticatorOutput> authData = registrationData.getAttestationObject()
|
AttestationObject wa4jAttestationObject = wa4jRegistrationData.getAttestationObject();
|
||||||
|
Assert.notNull(wa4jAttestationObject, "attestationObject cannot be null");
|
||||||
|
AuthenticatorData<RegistrationExtensionAuthenticatorOutput> wa4jAuthData = wa4jAttestationObject
|
||||||
.getAuthenticatorData();
|
.getAuthenticatorData();
|
||||||
|
|
||||||
CborConverter cborConverter = this.objectConverter.getCborConverter();
|
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);
|
byte[] rawCoseKey = cborConverter.writeValueAsBytes(coseKey);
|
||||||
ImmutableCredentialRecord userCredential = ImmutableCredentialRecord.builder()
|
ImmutableCredentialRecord userCredential = ImmutableCredentialRecord.builder()
|
||||||
.userEntityUserId(creationOptions.getUser().getId())
|
.userEntityUserId(creationOptions.getUser().getId())
|
||||||
.credentialType(credential.getType())
|
.credentialType(credential.getType())
|
||||||
.credentialId(credential.getRawId())
|
.credentialId(credential.getRawId())
|
||||||
.publicKey(new ImmutablePublicKeyCose(rawCoseKey))
|
.publicKey(new ImmutablePublicKeyCose(rawCoseKey))
|
||||||
.signatureCount(authData.getSignCount())
|
.signatureCount(wa4jAuthData.getSignCount())
|
||||||
.uvInitialized(authData.isFlagUV())
|
.uvInitialized(wa4jAuthData.isFlagUV())
|
||||||
.transports(convertTransports(registrationData.getTransports()))
|
.transports(convertTransports(wa4jRegistrationData.getTransports()))
|
||||||
.backupEligible(authData.isFlagBE())
|
.backupEligible(wa4jAuthData.isFlagBE())
|
||||||
.backupState(authData.isFlagBS())
|
.backupState(wa4jAuthData.isFlagBS())
|
||||||
.label(publicKey.getLabel())
|
.label(publicKey.getLabel())
|
||||||
.attestationClientDataJSON(credential.getResponse().getClientDataJSON())
|
.attestationClientDataJSON(credential.getResponse().getClientDataJSON())
|
||||||
.attestationObject(credential.getResponse().getAttestationObject())
|
.attestationObject(credential.getResponse().getAttestationObject())
|
||||||
@ -286,7 +293,7 @@ public class Webauthn4JRelyingPartyOperations implements WebAuthnRelyingPartyOpe
|
|||||||
return userCredential;
|
return userCredential;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Set<String> convertTransportsToString(AuthenticatorAttestationResponse response) {
|
private static @Nullable Set<String> convertTransportsToString(AuthenticatorAttestationResponse response) {
|
||||||
if (response.getTransports() == null) {
|
if (response.getTransports() == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -320,7 +327,7 @@ public class Webauthn4JRelyingPartyOperations implements WebAuthnRelyingPartyOpe
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static Set<AuthenticatorTransport> convertTransports(
|
private static Set<AuthenticatorTransport> convertTransports(
|
||||||
Set<com.webauthn4j.data.AuthenticatorTransport> transports) {
|
@Nullable Set<com.webauthn4j.data.AuthenticatorTransport> transports) {
|
||||||
if (transports == null) {
|
if (transports == null) {
|
||||||
return Collections.emptySet();
|
return Collections.emptySet();
|
||||||
}
|
}
|
||||||
@ -344,7 +351,8 @@ public class Webauthn4JRelyingPartyOperations implements WebAuthnRelyingPartyOpe
|
|||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<CredentialRecord> findCredentialRecords(Authentication authentication) {
|
@NullUnmarked
|
||||||
|
private List<CredentialRecord> findCredentialRecords(@Nullable Authentication authentication) {
|
||||||
if (!this.trustResolver.isAuthenticated(authentication)) {
|
if (!this.trustResolver.isAuthenticated(authentication)) {
|
||||||
return Collections.emptyList();
|
return Collections.emptyList();
|
||||||
}
|
}
|
||||||
@ -361,22 +369,30 @@ public class Webauthn4JRelyingPartyOperations implements WebAuthnRelyingPartyOpe
|
|||||||
AuthenticatorAssertionResponse assertionResponse = request.getPublicKey().getResponse();
|
AuthenticatorAssertionResponse assertionResponse = request.getPublicKey().getResponse();
|
||||||
Bytes keyId = request.getPublicKey().getRawId();
|
Bytes keyId = request.getPublicKey().getRawId();
|
||||||
CredentialRecord credentialRecord = this.userCredentials.findByCredentialId(keyId);
|
CredentialRecord credentialRecord = this.userCredentials.findByCredentialId(keyId);
|
||||||
|
if (credentialRecord == null) {
|
||||||
|
throw new IllegalArgumentException("Unable to find CredentialRecord with id " + keyId);
|
||||||
|
}
|
||||||
CborConverter cborConverter = this.objectConverter.getCborConverter();
|
CborConverter cborConverter = this.objectConverter.getCborConverter();
|
||||||
AttestationObject attestationObject = cborConverter
|
Bytes attestationObject = credentialRecord.getAttestationObject();
|
||||||
.readValue(credentialRecord.getAttestationObject().getBytes(), AttestationObject.class);
|
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();
|
Authenticator authenticator = new AuthenticatorImpl(data, wa4jAttestationObject.getAttestationStatement(),
|
||||||
AttestedCredentialData data = new AttestedCredentialData(authData.getAttestedCredentialData().getAaguid(),
|
|
||||||
keyId.getBytes(), authData.getAttestedCredentialData().getCOSEKey());
|
|
||||||
|
|
||||||
Authenticator authenticator = new AuthenticatorImpl(data, attestationObject.getAttestationStatement(),
|
|
||||||
credentialRecord.getSignatureCount());
|
credentialRecord.getSignatureCount());
|
||||||
Set<Origin> origins = toOrigins();
|
Set<Origin> origins = toOrigins();
|
||||||
Challenge challenge = new DefaultChallenge(requestOptions.getChallenge().getBytes());
|
Challenge challenge = new DefaultChallenge(requestOptions.getChallenge().getBytes());
|
||||||
// FIXME: should populate this
|
// FIXME: should populate this
|
||||||
byte[] tokenBindingId = null /* set tokenBindingId */;
|
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);
|
tokenBindingId);
|
||||||
boolean userVerificationRequired = request.getRequestOptions()
|
boolean userVerificationRequired = request.getRequestOptions()
|
||||||
.getUserVerification() == UserVerificationRequirement.REQUIRED;
|
.getUserVerification() == UserVerificationRequirement.REQUIRED;
|
||||||
@ -387,17 +403,24 @@ public class Webauthn4JRelyingPartyOperations implements WebAuthnRelyingPartyOpe
|
|||||||
AuthenticationParameters authenticationParameters = new AuthenticationParameters(serverProperty, authenticator,
|
AuthenticationParameters authenticationParameters = new AuthenticationParameters(serverProperty, authenticator,
|
||||||
userVerificationRequired);
|
userVerificationRequired);
|
||||||
|
|
||||||
AuthenticationData authenticationData = this.webAuthnManager.validate(authenticationRequest,
|
AuthenticationData wa4jAuthenticationData = this.webAuthnManager.validate(authenticationRequest,
|
||||||
authenticationParameters);
|
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)
|
ImmutableCredentialRecord updatedRecord = ImmutableCredentialRecord.fromCredentialRecord(credentialRecord)
|
||||||
.lastUsed(Instant.now())
|
.lastUsed(Instant.now())
|
||||||
.signatureCount(updatedSignCount)
|
.signatureCount(updatedSignCount)
|
||||||
.build();
|
.build();
|
||||||
this.userCredentials.save(updatedRecord);
|
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.HttpServletRequest;
|
||||||
import jakarta.servlet.http.HttpServletResponse;
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
import jakarta.servlet.http.HttpSession;
|
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.PublicKeyCredentialCreationOptions;
|
||||||
|
import org.springframework.security.web.webauthn.api.PublicKeyCredentialRequestOptions;
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
|
|
||||||
public class HttpSessionPublicKeyCredentialCreationOptionsRepository
|
public class HttpSessionPublicKeyCredentialCreationOptionsRepository
|
||||||
@ -32,11 +34,12 @@ public class HttpSessionPublicKeyCredentialCreationOptionsRepository
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void save(HttpServletRequest request, HttpServletResponse response,
|
public void save(HttpServletRequest request, HttpServletResponse response,
|
||||||
PublicKeyCredentialCreationOptions options) {
|
@Nullable PublicKeyCredentialCreationOptions options) {
|
||||||
request.getSession().setAttribute(this.attrName, options);
|
request.getSession().setAttribute(this.attrName, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
public PublicKeyCredentialCreationOptions load(HttpServletRequest request) {
|
|
||||||
|
public @Nullable PublicKeyCredentialCreationOptions load(HttpServletRequest request) {
|
||||||
HttpSession session = request.getSession(false);
|
HttpSession session = request.getSession(false);
|
||||||
if (session == null) {
|
if (session == null) {
|
||||||
return null;
|
return null;
|
||||||
|
@ -18,6 +18,7 @@ package org.springframework.security.web.webauthn.registration;
|
|||||||
|
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
import jakarta.servlet.http.HttpServletResponse;
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
|
import org.jspecify.annotations.Nullable;
|
||||||
|
|
||||||
import org.springframework.security.web.webauthn.api.PublicKeyCredentialCreationOptions;
|
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
|
* @param options the {@link PublicKeyCredentialCreationOptions} to save or null if an
|
||||||
* existing {@link PublicKeyCredentialCreationOptions} should be removed.
|
* 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
|
* Gets a saved {@link PublicKeyCredentialCreationOptions} if it exists, otherwise
|
||||||
@ -47,6 +48,6 @@ public interface PublicKeyCredentialCreationOptionsRepository {
|
|||||||
* @return the {@link PublicKeyCredentialCreationOptions} that was saved, otherwise
|
* @return the {@link PublicKeyCredentialCreationOptions} that was saved, otherwise
|
||||||
* null.
|
* 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 jakarta.servlet.http.HttpServletResponse;
|
||||||
import org.apache.commons.logging.Log;
|
import org.apache.commons.logging.Log;
|
||||||
import org.apache.commons.logging.LogFactory;
|
import org.apache.commons.logging.LogFactory;
|
||||||
|
import org.jspecify.annotations.Nullable;
|
||||||
|
|
||||||
import org.springframework.http.HttpInputMessage;
|
import org.springframework.http.HttpInputMessage;
|
||||||
import org.springframework.http.HttpMethod;
|
import org.springframework.http.HttpMethod;
|
||||||
@ -190,7 +191,7 @@ public class WebAuthnRegistrationFilter extends OncePerRequestFilter {
|
|||||||
this.converter.write(registrationResponse, MediaType.APPLICATION_JSON, outputMessage);
|
this.converter.write(registrationResponse, MediaType.APPLICATION_JSON, outputMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
private WebAuthnRegistrationRequest readRegistrationRequest(HttpServletRequest request) {
|
private @Nullable WebAuthnRegistrationRequest readRegistrationRequest(HttpServletRequest request) {
|
||||||
HttpInputMessage inputMessage = new ServletServerHttpRequest(request);
|
HttpInputMessage inputMessage = new ServletServerHttpRequest(request);
|
||||||
try {
|
try {
|
||||||
return (WebAuthnRegistrationRequest) this.converter.read(WebAuthnRegistrationRequest.class, inputMessage);
|
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 {
|
throws IOException {
|
||||||
this.userCredentials.delete(Bytes.fromBase64(id));
|
this.userCredentials.delete(Bytes.fromBase64(id));
|
||||||
response.setStatus(HttpStatus.NO_CONTENT.value());
|
response.setStatus(HttpStatus.NO_CONTENT.value());
|
||||||
@ -209,9 +210,9 @@ public class WebAuthnRegistrationFilter extends OncePerRequestFilter {
|
|||||||
|
|
||||||
static class WebAuthnRegistrationRequest {
|
static class WebAuthnRegistrationRequest {
|
||||||
|
|
||||||
private RelyingPartyPublicKey publicKey;
|
private @Nullable RelyingPartyPublicKey publicKey;
|
||||||
|
|
||||||
RelyingPartyPublicKey getPublicKey() {
|
@Nullable RelyingPartyPublicKey getPublicKey() {
|
||||||
return this.publicKey;
|
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