Extract spring-security-webauthn
Closes gh-17586
This commit is contained in:
-103
@@ -1,103 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2024 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.
|
||||
*/
|
||||
|
||||
package org.springframework.security.web.webauthn.api;
|
||||
|
||||
/**
|
||||
* <a href="https://www.w3.org/TR/webauthn-3/#webauthn-relying-party">WebAuthn Relying
|
||||
* Parties</a> may use <a href=
|
||||
* "https://www.w3.org/TR/webauthn-3/#enumdef-attestationconveyancepreference">AttestationConveyancePreference</a>
|
||||
* to specify their preference regarding attestation conveyance during credential
|
||||
* generation.
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 6.4
|
||||
*/
|
||||
public final class AttestationConveyancePreference {
|
||||
|
||||
/**
|
||||
* The <a href=
|
||||
* "https://www.w3.org/TR/webauthn-3/#dom-attestationconveyancepreference-none">none</a>
|
||||
* preference indicates that the Relying Party is not interested in
|
||||
* <a href="https://www.w3.org/TR/webauthn-3/#authenticator">authenticator</a>
|
||||
* <a href="https://www.w3.org/TR/webauthn-3/#attestation">attestation</a>.
|
||||
*/
|
||||
public static final AttestationConveyancePreference NONE = new AttestationConveyancePreference("none");
|
||||
|
||||
/**
|
||||
* The <a href=
|
||||
* "https://www.w3.org/TR/webauthn-3/#dom-attestationconveyancepreference-indirect">indirect</a>
|
||||
* preference indicates that the Relying Party wants to receive a verifiable
|
||||
* <a href="https://www.w3.org/TR/webauthn-3/#attestation-statement">attestation
|
||||
* statement</a>, but allows the client to decide how to obtain such an attestation
|
||||
* statement.
|
||||
*/
|
||||
public static final AttestationConveyancePreference INDIRECT = new AttestationConveyancePreference("indirect");
|
||||
|
||||
/**
|
||||
* The <a href=
|
||||
* "https://www.w3.org/TR/webauthn-3/#dom-attestationconveyancepreference-direct">direct</a>
|
||||
* preference indicates that the Relying Party wants to receive the
|
||||
* <a href="https://www.w3.org/TR/webauthn-3/#attestation-statement">attestation
|
||||
* statement</a> as generated by the
|
||||
* <a href="https://www.w3.org/TR/webauthn-3/#authenticator">authenticator</a>.
|
||||
*/
|
||||
public static final AttestationConveyancePreference DIRECT = new AttestationConveyancePreference("direct");
|
||||
|
||||
/**
|
||||
* The <a href=
|
||||
* "https://www.w3.org/TR/webauthn-3/#dom-attestationconveyancepreference-enterprise">enterprise</a>
|
||||
* preference indicates that the Relying Party wants to receive an
|
||||
* <a href="https://www.w3.org/TR/webauthn-3/#attestation-statement">attestation
|
||||
* statement</a> that may include uniquely identifying information.
|
||||
*/
|
||||
public static final AttestationConveyancePreference ENTERPRISE = new AttestationConveyancePreference("enterprise");
|
||||
|
||||
private final String value;
|
||||
|
||||
AttestationConveyancePreference(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the String value of the preference.
|
||||
* @return the String value of the preference.
|
||||
*/
|
||||
public String getValue() {
|
||||
return this.value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an instance of {@link AttestationConveyancePreference}
|
||||
* @param value the {@link #getValue()}
|
||||
* @return an {@link AttestationConveyancePreference}
|
||||
*/
|
||||
public static AttestationConveyancePreference valueOf(String value) {
|
||||
switch (value) {
|
||||
case "none":
|
||||
return NONE;
|
||||
case "indirect":
|
||||
return INDIRECT;
|
||||
case "direct":
|
||||
return DIRECT;
|
||||
case "enterprise":
|
||||
return ENTERPRISE;
|
||||
default:
|
||||
return new AttestationConveyancePreference(value);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
-50
@@ -1,50 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2025 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.
|
||||
*/
|
||||
|
||||
package org.springframework.security.web.webauthn.api;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* A <a href="https://www.w3.org/TR/webauthn-3/#client-extension-input">client extension
|
||||
* input</a> entry in the {@link AuthenticationExtensionsClientInputs}.
|
||||
*
|
||||
* @param <T>
|
||||
* @author Rob Winch
|
||||
* @since 6.4
|
||||
* @see ImmutableAuthenticationExtensionsClientInput
|
||||
*/
|
||||
public interface AuthenticationExtensionsClientInput<T> extends Serializable {
|
||||
|
||||
/**
|
||||
* Gets the <a href="https://www.w3.org/TR/webauthn-3/#extension-identifier">extension
|
||||
* identifier</a>.
|
||||
* @return the
|
||||
* <a href="https://www.w3.org/TR/webauthn-3/#extension-identifier">extension
|
||||
* identifier</a>.
|
||||
*/
|
||||
String getExtensionId();
|
||||
|
||||
/**
|
||||
* Gets the <a href="https://www.w3.org/TR/webauthn-3/#client-extension-input">client
|
||||
* extension</a>.
|
||||
* @return the
|
||||
* <a href="https://www.w3.org/TR/webauthn-3/#client-extension-input">client
|
||||
* extension</a>.
|
||||
*/
|
||||
T getInput();
|
||||
|
||||
}
|
||||
-43
@@ -1,43 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2025 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.
|
||||
*/
|
||||
|
||||
package org.springframework.security.web.webauthn.api;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* <a href=
|
||||
* "https://www.w3.org/TR/webauthn-3/#iface-authentication-extensions-client-inputs">AuthenticationExtensionsClientInputs</a>
|
||||
* is a dictionary containing the
|
||||
* <a href="https://www.w3.org/TR/webauthn-3/#client-extension-input">client extension
|
||||
* input</a> values for zero or more
|
||||
* <a href="https://www.w3.org/TR/webauthn-3/#webauthn-extensions">WebAuthn
|
||||
* Extensions</a>.
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 6.4
|
||||
* @see PublicKeyCredentialCreationOptions#getExtensions()
|
||||
*/
|
||||
public interface AuthenticationExtensionsClientInputs extends Serializable {
|
||||
|
||||
/**
|
||||
* Gets all of the {@link AuthenticationExtensionsClientInput}.
|
||||
* @return a non-null {@link List} of {@link AuthenticationExtensionsClientInput}.
|
||||
*/
|
||||
List<AuthenticationExtensionsClientInput> getInputs();
|
||||
|
||||
}
|
||||
-49
@@ -1,49 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2025 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.
|
||||
*/
|
||||
|
||||
package org.springframework.security.web.webauthn.api;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* A <a href="https://www.w3.org/TR/webauthn-3/#client-extension-output">client extension
|
||||
* output</a> entry in {@link AuthenticationExtensionsClientOutputs}.
|
||||
*
|
||||
* @param <T>
|
||||
* @see AuthenticationExtensionsClientOutputs#getOutputs()
|
||||
* @see CredentialPropertiesOutput
|
||||
*/
|
||||
public interface AuthenticationExtensionsClientOutput<T> extends Serializable {
|
||||
|
||||
/**
|
||||
* Gets the <a href="https://www.w3.org/TR/webauthn-3/#extension-identifier">extension
|
||||
* identifier</a>.
|
||||
* @return the
|
||||
* <a href="https://www.w3.org/TR/webauthn-3/#extension-identifier">extension
|
||||
* identifier</a>.
|
||||
*/
|
||||
String getExtensionId();
|
||||
|
||||
/**
|
||||
* The <a href="https://www.w3.org/TR/webauthn-3/#client-extension-output">client
|
||||
* extension output</a>.
|
||||
* @return the
|
||||
* <a href="https://www.w3.org/TR/webauthn-3/#client-extension-output">client
|
||||
* extension output</a>.
|
||||
*/
|
||||
T getOutput();
|
||||
|
||||
}
|
||||
-43
@@ -1,43 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2025 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.
|
||||
*/
|
||||
|
||||
package org.springframework.security.web.webauthn.api;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* <a href=
|
||||
* "https://www.w3.org/TR/webauthn-3/#dictdef-authenticationextensionsclientoutputs">AuthenticationExtensionsClientOutputs</a>
|
||||
* is a dictionary containing the
|
||||
* <a href="https://www.w3.org/TR/webauthn-3/#client-extension-output">client extension
|
||||
* output</a> values for zero or more
|
||||
* <a href="https://www.w3.org/TR/webauthn-3/#webauthn-extensions">WebAuthn
|
||||
* Extensions</a>.
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 6.4
|
||||
* @see PublicKeyCredential#getClientExtensionResults()
|
||||
*/
|
||||
public interface AuthenticationExtensionsClientOutputs extends Serializable {
|
||||
|
||||
/**
|
||||
* Gets all of the {@link AuthenticationExtensionsClientOutput}.
|
||||
* @return a non-null {@link List} of {@link AuthenticationExtensionsClientOutput}.
|
||||
*/
|
||||
List<AuthenticationExtensionsClientOutput<?>> getOutputs();
|
||||
|
||||
}
|
||||
-210
@@ -1,210 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2025 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.
|
||||
*/
|
||||
|
||||
package org.springframework.security.web.webauthn.api;
|
||||
|
||||
import java.io.Serial;
|
||||
|
||||
/**
|
||||
* The <a href=
|
||||
* "https://www.w3.org/TR/webauthn-3/#authenticatorassertionresponse">AuthenticatorAssertionResponse</a>
|
||||
* interface represents an
|
||||
* <a href="https://www.w3.org/TR/webauthn-3/#authenticator">authenticator</a>'s response
|
||||
* to a client's request for generation of a new
|
||||
* <a href="https://www.w3.org/TR/webauthn-3/#authentication-assertion">authentication
|
||||
* assertion</a> given the
|
||||
* <a href="https://www.w3.org/TR/webauthn-3/#webauthn-relying-party">WebAuthn Relying
|
||||
* Party</a>'s challenge and OPTIONAL list of credentials it is aware of. This response
|
||||
* contains a cryptographic signature proving possession of the
|
||||
* <a href="https://www.w3.org/TR/webauthn-3/#credential-private-key">credential private
|
||||
* key</a>, and optionally evidence of
|
||||
* <a href="https://www.w3.org/TR/webauthn-3/#user-consent">user consent</a> to a specific
|
||||
* transaction.
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 6.4
|
||||
* @see PublicKeyCredential#getResponse()
|
||||
*/
|
||||
public final class AuthenticatorAssertionResponse extends AuthenticatorResponse {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 324976481675434298L;
|
||||
|
||||
private final Bytes authenticatorData;
|
||||
|
||||
private final Bytes signature;
|
||||
|
||||
private final Bytes userHandle;
|
||||
|
||||
private final Bytes attestationObject;
|
||||
|
||||
private AuthenticatorAssertionResponse(Bytes clientDataJSON, Bytes authenticatorData, Bytes signature,
|
||||
Bytes userHandle, Bytes attestationObject) {
|
||||
super(clientDataJSON);
|
||||
this.authenticatorData = authenticatorData;
|
||||
this.signature = signature;
|
||||
this.userHandle = userHandle;
|
||||
this.attestationObject = attestationObject;
|
||||
}
|
||||
|
||||
/**
|
||||
* The <a href=
|
||||
* "https://www.w3.org/TR/webauthn-3/#dom-authenticatorassertionresponse-authenticatordata">authenticatorData</a>
|
||||
* contains the
|
||||
* <a href="https://www.w3.org/TR/webauthn-3/#authenticator-data">authenticator
|
||||
* data</a> returned by the authenticator. See
|
||||
* <a href="https://www.w3.org/TR/webauthn-3/#sctn-authenticator-data">6.1
|
||||
* Authenticator Data.</a>.
|
||||
* @return the {@code authenticatorData}
|
||||
*/
|
||||
public Bytes getAuthenticatorData() {
|
||||
return this.authenticatorData;
|
||||
}
|
||||
|
||||
/**
|
||||
* The <a href=
|
||||
* "https://www.w3.org/TR/webauthn-3/#dom-authenticatorassertionresponse-signature">signature</a>
|
||||
* contains the raw signature returned from the authenticator. See
|
||||
* <a href="https://www.w3.org/TR/webauthn-3/#sctn-op-get-assertion">6.3.3 The
|
||||
* authenticatorGetAssertion Operation</a>.
|
||||
* @return the {@code signature}
|
||||
*/
|
||||
public Bytes getSignature() {
|
||||
return this.signature;
|
||||
}
|
||||
|
||||
/**
|
||||
* The <a href=
|
||||
* "https://www.w3.org/TR/webauthn-3/#dom-authenticatorassertionresponse-userhandle">userHandle</a>
|
||||
* is the <a href="https://www.w3.org/TR/webauthn-3/#user-handle">user handle</a>
|
||||
* which is returned from the authenticator, or null if the authenticator did not
|
||||
* return a user handle. See
|
||||
* <a href="https://www.w3.org/TR/webauthn-3/#sctn-op-get-assertion">6.3.3 The
|
||||
* authenticatorGetAssertion Operation</a>. The authenticator MUST always return a
|
||||
* user handle if the <a href=
|
||||
* "https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialrequestoptions-allowcredentials">allowCredentials</a>
|
||||
* option used in the
|
||||
* <a href="https://www.w3.org/TR/webauthn-3/#authentication-ceremony">authentication
|
||||
* ceremony</a> is empty, and MAY return one otherwise.
|
||||
* @return the <a href="https://www.w3.org/TR/webauthn-3/#user-handle">user handle</a>
|
||||
*/
|
||||
public Bytes getUserHandle() {
|
||||
return this.userHandle;
|
||||
}
|
||||
|
||||
/**
|
||||
* The <a href=
|
||||
* "https://www.w3.org/TR/webauthn-3/#dom-authenticatorattestationresponse-attestationobject">attestationObject</a>
|
||||
* is an OPTIONAL attribute contains an
|
||||
* <a href="https://www.w3.org/TR/webauthn-3/#attestation-object">attestation
|
||||
* object</a>, if the authenticator supports attestation in assertions.
|
||||
* @return the {@code attestationObject}
|
||||
*/
|
||||
public Bytes getAttestationObject() {
|
||||
return this.attestationObject;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link AuthenticatorAssertionResponseBuilder}
|
||||
* @return the {@link AuthenticatorAssertionResponseBuilder}
|
||||
*/
|
||||
public static AuthenticatorAssertionResponseBuilder builder() {
|
||||
return new AuthenticatorAssertionResponseBuilder();
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a {@link AuthenticatorAssertionResponse}.
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 6.4
|
||||
*/
|
||||
public static final class AuthenticatorAssertionResponseBuilder {
|
||||
|
||||
private Bytes authenticatorData;
|
||||
|
||||
private Bytes signature;
|
||||
|
||||
private Bytes userHandle;
|
||||
|
||||
private Bytes attestationObject;
|
||||
|
||||
private Bytes clientDataJSON;
|
||||
|
||||
private AuthenticatorAssertionResponseBuilder() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the {@link #getAuthenticatorData()} property
|
||||
* @param authenticatorData the authenticator data.
|
||||
* @return the {@link AuthenticatorAssertionResponseBuilder}
|
||||
*/
|
||||
public AuthenticatorAssertionResponseBuilder authenticatorData(Bytes authenticatorData) {
|
||||
this.authenticatorData = authenticatorData;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the {@link #getSignature()} property
|
||||
* @param signature the signature
|
||||
* @return the {@link AuthenticatorAssertionResponseBuilder}
|
||||
*/
|
||||
public AuthenticatorAssertionResponseBuilder signature(Bytes signature) {
|
||||
this.signature = signature;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the {@link #getUserHandle()} property
|
||||
* @param userHandle the user handle
|
||||
* @return the {@link AuthenticatorAssertionResponseBuilder}
|
||||
*/
|
||||
public AuthenticatorAssertionResponseBuilder userHandle(Bytes userHandle) {
|
||||
this.userHandle = userHandle;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the {@link #attestationObject} property
|
||||
* @param attestationObject the attestation object
|
||||
* @return the {@link AuthenticatorAssertionResponseBuilder}
|
||||
*/
|
||||
public AuthenticatorAssertionResponseBuilder attestationObject(Bytes attestationObject) {
|
||||
this.attestationObject = attestationObject;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the {@link #getClientDataJSON()} property
|
||||
* @param clientDataJSON the client data JSON
|
||||
* @return the {@link AuthenticatorAssertionResponseBuilder}
|
||||
*/
|
||||
public AuthenticatorAssertionResponseBuilder clientDataJSON(Bytes clientDataJSON) {
|
||||
this.clientDataJSON = clientDataJSON;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the {@link AuthenticatorAssertionResponse}
|
||||
* @return the {@link AuthenticatorAssertionResponse}
|
||||
*/
|
||||
public AuthenticatorAssertionResponse build() {
|
||||
return new AuthenticatorAssertionResponse(this.clientDataJSON, this.authenticatorData, this.signature,
|
||||
this.userHandle, this.attestationObject);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
-100
@@ -1,100 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2024 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.
|
||||
*/
|
||||
|
||||
package org.springframework.security.web.webauthn.api;
|
||||
|
||||
import java.io.ObjectStreamException;
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* The <a href=
|
||||
* "https://www.w3.org/TR/webauthn-3/#enumdef-authenticatorattachment">AuthenticatorAttachment</a>.
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 6.4
|
||||
*/
|
||||
public final class AuthenticatorAttachment implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 8446133215195918090L;
|
||||
|
||||
/**
|
||||
* Indicates <a href=
|
||||
* "https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#cross-platform-attachment">cross-platform
|
||||
* attachment</a>.
|
||||
*
|
||||
* <p>
|
||||
* Authenticators of this class are removable from, and can "roam" among, client
|
||||
* platforms.
|
||||
*/
|
||||
public static final AuthenticatorAttachment CROSS_PLATFORM = new AuthenticatorAttachment("cross-platform");
|
||||
|
||||
/**
|
||||
* Indicates <a href=
|
||||
* "https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#platform-attachment">platform
|
||||
* attachment</a>.
|
||||
*
|
||||
* <p>
|
||||
* Usually, authenticators of this class are not removable from the platform.
|
||||
*/
|
||||
public static final AuthenticatorAttachment PLATFORM = new AuthenticatorAttachment("platform");
|
||||
|
||||
private final String value;
|
||||
|
||||
AuthenticatorAttachment(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value.
|
||||
* @return the value.
|
||||
*/
|
||||
public String getValue() {
|
||||
return this.value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "AuthenticatorAttachment [" + this.value + "]";
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an instance of {@link AuthenticatorAttachment} based upon the value passed in.
|
||||
* @param value the value to obtain the {@link AuthenticatorAttachment}
|
||||
* @return the {@link AuthenticatorAttachment}
|
||||
*/
|
||||
public static AuthenticatorAttachment valueOf(String value) {
|
||||
switch (value) {
|
||||
case "cross-platform":
|
||||
return CROSS_PLATFORM;
|
||||
case "platform":
|
||||
return PLATFORM;
|
||||
default:
|
||||
return new AuthenticatorAttachment(value);
|
||||
}
|
||||
}
|
||||
|
||||
public static AuthenticatorAttachment[] values() {
|
||||
return new AuthenticatorAttachment[] { CROSS_PLATFORM, PLATFORM };
|
||||
}
|
||||
|
||||
@Serial
|
||||
private Object readResolve() throws ObjectStreamException {
|
||||
return valueOf(this.value);
|
||||
}
|
||||
|
||||
}
|
||||
-144
@@ -1,144 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2024 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.
|
||||
*/
|
||||
|
||||
package org.springframework.security.web.webauthn.api;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* <a href=
|
||||
* "https://www.w3.org/TR/webauthn-3/#authenticatorattestationresponse">AuthenticatorAttestationResponse</a>
|
||||
* represents the
|
||||
* <a href="https://www.w3.org/TR/webauthn-3/#authenticator">authenticator</a>'s response
|
||||
* to a client's request for the creation of a new
|
||||
* <a href="https://www.w3.org/TR/webauthn-3/#public-key-credential">public key
|
||||
* credential</a>.
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 6.4
|
||||
* @see PublicKeyCredential#getResponse()
|
||||
*/
|
||||
public final class AuthenticatorAttestationResponse extends AuthenticatorResponse {
|
||||
|
||||
private final Bytes attestationObject;
|
||||
|
||||
private final List<AuthenticatorTransport> transports;
|
||||
|
||||
private AuthenticatorAttestationResponse(Bytes clientDataJSON, Bytes attestationObject,
|
||||
List<AuthenticatorTransport> transports) {
|
||||
super(clientDataJSON);
|
||||
this.attestationObject = attestationObject;
|
||||
this.transports = transports;
|
||||
}
|
||||
|
||||
/**
|
||||
* The <a href=
|
||||
* "https://www.w3.org/TR/webauthn-3/#dom-authenticatorattestationresponse-attestationobject">attestationObject</a>
|
||||
* attribute contains an attestation object, which is opaque to, and cryptographically
|
||||
* protected against tampering by, the client.
|
||||
* @return the attestationObject
|
||||
*/
|
||||
public Bytes getAttestationObject() {
|
||||
return this.attestationObject;
|
||||
}
|
||||
|
||||
/**
|
||||
* The <a href=
|
||||
* "https://www.w3.org/TR/webauthn-3/#dom-authenticatorattestationresponse-gettransports">transports</a>
|
||||
* returns the <a href=
|
||||
* "https://www.w3.org/TR/webauthn-3/#dom-authenticatorattestationresponse-transports-slot">transports</a>
|
||||
* @return the transports
|
||||
*/
|
||||
public List<AuthenticatorTransport> getTransports() {
|
||||
return this.transports;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new instance.
|
||||
* @return the {@link AuthenticatorAttestationResponseBuilder}
|
||||
*/
|
||||
public static AuthenticatorAttestationResponseBuilder builder() {
|
||||
return new AuthenticatorAttestationResponseBuilder();
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds {@link AuthenticatorAssertionResponse}.
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 6.4
|
||||
*/
|
||||
public static final class AuthenticatorAttestationResponseBuilder {
|
||||
|
||||
private Bytes attestationObject;
|
||||
|
||||
private List<AuthenticatorTransport> transports;
|
||||
|
||||
private Bytes clientDataJSON;
|
||||
|
||||
private AuthenticatorAttestationResponseBuilder() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link #getAttestationObject()} property.
|
||||
* @param attestationObject the attestation object.
|
||||
* @return the {@link AuthenticatorAttestationResponseBuilder}
|
||||
*/
|
||||
public AuthenticatorAttestationResponseBuilder attestationObject(Bytes attestationObject) {
|
||||
this.attestationObject = attestationObject;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link #getTransports()} property.
|
||||
* @param transports the transports
|
||||
* @return the {@link AuthenticatorAttestationResponseBuilder}
|
||||
*/
|
||||
public AuthenticatorAttestationResponseBuilder transports(AuthenticatorTransport... transports) {
|
||||
return transports(Arrays.asList(transports));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link #getTransports()} property.
|
||||
* @param transports the transports
|
||||
* @return the {@link AuthenticatorAttestationResponseBuilder}
|
||||
*/
|
||||
public AuthenticatorAttestationResponseBuilder transports(List<AuthenticatorTransport> transports) {
|
||||
this.transports = transports;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link #getClientDataJSON()} property.
|
||||
* @param clientDataJSON the client data JSON.
|
||||
* @return the {@link AuthenticatorAttestationResponseBuilder}
|
||||
*/
|
||||
public AuthenticatorAttestationResponseBuilder clientDataJSON(Bytes clientDataJSON) {
|
||||
this.clientDataJSON = clientDataJSON;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a {@link AuthenticatorAssertionResponse}.
|
||||
* @return the {@link AuthenticatorAttestationResponseBuilder}
|
||||
*/
|
||||
public AuthenticatorAttestationResponse build() {
|
||||
return new AuthenticatorAttestationResponse(this.clientDataJSON, this.attestationObject, this.transports);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
-55
@@ -1,55 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2025 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.
|
||||
*/
|
||||
|
||||
package org.springframework.security.web.webauthn.api;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* The <a href=
|
||||
* "https://www.w3.org/TR/webauthn-3/#iface-authenticatorresponse">AuthenticatorResponse</a>
|
||||
* represents <a href="https://www.w3.org/TR/webauthn-3/#authenticator">Authenticators</a>
|
||||
* respond to <a href="https://www.w3.org/TR/webauthn-3/#relying-party">Relying Party</a>
|
||||
* requests.
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 6.4
|
||||
*/
|
||||
public abstract class AuthenticatorResponse implements Serializable {
|
||||
|
||||
private final Bytes clientDataJSON;
|
||||
|
||||
/**
|
||||
* Creates a new instance
|
||||
* @param clientDataJSON the {@link #getClientDataJSON()}
|
||||
*/
|
||||
AuthenticatorResponse(Bytes clientDataJSON) {
|
||||
this.clientDataJSON = clientDataJSON;
|
||||
}
|
||||
|
||||
/**
|
||||
* The <a href=
|
||||
* "https://www.w3.org/TR/webauthn-3/#dom-authenticatorresponse-clientdatajson">clientDataJSON</a>
|
||||
* contains a JSON-compatible serialization of the client data, the hash of which is
|
||||
* passed to the authenticator by the client in its call to either create() or get()
|
||||
* (i.e., the client data itself is not sent to the authenticator).
|
||||
* @return the client data JSON
|
||||
*/
|
||||
public Bytes getClientDataJSON() {
|
||||
return this.clientDataJSON;
|
||||
}
|
||||
|
||||
}
|
||||
-170
@@ -1,170 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2024 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.
|
||||
*/
|
||||
|
||||
package org.springframework.security.web.webauthn.api;
|
||||
|
||||
/**
|
||||
* <a href=
|
||||
* "https://www.w3.org/TR/webauthn-3/#dictdef-authenticatorselectioncriteria">AuthenticatorAttachment</a>
|
||||
* can be used by
|
||||
* <a href="https://www.w3.org/TR/webauthn-3/#webauthn-relying-party">WebAuthn Relying
|
||||
* Parties</a> to specify their requirements regarding authenticator attributes.
|
||||
*
|
||||
* There is no <a href=
|
||||
* "https://www.w3.org/TR/webauthn-3/#dom-authenticatorselectioncriteria-requireresidentkey">requireResidentKey</a>
|
||||
* property because it is only for backwards compatibility with WebAuthn Level 1.
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 6.4
|
||||
* @see PublicKeyCredentialCreationOptions#getAuthenticatorSelection()
|
||||
*/
|
||||
public final class AuthenticatorSelectionCriteria {
|
||||
|
||||
private final AuthenticatorAttachment authenticatorAttachment;
|
||||
|
||||
private final ResidentKeyRequirement residentKey;
|
||||
|
||||
private final UserVerificationRequirement userVerification;
|
||||
|
||||
// NOTE: There is no requireResidentKey property because it is only for backward
|
||||
// compatibility with WebAuthn Level 1
|
||||
|
||||
/**
|
||||
* Creates a new instance
|
||||
* @param authenticatorAttachment the authenticator attachment
|
||||
* @param residentKey the resident key requirement
|
||||
* @param userVerification the user verification
|
||||
*/
|
||||
private AuthenticatorSelectionCriteria(AuthenticatorAttachment authenticatorAttachment,
|
||||
ResidentKeyRequirement residentKey, UserVerificationRequirement userVerification) {
|
||||
this.authenticatorAttachment = authenticatorAttachment;
|
||||
this.residentKey = residentKey;
|
||||
this.userVerification = userVerification;
|
||||
}
|
||||
|
||||
/**
|
||||
* If <a href=
|
||||
* "https://www.w3.org/TR/webauthn-3/#dom-authenticatorselectioncriteria-authenticatorattachment">
|
||||
* authenticatorAttachment</a> is present, eligible
|
||||
* <a href="https://www.w3.org/TR/webauthn-3/#authenticator">authenticators</a> are
|
||||
* filtered to be only those authenticators attached with the specified
|
||||
* <a href="https://www.w3.org/TR/webauthn-3/#enum-attachment">authenticator
|
||||
* attachment modality</a> (see also <a href=
|
||||
* "https://www.w3.org/TR/webauthn-3/#sctn-authenticator-attachment-modality">6.2.1
|
||||
* Authenticator Attachment Modality</a>).
|
||||
* @return the authenticator attachment
|
||||
*/
|
||||
public AuthenticatorAttachment getAuthenticatorAttachment() {
|
||||
return this.authenticatorAttachment;
|
||||
}
|
||||
|
||||
/**
|
||||
* The <a href=
|
||||
* "https://www.w3.org/TR/webauthn-3/#dom-authenticatorselectioncriteria-residentkey">residentKey</a>
|
||||
* specifies the extent to which the
|
||||
* <a href="https://www.w3.org/TR/webauthn-3/#relying-party">Relying Party</a> desires
|
||||
* to create a <a href=
|
||||
* "https://www.w3.org/TR/webauthn-3/#client-side-discoverable-credential">client-side
|
||||
* discoverable credential</a>.
|
||||
* @return the resident key requirement
|
||||
*/
|
||||
public ResidentKeyRequirement getResidentKey() {
|
||||
return this.residentKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* The <a href=
|
||||
* "https://www.w3.org/TR/webauthn-3/#dom-authenticatorselectioncriteria-userverification">userVerification</a>
|
||||
* specifies the <a href="https://www.w3.org/TR/webauthn-3/#relying-party">Relying
|
||||
* Party</a>'s requirements regarding
|
||||
* <a href="https://www.w3.org/TR/webauthn-3/#user-verification">user verification</a>
|
||||
* for the <a href=
|
||||
* "https://w3c.github.io/webappsec-credential-management/#dom-credentialscontainer-create">create()</a>
|
||||
* operation.
|
||||
* @return the user verification requirement
|
||||
*/
|
||||
public UserVerificationRequirement getUserVerification() {
|
||||
return this.userVerification;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link AuthenticatorSelectionCriteriaBuilder}
|
||||
* @return a new {@link AuthenticatorSelectionCriteriaBuilder}
|
||||
*/
|
||||
public static AuthenticatorSelectionCriteriaBuilder builder() {
|
||||
return new AuthenticatorSelectionCriteriaBuilder();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@link AuthenticatorSelectionCriteria}
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 6.4
|
||||
*/
|
||||
public static final class AuthenticatorSelectionCriteriaBuilder {
|
||||
|
||||
private AuthenticatorAttachment authenticatorAttachment;
|
||||
|
||||
private ResidentKeyRequirement residentKey;
|
||||
|
||||
private UserVerificationRequirement userVerification;
|
||||
|
||||
private AuthenticatorSelectionCriteriaBuilder() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link #getAuthenticatorAttachment()} property.
|
||||
* @param authenticatorAttachment the authenticator attachment
|
||||
* @return the {@link AuthenticatorSelectionCriteriaBuilder}
|
||||
*/
|
||||
public AuthenticatorSelectionCriteriaBuilder authenticatorAttachment(
|
||||
AuthenticatorAttachment authenticatorAttachment) {
|
||||
this.authenticatorAttachment = authenticatorAttachment;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link #getResidentKey()} property.
|
||||
* @param residentKey the resident key
|
||||
* @return the {@link AuthenticatorSelectionCriteriaBuilder}
|
||||
*/
|
||||
public AuthenticatorSelectionCriteriaBuilder residentKey(ResidentKeyRequirement residentKey) {
|
||||
this.residentKey = residentKey;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link #getUserVerification()} property.
|
||||
* @param userVerification the user verification requirement
|
||||
* @return the {@link AuthenticatorSelectionCriteriaBuilder}
|
||||
*/
|
||||
public AuthenticatorSelectionCriteriaBuilder userVerification(UserVerificationRequirement userVerification) {
|
||||
this.userVerification = userVerification;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a {@link AuthenticatorSelectionCriteria}
|
||||
* @return a new {@link AuthenticatorSelectionCriteria}
|
||||
*/
|
||||
public AuthenticatorSelectionCriteria build() {
|
||||
return new AuthenticatorSelectionCriteria(this.authenticatorAttachment, this.residentKey,
|
||||
this.userVerification);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
-124
@@ -1,124 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2025 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.
|
||||
*/
|
||||
|
||||
package org.springframework.security.web.webauthn.api;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* <a href=
|
||||
* "https://www.w3.org/TR/webauthn-3/#enumdef-authenticatortransport">AuthenticatorTransport</a>
|
||||
* defines hints as to how clients might communicate with a particular authenticator in
|
||||
* order to obtain an assertion for a specific credential.
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 6.4
|
||||
*/
|
||||
public final class AuthenticatorTransport implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = -5617945441117386982L;
|
||||
|
||||
/**
|
||||
* <a href="https://www.w3.org/TR/webauthn-3/#dom-authenticatortransport-usb">usbc</a>
|
||||
* indicates the respective authenticator can be contacted over removable USB.
|
||||
*/
|
||||
public static final AuthenticatorTransport USB = new AuthenticatorTransport("usb");
|
||||
|
||||
/**
|
||||
* <a href="https://www.w3.org/TR/webauthn-3/#dom-authenticatortransport-nfc">nfc</a>
|
||||
* indicates the respective authenticator can be contacted over Near Field
|
||||
* Communication (NFC).
|
||||
*/
|
||||
public static final AuthenticatorTransport NFC = new AuthenticatorTransport("nfc");
|
||||
|
||||
/**
|
||||
* <a href="https://www.w3.org/TR/webauthn-3/#dom-authenticatortransport-ble">ble</a>
|
||||
* Indicates the respective authenticator can be contacted over Bluetooth Smart
|
||||
* (Bluetooth Low Energy / BLE).
|
||||
*/
|
||||
public static final AuthenticatorTransport BLE = new AuthenticatorTransport("ble");
|
||||
|
||||
/**
|
||||
* <a href=
|
||||
* "https://www.w3.org/TR/webauthn-3/#dom-authenticatortransport-smart-card">smart-card</a>
|
||||
* indicates the respective authenticator can be contacted over ISO/IEC 7816 smart
|
||||
* card with contacts.
|
||||
*/
|
||||
public static final AuthenticatorTransport SMART_CARD = new AuthenticatorTransport("smart-card");
|
||||
|
||||
/**
|
||||
* <a href=
|
||||
* "https://www.w3.org/TR/webauthn-3/#dom-authenticatortransport-hybrid">hybrid</a>
|
||||
* indicates the respective authenticator can be contacted using a combination of
|
||||
* (often separate) data-transport and proximity mechanisms. This supports, for
|
||||
* example, authentication on a desktop computer using a smartphone.
|
||||
*/
|
||||
public static final AuthenticatorTransport HYBRID = new AuthenticatorTransport("hybrid");
|
||||
|
||||
/**
|
||||
* <a href=
|
||||
* "https://www.w3.org/TR/webauthn-3/#dom-authenticatortransport-internal">internal</a>
|
||||
* indicates the respective authenticator is contacted using a client device-specific
|
||||
* transport, i.e., it is a platform authenticator. These authenticators are not
|
||||
* removable from the client device.
|
||||
*/
|
||||
public static final AuthenticatorTransport INTERNAL = new AuthenticatorTransport("internal");
|
||||
|
||||
private final String value;
|
||||
|
||||
AuthenticatorTransport(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get's the value.
|
||||
* @return the value.
|
||||
*/
|
||||
public String getValue() {
|
||||
return this.value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an instance of {@link AuthenticatorTransport}.
|
||||
* @param value the value of the {@link AuthenticatorTransport}
|
||||
* @return the {@link AuthenticatorTransport}
|
||||
*/
|
||||
public static AuthenticatorTransport valueOf(String value) {
|
||||
switch (value) {
|
||||
case "usb":
|
||||
return USB;
|
||||
case "nfc":
|
||||
return NFC;
|
||||
case "ble":
|
||||
return BLE;
|
||||
case "smart-card":
|
||||
return SMART_CARD;
|
||||
case "hybrid":
|
||||
return HYBRID;
|
||||
case "internal":
|
||||
return INTERNAL;
|
||||
default:
|
||||
return new AuthenticatorTransport(value);
|
||||
}
|
||||
}
|
||||
|
||||
public static AuthenticatorTransport[] values() {
|
||||
return new AuthenticatorTransport[] { USB, NFC, BLE, HYBRID, INTERNAL };
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,108 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2025 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.
|
||||
*/
|
||||
|
||||
package org.springframework.security.web.webauthn.api;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
import java.security.SecureRandom;
|
||||
import java.util.Arrays;
|
||||
import java.util.Base64;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* An object representation of byte[].
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 6.4
|
||||
*/
|
||||
public final class Bytes implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = -3278138671365709777L;
|
||||
|
||||
private static final SecureRandom RANDOM = new SecureRandom();
|
||||
|
||||
private static final Base64.Encoder ENCODER = Base64.getUrlEncoder().withoutPadding();
|
||||
|
||||
private static final Base64.Decoder DECODER = Base64.getUrlDecoder();
|
||||
|
||||
private final byte[] bytes;
|
||||
|
||||
/**
|
||||
* Creates a new instance
|
||||
* @param bytes the raw base64UrlString that will be encoded.
|
||||
*/
|
||||
public Bytes(byte[] bytes) {
|
||||
Assert.notNull(bytes, "bytes cannot be null");
|
||||
this.bytes = bytes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the raw bytes.
|
||||
* @return the bytes
|
||||
*/
|
||||
public byte[] getBytes() {
|
||||
return Arrays.copyOf(this.bytes, this.bytes.length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the bytes as Base64 URL encoded String.
|
||||
* @return
|
||||
*/
|
||||
public String toBase64UrlString() {
|
||||
return ENCODER.encodeToString(getBytes());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj instanceof Bytes that) {
|
||||
return that.toBase64UrlString().equals(toBase64UrlString());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return toBase64UrlString().hashCode();
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "Bytes[" + toBase64UrlString() + "]";
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a secure random {@link Bytes} with random bytes and sufficient entropy.
|
||||
* @return a new secure random generated {@link Bytes}
|
||||
*/
|
||||
public static Bytes random() {
|
||||
byte[] bytes = new byte[32];
|
||||
RANDOM.nextBytes(bytes);
|
||||
return new Bytes(bytes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new instance from a base64 url string.
|
||||
* @param base64UrlString the base64 url string
|
||||
* @return the {@link Bytes}
|
||||
*/
|
||||
public static Bytes fromBase64(String base64UrlString) {
|
||||
byte[] bytes = DECODER.decode(base64UrlString);
|
||||
return new Bytes(bytes);
|
||||
}
|
||||
|
||||
}
|
||||
-65
@@ -1,65 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2024 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.
|
||||
*/
|
||||
|
||||
package org.springframework.security.web.webauthn.api;
|
||||
|
||||
/**
|
||||
* <a href=
|
||||
* "https://www.w3.org/TR/webauthn-3/#sctn-alg-identifier">COSEAlgorithmIdentifier</a> is
|
||||
* used to identify a cryptographic algorithm.
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 6.4
|
||||
* @see PublicKeyCredentialParameters#getAlg()
|
||||
*/
|
||||
public final class COSEAlgorithmIdentifier {
|
||||
|
||||
public static final COSEAlgorithmIdentifier EdDSA = new COSEAlgorithmIdentifier(-8);
|
||||
|
||||
public static final COSEAlgorithmIdentifier ES256 = new COSEAlgorithmIdentifier(-7);
|
||||
|
||||
public static final COSEAlgorithmIdentifier ES384 = new COSEAlgorithmIdentifier(-35);
|
||||
|
||||
public static final COSEAlgorithmIdentifier ES512 = new COSEAlgorithmIdentifier(-36);
|
||||
|
||||
public static final COSEAlgorithmIdentifier RS256 = new COSEAlgorithmIdentifier(-257);
|
||||
|
||||
public static final COSEAlgorithmIdentifier RS384 = new COSEAlgorithmIdentifier(-258);
|
||||
|
||||
public static final COSEAlgorithmIdentifier RS512 = new COSEAlgorithmIdentifier(-259);
|
||||
|
||||
public static final COSEAlgorithmIdentifier RS1 = new COSEAlgorithmIdentifier(-65535);
|
||||
|
||||
private final long value;
|
||||
|
||||
private COSEAlgorithmIdentifier(long value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public long getValue() {
|
||||
return this.value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.valueOf(this.value);
|
||||
}
|
||||
|
||||
public static COSEAlgorithmIdentifier[] values() {
|
||||
return new COSEAlgorithmIdentifier[] { EdDSA, ES256, ES384, ES512, RS256, RS384, RS512, RS1 };
|
||||
}
|
||||
|
||||
}
|
||||
-82
@@ -1,82 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2025 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.
|
||||
*/
|
||||
|
||||
package org.springframework.security.web.webauthn.api;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* Implements <a href=
|
||||
* "https://fidoalliance.org/specs/fido-v2.2-rd-20230321/fido-client-to-authenticator-protocol-v2.2-rd-20230321.html#sctn-credProtect-extension">
|
||||
* Credential Protection (credProtect)</a>.
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 6.4
|
||||
*/
|
||||
public class CredProtectAuthenticationExtensionsClientInput
|
||||
implements AuthenticationExtensionsClientInput<CredProtectAuthenticationExtensionsClientInput.CredProtect> {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = -6418175591005843455L;
|
||||
|
||||
private final CredProtect input;
|
||||
|
||||
public CredProtectAuthenticationExtensionsClientInput(CredProtect input) {
|
||||
this.input = input;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getExtensionId() {
|
||||
return "credProtect";
|
||||
}
|
||||
|
||||
@Override
|
||||
public CredProtect getInput() {
|
||||
return this.input;
|
||||
}
|
||||
|
||||
public static class CredProtect implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 109597301115842688L;
|
||||
|
||||
private final ProtectionPolicy credProtectionPolicy;
|
||||
|
||||
private final boolean enforceCredentialProtectionPolicy;
|
||||
|
||||
public CredProtect(ProtectionPolicy credProtectionPolicy, boolean enforceCredentialProtectionPolicy) {
|
||||
this.enforceCredentialProtectionPolicy = enforceCredentialProtectionPolicy;
|
||||
this.credProtectionPolicy = credProtectionPolicy;
|
||||
}
|
||||
|
||||
public boolean isEnforceCredentialProtectionPolicy() {
|
||||
return this.enforceCredentialProtectionPolicy;
|
||||
}
|
||||
|
||||
public ProtectionPolicy getCredProtectionPolicy() {
|
||||
return this.credProtectionPolicy;
|
||||
}
|
||||
|
||||
public enum ProtectionPolicy {
|
||||
|
||||
USER_VERIFICATION_OPTIONAL, USER_VERIFICATION_OPTIONAL_WITH_CREDENTIAL_ID_LIST, USER_VERIFICATION_REQUIRED
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
-92
@@ -1,92 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2025 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.
|
||||
*/
|
||||
|
||||
package org.springframework.security.web.webauthn.api;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* <a href=
|
||||
* "https://www.w3.org/TR/webauthn-3/#dictdef-credentialpropertiesoutput">CredentialPropertiesOutput</a>
|
||||
* is the Client extension output.
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 6.4
|
||||
*/
|
||||
public class CredentialPropertiesOutput
|
||||
implements AuthenticationExtensionsClientOutput<CredentialPropertiesOutput.ExtensionOutput> {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = -3201699313968303331L;
|
||||
|
||||
/**
|
||||
* The extension id.
|
||||
*/
|
||||
public static final String EXTENSION_ID = "credProps";
|
||||
|
||||
private final ExtensionOutput output;
|
||||
|
||||
/**
|
||||
* Creates a new instance.
|
||||
* @param rk is the resident key is discoverable
|
||||
*/
|
||||
public CredentialPropertiesOutput(boolean rk) {
|
||||
this.output = new ExtensionOutput(rk);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getExtensionId() {
|
||||
return EXTENSION_ID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExtensionOutput getOutput() {
|
||||
return this.output;
|
||||
}
|
||||
|
||||
/**
|
||||
* The output for {@link CredentialPropertiesOutput}
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 6.4
|
||||
* @see #getOutput()
|
||||
*/
|
||||
public static final class ExtensionOutput implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 4557406414847424019L;
|
||||
|
||||
private final boolean rk;
|
||||
|
||||
private ExtensionOutput(boolean rk) {
|
||||
this.rk = rk;
|
||||
}
|
||||
|
||||
/**
|
||||
* This OPTIONAL property, known abstractly as the resident key credential
|
||||
* property (i.e., client-side discoverable credential property), is a Boolean
|
||||
* value indicating whether the PublicKeyCredential returned as a result of a
|
||||
* registration ceremony is a client-side discoverable credential.
|
||||
* @return is resident key credential property
|
||||
*/
|
||||
public boolean isRk() {
|
||||
return this.rk;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
-136
@@ -1,136 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2024 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.
|
||||
*/
|
||||
|
||||
package org.springframework.security.web.webauthn.api;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Represents a <a href="https://www.w3.org/TR/webauthn-3/#credential-record">Credential
|
||||
* Record</a> that is stored by the Relying Party
|
||||
* <a href="https://www.w3.org/TR/webauthn-3/#reg-ceremony-store-credential-record">after
|
||||
* successful registration</a>.
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 6.4
|
||||
*/
|
||||
public interface CredentialRecord {
|
||||
|
||||
/**
|
||||
* The <a href=
|
||||
* "https://www.w3.org/TR/webauthn-3/#abstract-opdef-credential-record-type">credential.type</a>
|
||||
* @return
|
||||
*/
|
||||
PublicKeyCredentialType getCredentialType();
|
||||
|
||||
/**
|
||||
* The <a href=
|
||||
* "https://www.w3.org/TR/webauthn-3/#abstract-opdef-credential-record-id">credential.id</a>.
|
||||
* @return
|
||||
*/
|
||||
Bytes getCredentialId();
|
||||
|
||||
/**
|
||||
* The <a href=
|
||||
* "https://www.w3.org/TR/webauthn-3/#abstract-opdef-credential-record-publickey">publicKey</a>
|
||||
* @return
|
||||
*/
|
||||
PublicKeyCose getPublicKey();
|
||||
|
||||
/**
|
||||
* The <a href=
|
||||
* "https://www.w3.org/TR/webauthn-3/#abstract-opdef-credential-record-signcount">authData.signCount</a>
|
||||
* @return
|
||||
*/
|
||||
long getSignatureCount();
|
||||
|
||||
/**
|
||||
* <a href=
|
||||
* "https://www.w3.org/TR/webauthn-3/#abstract-opdef-credential-record-uvinitialized">uvInitialized</a>
|
||||
* is the value of the UV (user verified) flag in authData and indicates whether any
|
||||
* credential from this public key credential source has had the UV flag set.
|
||||
* @return
|
||||
*/
|
||||
boolean isUvInitialized();
|
||||
|
||||
/**
|
||||
* The <a href=
|
||||
* "https://www.w3.org/TR/webauthn-3/#abstract-opdef-credential-record-transports">transpots</a>
|
||||
* is the value returned from {@code response.getTransports()}.
|
||||
* @return
|
||||
*/
|
||||
Set<AuthenticatorTransport> getTransports();
|
||||
|
||||
/**
|
||||
* The <a href=
|
||||
* "https://www.w3.org/TR/webauthn-3/#abstract-opdef-credential-record-backupeligible">backupElgible</a>
|
||||
* flag is the same as the BE flag in authData.
|
||||
* @return
|
||||
*/
|
||||
boolean isBackupEligible();
|
||||
|
||||
/**
|
||||
* The <a href=
|
||||
* "https://www.w3.org/TR/webauthn-3/#abstract-opdef-credential-record-backupstate">backupState</a>
|
||||
* flag is the same as the BS flag in authData.
|
||||
* @return
|
||||
*/
|
||||
boolean isBackupState();
|
||||
|
||||
/**
|
||||
* A reference to the associated {@link PublicKeyCredentialUserEntity#getId()}
|
||||
* @return
|
||||
*/
|
||||
Bytes getUserEntityUserId();
|
||||
|
||||
/**
|
||||
* The <a href=
|
||||
* "https://www.w3.org/TR/webauthn-3/#abstract-opdef-credential-record-attestationobject">attestationObject</a>
|
||||
* is the value of the attestationObject attribute when the public key credential
|
||||
* source was registered.
|
||||
* @return the attestationObject
|
||||
*/
|
||||
Bytes getAttestationObject();
|
||||
|
||||
/**
|
||||
* The <a href=
|
||||
* "https://www.w3.org/TR/webauthn-3/#abstract-opdef-credential-record-attestationclientdatajson">attestationClientDataJSON</a>
|
||||
* is the value of the attestationObject attribute when the public key credential
|
||||
* source was registered.
|
||||
* @return
|
||||
*/
|
||||
Bytes getAttestationClientDataJSON();
|
||||
|
||||
/**
|
||||
* A human-readable label for this {@link CredentialRecord} assigned by the user.
|
||||
* @return a label
|
||||
*/
|
||||
String getLabel();
|
||||
|
||||
/**
|
||||
* The last time this {@link CredentialRecord} was used.
|
||||
* @return the last time this {@link CredentialRecord} was used.
|
||||
*/
|
||||
Instant getLastUsed();
|
||||
|
||||
/**
|
||||
* When this {@link CredentialRecord} was created.
|
||||
* @return When this {@link CredentialRecord} was created.
|
||||
*/
|
||||
Instant getCreated();
|
||||
|
||||
}
|
||||
-64
@@ -1,64 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2025 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.
|
||||
*/
|
||||
|
||||
package org.springframework.security.web.webauthn.api;
|
||||
|
||||
import java.io.Serial;
|
||||
|
||||
/**
|
||||
* An immutable {@link AuthenticationExtensionsClientInput}.
|
||||
*
|
||||
* @param <T> the input type
|
||||
* @author Rob Winch
|
||||
* @since 6.4
|
||||
* @see AuthenticationExtensionsClientInputs
|
||||
*/
|
||||
public class ImmutableAuthenticationExtensionsClientInput<T> implements AuthenticationExtensionsClientInput<T> {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = -1738152485672656808L;
|
||||
|
||||
/**
|
||||
* https://www.w3.org/TR/webauthn-3/#sctn-authenticator-credential-properties-extension
|
||||
*/
|
||||
public static final AuthenticationExtensionsClientInput<Boolean> credProps = new ImmutableAuthenticationExtensionsClientInput<>(
|
||||
"credProps", true);
|
||||
|
||||
private final String extensionId;
|
||||
|
||||
private final T input;
|
||||
|
||||
/**
|
||||
* Creates a new instance
|
||||
* @param extensionId the extension id.
|
||||
* @param input the input.
|
||||
*/
|
||||
public ImmutableAuthenticationExtensionsClientInput(String extensionId, T input) {
|
||||
this.extensionId = extensionId;
|
||||
this.input = input;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getExtensionId() {
|
||||
return this.extensionId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public T getInput() {
|
||||
return this.input;
|
||||
}
|
||||
|
||||
}
|
||||
-49
@@ -1,49 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2025 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.
|
||||
*/
|
||||
|
||||
package org.springframework.security.web.webauthn.api;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* An immutable implementation of {@link AuthenticationExtensionsClientInputs}.
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 6.4
|
||||
*/
|
||||
public class ImmutableAuthenticationExtensionsClientInputs implements AuthenticationExtensionsClientInputs {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 4277817521578485720L;
|
||||
|
||||
private final List<AuthenticationExtensionsClientInput> inputs;
|
||||
|
||||
public ImmutableAuthenticationExtensionsClientInputs(List<AuthenticationExtensionsClientInput> inputs) {
|
||||
this.inputs = inputs;
|
||||
}
|
||||
|
||||
public ImmutableAuthenticationExtensionsClientInputs(AuthenticationExtensionsClientInput... inputs) {
|
||||
this(Arrays.asList(inputs));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<AuthenticationExtensionsClientInput> getInputs() {
|
||||
return this.inputs;
|
||||
}
|
||||
|
||||
}
|
||||
-48
@@ -1,48 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2025 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.
|
||||
*/
|
||||
|
||||
package org.springframework.security.web.webauthn.api;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* An immutable implementation of {@link AuthenticationExtensionsClientOutputs}.
|
||||
*
|
||||
* @author Rob Winch
|
||||
*/
|
||||
public class ImmutableAuthenticationExtensionsClientOutputs implements AuthenticationExtensionsClientOutputs {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = -4656390173585180393L;
|
||||
|
||||
private final List<AuthenticationExtensionsClientOutput<?>> outputs;
|
||||
|
||||
public ImmutableAuthenticationExtensionsClientOutputs(List<AuthenticationExtensionsClientOutput<?>> outputs) {
|
||||
this.outputs = outputs;
|
||||
}
|
||||
|
||||
public ImmutableAuthenticationExtensionsClientOutputs(AuthenticationExtensionsClientOutput<?>... outputs) {
|
||||
this(Arrays.asList(outputs));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<AuthenticationExtensionsClientOutput<?>> getOutputs() {
|
||||
return this.outputs;
|
||||
}
|
||||
|
||||
}
|
||||
-285
@@ -1,285 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2024 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.
|
||||
*/
|
||||
|
||||
package org.springframework.security.web.webauthn.api;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* An immutable {@link CredentialRecord}.
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 6.4
|
||||
*/
|
||||
public final class ImmutableCredentialRecord implements CredentialRecord {
|
||||
|
||||
private final PublicKeyCredentialType credentialType;
|
||||
|
||||
private final Bytes credentialId;
|
||||
|
||||
private final Bytes userEntityUserId;
|
||||
|
||||
private final PublicKeyCose publicKey;
|
||||
|
||||
private final long signatureCount;
|
||||
|
||||
private final boolean uvInitialized;
|
||||
|
||||
private final Set<AuthenticatorTransport> transports;
|
||||
|
||||
private final boolean backupEligible;
|
||||
|
||||
private final boolean backupState;
|
||||
|
||||
private final Bytes attestationObject;
|
||||
|
||||
private final Bytes attestationClientDataJSON;
|
||||
|
||||
private final Instant created;
|
||||
|
||||
private final Instant lastUsed;
|
||||
|
||||
private final String label;
|
||||
|
||||
private ImmutableCredentialRecord(PublicKeyCredentialType credentialType, Bytes credentialId,
|
||||
Bytes userEntityUserId, PublicKeyCose publicKey, long signatureCount, boolean uvInitialized,
|
||||
Set<AuthenticatorTransport> transports, boolean backupEligible, boolean backupState,
|
||||
Bytes attestationObject, Bytes attestationClientDataJSON, Instant created, Instant lastUsed, String label) {
|
||||
this.credentialType = credentialType;
|
||||
this.credentialId = credentialId;
|
||||
this.userEntityUserId = userEntityUserId;
|
||||
this.publicKey = publicKey;
|
||||
this.signatureCount = signatureCount;
|
||||
this.uvInitialized = uvInitialized;
|
||||
this.transports = transports;
|
||||
this.backupEligible = backupEligible;
|
||||
this.backupState = backupState;
|
||||
this.attestationObject = attestationObject;
|
||||
this.attestationClientDataJSON = attestationClientDataJSON;
|
||||
this.created = created;
|
||||
this.lastUsed = lastUsed;
|
||||
this.label = label;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PublicKeyCredentialType getCredentialType() {
|
||||
return this.credentialType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Bytes getCredentialId() {
|
||||
return this.credentialId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Bytes getUserEntityUserId() {
|
||||
return this.userEntityUserId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PublicKeyCose getPublicKey() {
|
||||
return this.publicKey;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getSignatureCount() {
|
||||
return this.signatureCount;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isUvInitialized() {
|
||||
return this.uvInitialized;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<AuthenticatorTransport> getTransports() {
|
||||
return this.transports;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isBackupEligible() {
|
||||
return this.backupEligible;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isBackupState() {
|
||||
return this.backupState;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Bytes getAttestationObject() {
|
||||
return this.attestationObject;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Bytes getAttestationClientDataJSON() {
|
||||
return this.attestationClientDataJSON;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Instant getCreated() {
|
||||
return this.created;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Instant getLastUsed() {
|
||||
return this.lastUsed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getLabel() {
|
||||
return this.label;
|
||||
}
|
||||
|
||||
public static ImmutableCredentialRecordBuilder builder() {
|
||||
return new ImmutableCredentialRecordBuilder();
|
||||
}
|
||||
|
||||
public static ImmutableCredentialRecordBuilder fromCredentialRecord(CredentialRecord credentialRecord) {
|
||||
return new ImmutableCredentialRecordBuilder(credentialRecord);
|
||||
}
|
||||
|
||||
public static final class ImmutableCredentialRecordBuilder {
|
||||
|
||||
private PublicKeyCredentialType credentialType;
|
||||
|
||||
private Bytes credentialId;
|
||||
|
||||
private Bytes userEntityUserId;
|
||||
|
||||
private PublicKeyCose publicKey;
|
||||
|
||||
private long signatureCount;
|
||||
|
||||
private boolean uvInitialized;
|
||||
|
||||
private Set<AuthenticatorTransport> transports;
|
||||
|
||||
private boolean backupEligible;
|
||||
|
||||
private boolean backupState;
|
||||
|
||||
private Bytes attestationObject;
|
||||
|
||||
private Bytes attestationClientDataJSON;
|
||||
|
||||
private Instant created = Instant.now();
|
||||
|
||||
private Instant lastUsed = this.created;
|
||||
|
||||
private String label;
|
||||
|
||||
private ImmutableCredentialRecordBuilder() {
|
||||
}
|
||||
|
||||
private ImmutableCredentialRecordBuilder(CredentialRecord other) {
|
||||
this.credentialType = other.getCredentialType();
|
||||
this.credentialId = other.getCredentialId();
|
||||
this.userEntityUserId = other.getUserEntityUserId();
|
||||
this.publicKey = other.getPublicKey();
|
||||
this.signatureCount = other.getSignatureCount();
|
||||
this.uvInitialized = other.isUvInitialized();
|
||||
this.transports = other.getTransports();
|
||||
this.backupEligible = other.isBackupEligible();
|
||||
this.backupState = other.isBackupState();
|
||||
this.attestationObject = other.getAttestationObject();
|
||||
this.attestationClientDataJSON = other.getAttestationClientDataJSON();
|
||||
this.created = other.getCreated();
|
||||
this.lastUsed = other.getLastUsed();
|
||||
this.label = other.getLabel();
|
||||
}
|
||||
|
||||
public ImmutableCredentialRecordBuilder credentialType(PublicKeyCredentialType credentialType) {
|
||||
this.credentialType = credentialType;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ImmutableCredentialRecordBuilder credentialId(Bytes credentialId) {
|
||||
this.credentialId = credentialId;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ImmutableCredentialRecordBuilder userEntityUserId(Bytes userEntityUserId) {
|
||||
this.userEntityUserId = userEntityUserId;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ImmutableCredentialRecordBuilder publicKey(PublicKeyCose publicKey) {
|
||||
this.publicKey = publicKey;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ImmutableCredentialRecordBuilder signatureCount(long signatureCount) {
|
||||
this.signatureCount = signatureCount;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ImmutableCredentialRecordBuilder uvInitialized(boolean uvInitialized) {
|
||||
this.uvInitialized = uvInitialized;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ImmutableCredentialRecordBuilder transports(Set<AuthenticatorTransport> transports) {
|
||||
this.transports = transports;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ImmutableCredentialRecordBuilder backupEligible(boolean backupEligible) {
|
||||
this.backupEligible = backupEligible;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ImmutableCredentialRecordBuilder backupState(boolean backupState) {
|
||||
this.backupState = backupState;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ImmutableCredentialRecordBuilder attestationObject(Bytes attestationObject) {
|
||||
this.attestationObject = attestationObject;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ImmutableCredentialRecordBuilder attestationClientDataJSON(Bytes attestationClientDataJSON) {
|
||||
this.attestationClientDataJSON = attestationClientDataJSON;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ImmutableCredentialRecordBuilder created(Instant created) {
|
||||
this.created = created;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ImmutableCredentialRecordBuilder lastUsed(Instant lastUsed) {
|
||||
this.lastUsed = lastUsed;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ImmutableCredentialRecordBuilder label(String label) {
|
||||
this.label = label;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ImmutableCredentialRecord build() {
|
||||
return new ImmutableCredentialRecord(this.credentialType, this.credentialId, this.userEntityUserId,
|
||||
this.publicKey, this.signatureCount, this.uvInitialized, this.transports, this.backupEligible,
|
||||
this.backupState, this.attestationObject, this.attestationClientDataJSON, this.created,
|
||||
this.lastUsed, this.label);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
-55
@@ -1,55 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2024 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.
|
||||
*/
|
||||
|
||||
package org.springframework.security.web.webauthn.api;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Base64;
|
||||
|
||||
/**
|
||||
* An immutable {@link PublicKeyCose}
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 6.4
|
||||
*/
|
||||
public class ImmutablePublicKeyCose implements PublicKeyCose {
|
||||
|
||||
private final byte[] bytes;
|
||||
|
||||
/**
|
||||
* Creates a new instance.
|
||||
* @param bytes the raw bytes of the public key.
|
||||
*/
|
||||
public ImmutablePublicKeyCose(byte[] bytes) {
|
||||
this.bytes = Arrays.copyOf(bytes, bytes.length);
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getBytes() {
|
||||
return Arrays.copyOf(this.bytes, this.bytes.length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new instance form a Base64 URL encoded String
|
||||
* @param base64EncodedString the base64EncodedString encoded String
|
||||
* @return
|
||||
*/
|
||||
public static ImmutablePublicKeyCose fromBase64(String base64EncodedString) {
|
||||
byte[] decode = Base64.getUrlDecoder().decode(base64EncodedString);
|
||||
return new ImmutablePublicKeyCose(decode);
|
||||
}
|
||||
|
||||
}
|
||||
-193
@@ -1,193 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2025 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.
|
||||
*/
|
||||
|
||||
package org.springframework.security.web.webauthn.api;
|
||||
|
||||
import java.io.Serial;
|
||||
|
||||
/**
|
||||
* <a href=
|
||||
* "https://www.w3.org/TR/webauthn-3/#dictdef-publickeycredentialuserentity">PublicKeyCredentialUserEntity</a>
|
||||
* is used to supply additional
|
||||
* <a href="https://www.w3.org/TR/webauthn-3/#user-account">user account</a> attributes
|
||||
* when creating a new credential.
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 6.4
|
||||
*/
|
||||
public final class ImmutablePublicKeyCredentialUserEntity implements PublicKeyCredentialUserEntity {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = -3438693960347279759L;
|
||||
|
||||
/**
|
||||
* When inherited by PublicKeyCredentialUserEntity, it is a human-palatable identifier
|
||||
* for a user account. It is intended only for display, i.e., aiding the user in
|
||||
* determining the difference between user accounts with similar displayNames. For
|
||||
* example, "alexm", "alex.mueller@example.com" or "+14255551234".
|
||||
*
|
||||
* The Relying Party MAY let the user choose this value. The Relying Party SHOULD
|
||||
* perform enforcement, as prescribed in Section 3.4.3 of [RFC8265] for the
|
||||
* UsernameCasePreserved Profile of the PRECIS IdentifierClass [RFC8264], when setting
|
||||
* name's value, or displaying the value to the user.
|
||||
*
|
||||
* This string MAY contain language and direction metadata. Relying Parties SHOULD
|
||||
* consider providing this information. See 6.4.2 Language and Direction Encoding
|
||||
* about how this metadata is encoded.
|
||||
*
|
||||
* Clients SHOULD perform enforcement, as prescribed in Section 3.4.3 of [RFC8265] for
|
||||
* the UsernameCasePreserved Profile of the PRECIS IdentifierClass [RFC8264], on
|
||||
* name's value prior to displaying the value to the user or including the value as a
|
||||
* parameter of the authenticatorMakeCredential operation.
|
||||
*/
|
||||
private final String name;
|
||||
|
||||
/**
|
||||
* The user handle of the user account entity. A user handle is an opaque byte
|
||||
* sequence with a maximum size of 64 bytes, and is not meant to be displayed to the
|
||||
* user.
|
||||
*
|
||||
* To ensure secure operation, authentication and authorization decisions MUST be made
|
||||
* on the basis of this id member, not the displayName nor name members. See Section
|
||||
* 6.1 of [RFC8266].
|
||||
*
|
||||
* The user handle MUST NOT contain personally identifying information about the user,
|
||||
* such as a username or e-mail address; see 14.6.1 User Handle Contents for details.
|
||||
* The user handle MUST NOT be empty, though it MAY be null.
|
||||
*
|
||||
* Note: the user handle ought not be a constant value across different accounts, even
|
||||
* for non-discoverable credentials, because some authenticators always create
|
||||
* discoverable credentials. Thus a constant user handle would prevent a user from
|
||||
* using such an authenticator with more than one account at the Relying Party.
|
||||
*/
|
||||
private final Bytes id;
|
||||
|
||||
/**
|
||||
* A human-palatable name for the user account, intended only for display. The Relying
|
||||
* Party SHOULD let the user choose this, and SHOULD NOT restrict the choice more than
|
||||
* necessary.
|
||||
*
|
||||
* Relying Parties SHOULD perform enforcement, as prescribed in Section 2.3 of
|
||||
* [RFC8266] for the Nickname Profile of the PRECIS FreeformClass [RFC8264], when
|
||||
* setting displayName's value, or displaying the value to the user.
|
||||
*
|
||||
* This string MAY contain language and direction metadata. Relying Parties SHOULD
|
||||
* consider providing this information. See 6.4.2 Language and Direction Encoding
|
||||
* about how this metadata is encoded.
|
||||
*
|
||||
* Clients SHOULD perform enforcement, as prescribed in Section 2.3 of [RFC8266] for
|
||||
* the Nickname Profile of the PRECIS FreeformClass [RFC8264], on displayName's value
|
||||
* prior to displaying the value to the user or including the value as a parameter of
|
||||
* the authenticatorMakeCredential operation.
|
||||
*
|
||||
* When clients, client platforms, or authenticators display a displayName's value,
|
||||
* they should always use UI elements to provide a clear boundary around the displayed
|
||||
* value, and not allow overflow into other elements [css-overflow-3].
|
||||
*
|
||||
* Authenticators MUST accept and store a 64-byte minimum length for a displayName
|
||||
* member's value. Authenticators MAY truncate a displayName member's value so that it
|
||||
* fits within 64 bytes. See 6.4.1 String Truncation about truncation and other
|
||||
* considerations.
|
||||
*/
|
||||
private final String displayName;
|
||||
|
||||
private ImmutablePublicKeyCredentialUserEntity(String name, Bytes id, String displayName) {
|
||||
this.name = name;
|
||||
this.id = id;
|
||||
this.displayName = displayName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Bytes getId() {
|
||||
return this.id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDisplayName() {
|
||||
return this.displayName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link PublicKeyCredentialUserEntityBuilder}
|
||||
* @return a new {@link PublicKeyCredentialUserEntityBuilder}
|
||||
*/
|
||||
public static PublicKeyCredentialUserEntityBuilder builder() {
|
||||
return new PublicKeyCredentialUserEntityBuilder();
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to build {@link PublicKeyCredentialUserEntity}.
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 6.4
|
||||
*/
|
||||
public static final class PublicKeyCredentialUserEntityBuilder {
|
||||
|
||||
private String name;
|
||||
|
||||
private Bytes id;
|
||||
|
||||
private String displayName;
|
||||
|
||||
private PublicKeyCredentialUserEntityBuilder() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link #getName()} property.
|
||||
* @param name the name
|
||||
* @return the {@link PublicKeyCredentialUserEntityBuilder}
|
||||
*/
|
||||
public PublicKeyCredentialUserEntityBuilder name(String name) {
|
||||
this.name = name;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link #getId()} property.
|
||||
* @param id the id
|
||||
* @return the {@link PublicKeyCredentialUserEntityBuilder}
|
||||
*/
|
||||
public PublicKeyCredentialUserEntityBuilder id(Bytes id) {
|
||||
this.id = id;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link #getDisplayName()} property.
|
||||
* @param displayName the display name
|
||||
* @return the {@link PublicKeyCredentialUserEntityBuilder}
|
||||
*/
|
||||
public PublicKeyCredentialUserEntityBuilder displayName(String displayName) {
|
||||
this.displayName = displayName;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a new {@link PublicKeyCredentialUserEntity}
|
||||
* @return a new {@link PublicKeyCredentialUserEntity}
|
||||
*/
|
||||
public PublicKeyCredentialUserEntity build() {
|
||||
return new ImmutablePublicKeyCredentialUserEntity(this.name, this.id, this.displayName);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2024 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.
|
||||
*/
|
||||
|
||||
package org.springframework.security.web.webauthn.api;
|
||||
|
||||
/**
|
||||
* A <a href="https://www.w3.org/TR/webauthn-3/#sctn-encoded-credPubKey-examples">COSE
|
||||
* encoded public key</a>.
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 6.4
|
||||
*/
|
||||
public interface PublicKeyCose {
|
||||
|
||||
/**
|
||||
* The byes of a COSE encoded public key.
|
||||
* @return the bytes of a COSE encoded public key.
|
||||
*/
|
||||
byte[] getBytes();
|
||||
|
||||
}
|
||||
-229
@@ -1,229 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2025 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.
|
||||
*/
|
||||
|
||||
package org.springframework.security.web.webauthn.api;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* <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
|
||||
* created, or a new assertion is requested.
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 6.4
|
||||
*/
|
||||
public final class PublicKeyCredential<R extends AuthenticatorResponse> implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = -1864035469276082606L;
|
||||
|
||||
private final String id;
|
||||
|
||||
private final PublicKeyCredentialType type;
|
||||
|
||||
private final Bytes rawId;
|
||||
|
||||
private final R response;
|
||||
|
||||
private final AuthenticatorAttachment authenticatorAttachment;
|
||||
|
||||
private final AuthenticationExtensionsClientOutputs clientExtensionResults;
|
||||
|
||||
private PublicKeyCredential(String id, PublicKeyCredentialType type, Bytes rawId, R response,
|
||||
AuthenticatorAttachment authenticatorAttachment,
|
||||
AuthenticationExtensionsClientOutputs clientExtensionResults) {
|
||||
this.id = id;
|
||||
this.type = type;
|
||||
this.rawId = rawId;
|
||||
this.response = response;
|
||||
this.authenticatorAttachment = authenticatorAttachment;
|
||||
this.clientExtensionResults = clientExtensionResults;
|
||||
}
|
||||
|
||||
/**
|
||||
* The
|
||||
* <a href="https://www.w3.org/TR/credential-management-1/#dom-credential-id">id</a>
|
||||
* attribute is inherited from Credential, though PublicKeyCredential overrides
|
||||
* Credential's getter, instead returning the base64url encoding of the data contained
|
||||
* in the object's [[identifier]] internal slot.
|
||||
*/
|
||||
public String getId() {
|
||||
return this.id;
|
||||
}
|
||||
|
||||
/**
|
||||
* The <a href=
|
||||
* "https://www.w3.org/TR/credential-management-1/#dom-credential-type">type</a>
|
||||
* attribute returns the value of the object's interface object's [[type]] slot, which
|
||||
* specifies the credential type represented by this object.
|
||||
* @return the credential type
|
||||
*/
|
||||
public PublicKeyCredentialType getType() {
|
||||
return this.type;
|
||||
}
|
||||
|
||||
/**
|
||||
* The
|
||||
* <a href="https://www.w3.org/TR/webauthn-3/#dom-publickeycredential-rawid">rawId</a>
|
||||
* returns the raw identifier.
|
||||
* @return the raw id
|
||||
*/
|
||||
public Bytes getRawId() {
|
||||
return this.rawId;
|
||||
}
|
||||
|
||||
/**
|
||||
* The <a href=
|
||||
* "https://www.w3.org/TR/webauthn-3/#dom-publickeycredential-response">response</a>
|
||||
* to the client's request to either create a public key credential, or generate an
|
||||
* authentication assertion.
|
||||
* @return the response
|
||||
*/
|
||||
public R getResponse() {
|
||||
return this.response;
|
||||
}
|
||||
|
||||
/**
|
||||
* The <a href=
|
||||
* "https://www.w3.org/TR/webauthn-3/#dom-publickeycredential-authenticatorattachment">authenticatorAttachment</a>
|
||||
* reports the <a href=
|
||||
* "https://www.w3.org/TR/webauthn-3/#authenticator-attachment-modality">authenticator
|
||||
* attachment modality</a> in effect at the time the navigator.credentials.create() or
|
||||
* navigator.credentials.get() methods successfully complete.
|
||||
* @return the authenticator attachment
|
||||
*/
|
||||
public AuthenticatorAttachment getAuthenticatorAttachment() {
|
||||
return this.authenticatorAttachment;
|
||||
}
|
||||
|
||||
/**
|
||||
* The <a href=
|
||||
* "https://www.w3.org/TR/webauthn-3/#dom-publickeycredential-getclientextensionresults">clientExtensionsResults</a>
|
||||
* is a mapping of extension identifier to client extension output.
|
||||
* @return the extension results
|
||||
*/
|
||||
public AuthenticationExtensionsClientOutputs getClientExtensionResults() {
|
||||
return this.clientExtensionResults;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link PublicKeyCredentialBuilder}
|
||||
* @param <T> the response type
|
||||
* @return the {@link PublicKeyCredentialBuilder}
|
||||
*/
|
||||
public static <T extends AuthenticatorResponse> PublicKeyCredentialBuilder<T> builder() {
|
||||
return new PublicKeyCredentialBuilder<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* The {@link PublicKeyCredentialBuilder}
|
||||
*
|
||||
* @param <R> the response type
|
||||
* @author Rob Winch
|
||||
* @since 6.4
|
||||
*/
|
||||
public static final class PublicKeyCredentialBuilder<R extends AuthenticatorResponse> {
|
||||
|
||||
private String id;
|
||||
|
||||
private PublicKeyCredentialType type;
|
||||
|
||||
private Bytes rawId;
|
||||
|
||||
private R response;
|
||||
|
||||
private AuthenticatorAttachment authenticatorAttachment;
|
||||
|
||||
private AuthenticationExtensionsClientOutputs clientExtensionResults;
|
||||
|
||||
private PublicKeyCredentialBuilder() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link #getId()} property
|
||||
* @param id the id
|
||||
* @return the PublicKeyCredentialBuilder
|
||||
*/
|
||||
public PublicKeyCredentialBuilder id(String id) {
|
||||
this.id = id;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link #getType()} property.
|
||||
* @param type the type
|
||||
* @return the PublicKeyCredentialBuilder
|
||||
*/
|
||||
public PublicKeyCredentialBuilder type(PublicKeyCredentialType type) {
|
||||
this.type = type;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link #getRawId()} property.
|
||||
* @param rawId the raw id
|
||||
* @return the PublicKeyCredentialBuilder
|
||||
*/
|
||||
public PublicKeyCredentialBuilder rawId(Bytes rawId) {
|
||||
this.rawId = rawId;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link #getResponse()} property.
|
||||
* @param response the response
|
||||
* @return the PublicKeyCredentialBuilder
|
||||
*/
|
||||
public PublicKeyCredentialBuilder response(R response) {
|
||||
this.response = response;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link #getAuthenticatorAttachment()} property.
|
||||
* @param authenticatorAttachment the authenticator attachement
|
||||
* @return the PublicKeyCredentialBuilder
|
||||
*/
|
||||
public PublicKeyCredentialBuilder authenticatorAttachment(AuthenticatorAttachment authenticatorAttachment) {
|
||||
this.authenticatorAttachment = authenticatorAttachment;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link #getClientExtensionResults()} property.
|
||||
* @param clientExtensionResults the client extension results
|
||||
* @return the PublicKeyCredentialBuilder
|
||||
*/
|
||||
public PublicKeyCredentialBuilder clientExtensionResults(
|
||||
AuthenticationExtensionsClientOutputs clientExtensionResults) {
|
||||
this.clientExtensionResults = clientExtensionResults;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link PublicKeyCredential}
|
||||
* @return a new {@link PublicKeyCredential}
|
||||
*/
|
||||
public PublicKeyCredential<R> build() {
|
||||
return new PublicKeyCredential(this.id, this.type, this.rawId, this.response, this.authenticatorAttachment,
|
||||
this.clientExtensionResults);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
-332
@@ -1,332 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2024 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.
|
||||
*/
|
||||
|
||||
package org.springframework.security.web.webauthn.api;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* Represents the <a href=
|
||||
* "https://www.w3.org/TR/webauthn-3/#dictdef-publickeycredentialcreationoptions">PublicKeyCredentialCreationOptions</a>
|
||||
* which is an argument to <a href=
|
||||
* "https://w3c.github.io/webappsec-credential-management/#dom-credentialscontainer-create">creating</a>
|
||||
* a new credential.
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 6.4
|
||||
*/
|
||||
public final class PublicKeyCredentialCreationOptions {
|
||||
|
||||
private final PublicKeyCredentialRpEntity rp;
|
||||
|
||||
private final PublicKeyCredentialUserEntity user;
|
||||
|
||||
private final Bytes challenge;
|
||||
|
||||
private final List<PublicKeyCredentialParameters> pubKeyCredParams;
|
||||
|
||||
private final Duration timeout;
|
||||
|
||||
private final List<PublicKeyCredentialDescriptor> excludeCredentials;
|
||||
|
||||
private final AuthenticatorSelectionCriteria authenticatorSelection;
|
||||
|
||||
private final AttestationConveyancePreference attestation;
|
||||
|
||||
private final AuthenticationExtensionsClientInputs extensions;
|
||||
|
||||
private PublicKeyCredentialCreationOptions(PublicKeyCredentialRpEntity rp, PublicKeyCredentialUserEntity user,
|
||||
Bytes challenge, List<PublicKeyCredentialParameters> pubKeyCredParams, Duration timeout,
|
||||
List<PublicKeyCredentialDescriptor> excludeCredentials,
|
||||
AuthenticatorSelectionCriteria authenticatorSelection, AttestationConveyancePreference attestation,
|
||||
AuthenticationExtensionsClientInputs extensions) {
|
||||
this.rp = rp;
|
||||
this.user = user;
|
||||
this.challenge = challenge;
|
||||
this.pubKeyCredParams = pubKeyCredParams;
|
||||
this.timeout = timeout;
|
||||
this.excludeCredentials = excludeCredentials;
|
||||
this.authenticatorSelection = authenticatorSelection;
|
||||
this.attestation = attestation;
|
||||
this.extensions = extensions;
|
||||
}
|
||||
|
||||
/**
|
||||
* The <a href=
|
||||
* "https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialcreationoptions-rp">rp</a>
|
||||
* property contains data about the Relying Party responsible for the request.
|
||||
* @return the relying party
|
||||
*/
|
||||
public PublicKeyCredentialRpEntity getRp() {
|
||||
return this.rp;
|
||||
}
|
||||
|
||||
/**
|
||||
* The <a href=
|
||||
* "https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialcreationoptions-user">user</a>
|
||||
* contains names and an identifier for the user account performing the registration.
|
||||
* @return the user
|
||||
*/
|
||||
public PublicKeyCredentialUserEntity getUser() {
|
||||
return this.user;
|
||||
}
|
||||
|
||||
/**
|
||||
* The <a href=
|
||||
* "https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialcreationoptions-challenge">challenge</a>
|
||||
* specifies the challenge that the authenticator signs, along with other data, when
|
||||
* producing an attestation object for the newly created credential.
|
||||
* @return the challenge
|
||||
*/
|
||||
public Bytes getChallenge() {
|
||||
return this.challenge;
|
||||
}
|
||||
|
||||
/**
|
||||
* The <a href=
|
||||
* "https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialcreationoptions-pubkeycredparams">publicKeyCredParams</a>
|
||||
* params lisst the key types and signature algorithms the Relying Party Supports,
|
||||
* ordered from most preferred to least preferred.
|
||||
* @return the public key credential parameters
|
||||
*/
|
||||
public List<PublicKeyCredentialParameters> getPubKeyCredParams() {
|
||||
return this.pubKeyCredParams;
|
||||
}
|
||||
|
||||
/**
|
||||
* The <a href=
|
||||
* "https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialcreationoptions-timeout">timeout</a>
|
||||
* property specifies a time, in milliseconds, that the Relying Party is willing to
|
||||
* wait for the call to complete.
|
||||
* @return the timeout
|
||||
*/
|
||||
public Duration getTimeout() {
|
||||
return this.timeout;
|
||||
}
|
||||
|
||||
/**
|
||||
* The <a href=
|
||||
* "https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialcreationoptions-excludecredentials">excludeCredentials</a>
|
||||
* property is the OPTIONAL member used by the Relying Party to list any existing
|
||||
* credentials mapped to this user account (as identified by user.id).
|
||||
* @return exclude credentials
|
||||
*/
|
||||
public List<PublicKeyCredentialDescriptor> getExcludeCredentials() {
|
||||
return this.excludeCredentials;
|
||||
}
|
||||
|
||||
/**
|
||||
* The <a href=
|
||||
* "https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialcreationoptions-authenticatorselection">authenticatorSelection</a>
|
||||
* property is an OPTIONAL member used by the Relying Party to list any existing
|
||||
* credentials mapped to this user account (as identified by user.id).
|
||||
* @return the authenticatorSelection
|
||||
*/
|
||||
public AuthenticatorSelectionCriteria getAuthenticatorSelection() {
|
||||
return this.authenticatorSelection;
|
||||
}
|
||||
|
||||
/**
|
||||
* The <a href=
|
||||
* "https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialcreationoptions-attestation">attestation</a>
|
||||
* property is an OPTIONAL member used by the Relying Party to specify a preference
|
||||
* regarding attestation conveyance.
|
||||
* @return the attestation preference
|
||||
*/
|
||||
public AttestationConveyancePreference getAttestation() {
|
||||
return this.attestation;
|
||||
}
|
||||
|
||||
/**
|
||||
* The <a href=
|
||||
* "https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialcreationoptions-extensions">extensions</a>
|
||||
* property is an OPTIONAL member used by the Relying Party to provide client
|
||||
* extension inputs requesting additional processing by the client and authenticator.
|
||||
* @return the extensions
|
||||
*/
|
||||
public AuthenticationExtensionsClientInputs getExtensions() {
|
||||
return this.extensions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link PublicKeyCredentialCreationOptions}
|
||||
* @return a new {@link PublicKeyCredentialCreationOptions}
|
||||
*/
|
||||
public static PublicKeyCredentialCreationOptionsBuilder builder() {
|
||||
return new PublicKeyCredentialCreationOptionsBuilder();
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to build {@link PublicKeyCredentialCreationOptions}.
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 6.4
|
||||
*/
|
||||
public static final class PublicKeyCredentialCreationOptionsBuilder {
|
||||
|
||||
private PublicKeyCredentialRpEntity rp;
|
||||
|
||||
private PublicKeyCredentialUserEntity user;
|
||||
|
||||
private Bytes challenge;
|
||||
|
||||
private List<PublicKeyCredentialParameters> pubKeyCredParams = new ArrayList<>();
|
||||
|
||||
private Duration timeout;
|
||||
|
||||
private List<PublicKeyCredentialDescriptor> excludeCredentials = new ArrayList<>();
|
||||
|
||||
private AuthenticatorSelectionCriteria authenticatorSelection;
|
||||
|
||||
private AttestationConveyancePreference attestation;
|
||||
|
||||
private AuthenticationExtensionsClientInputs extensions;
|
||||
|
||||
private PublicKeyCredentialCreationOptionsBuilder() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link #getRp()} property.
|
||||
* @param rp the relying party
|
||||
* @return the PublicKeyCredentialCreationOptionsBuilder
|
||||
*/
|
||||
public PublicKeyCredentialCreationOptionsBuilder rp(PublicKeyCredentialRpEntity rp) {
|
||||
this.rp = rp;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link #getUser()} property.
|
||||
* @param user the user entity
|
||||
* @return the PublicKeyCredentialCreationOptionsBuilder
|
||||
*/
|
||||
public PublicKeyCredentialCreationOptionsBuilder user(PublicKeyCredentialUserEntity user) {
|
||||
this.user = user;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link #getChallenge()} property.
|
||||
* @param challenge the challenge
|
||||
* @return the PublicKeyCredentialCreationOptionsBuilder
|
||||
*/
|
||||
public PublicKeyCredentialCreationOptionsBuilder challenge(Bytes challenge) {
|
||||
this.challenge = challenge;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link #getPubKeyCredParams()} property.
|
||||
* @param pubKeyCredParams the public key credential parameters
|
||||
* @return the PublicKeyCredentialCreationOptionsBuilder
|
||||
*/
|
||||
public PublicKeyCredentialCreationOptionsBuilder pubKeyCredParams(
|
||||
PublicKeyCredentialParameters... pubKeyCredParams) {
|
||||
return pubKeyCredParams(Arrays.asList(pubKeyCredParams));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link #getPubKeyCredParams()} property.
|
||||
* @param pubKeyCredParams the public key credential parameters
|
||||
* @return the PublicKeyCredentialCreationOptionsBuilder
|
||||
*/
|
||||
public PublicKeyCredentialCreationOptionsBuilder pubKeyCredParams(
|
||||
List<PublicKeyCredentialParameters> pubKeyCredParams) {
|
||||
this.pubKeyCredParams = pubKeyCredParams;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link #getTimeout()} property.
|
||||
* @param timeout the timeout
|
||||
* @return the PublicKeyCredentialCreationOptionsBuilder
|
||||
*/
|
||||
public PublicKeyCredentialCreationOptionsBuilder timeout(Duration timeout) {
|
||||
this.timeout = timeout;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link #getExcludeCredentials()} property.
|
||||
* @param excludeCredentials the excluded credentials.
|
||||
* @return the PublicKeyCredentialCreationOptionsBuilder
|
||||
*/
|
||||
public PublicKeyCredentialCreationOptionsBuilder excludeCredentials(
|
||||
List<PublicKeyCredentialDescriptor> excludeCredentials) {
|
||||
this.excludeCredentials = excludeCredentials;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link #getAuthenticatorSelection()} property.
|
||||
* @param authenticatorSelection the authenticator selection
|
||||
* @return the PublicKeyCredentialCreationOptionsBuilder
|
||||
*/
|
||||
public PublicKeyCredentialCreationOptionsBuilder authenticatorSelection(
|
||||
AuthenticatorSelectionCriteria authenticatorSelection) {
|
||||
this.authenticatorSelection = authenticatorSelection;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link #getAttestation()} property.
|
||||
* @param attestation the attestation
|
||||
* @return the PublicKeyCredentialCreationOptionsBuilder
|
||||
*/
|
||||
public PublicKeyCredentialCreationOptionsBuilder attestation(AttestationConveyancePreference attestation) {
|
||||
this.attestation = attestation;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link #getExtensions()} property.
|
||||
* @param extensions the extensions
|
||||
* @return the PublicKeyCredentialCreationOptionsBuilder
|
||||
*/
|
||||
public PublicKeyCredentialCreationOptionsBuilder extensions(AuthenticationExtensionsClientInputs extensions) {
|
||||
this.extensions = extensions;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows customizing the builder using the {@link Consumer} that is passed in.
|
||||
* @param customizer the {@link Consumer} that can be used to customize the
|
||||
* {@link PublicKeyCredentialCreationOptionsBuilder}
|
||||
* @return the PublicKeyCredentialCreationOptionsBuilder
|
||||
*/
|
||||
public PublicKeyCredentialCreationOptionsBuilder customize(
|
||||
Consumer<PublicKeyCredentialCreationOptionsBuilder> customizer) {
|
||||
customizer.accept(this);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a new {@link PublicKeyCredentialCreationOptions}
|
||||
* @return the new {@link PublicKeyCredentialCreationOptions}
|
||||
*/
|
||||
public PublicKeyCredentialCreationOptions build() {
|
||||
return new PublicKeyCredentialCreationOptions(this.rp, this.user, this.challenge, this.pubKeyCredParams,
|
||||
this.timeout, this.excludeCredentials, this.authenticatorSelection, this.attestation,
|
||||
this.extensions);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
-159
@@ -1,159 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2025 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.
|
||||
*/
|
||||
|
||||
package org.springframework.security.web.webauthn.api;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* <a href=
|
||||
* "https://www.w3.org/TR/webauthn-3/#dictdef-publickeycredentialdescriptor">PublicKeyCredentialDescriptor</a>
|
||||
* identifies a specific public key credential. It is used in create() to prevent creating
|
||||
* duplicate credentials on the same authenticator, and in get() to determine if and how
|
||||
* the credential can currently be reached by the client. It mirrors some fields of the
|
||||
* PublicKeyCredential object returned by create() and get().
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 6.4
|
||||
*/
|
||||
public final class PublicKeyCredentialDescriptor implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 8793385059692676240L;
|
||||
|
||||
private final PublicKeyCredentialType type;
|
||||
|
||||
private final Bytes id;
|
||||
|
||||
private final Set<AuthenticatorTransport> transports;
|
||||
|
||||
private PublicKeyCredentialDescriptor(PublicKeyCredentialType type, Bytes id,
|
||||
Set<AuthenticatorTransport> transports) {
|
||||
this.type = type;
|
||||
this.id = id;
|
||||
this.transports = transports;
|
||||
}
|
||||
|
||||
/**
|
||||
* The <a href=
|
||||
* "https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialdescriptor-type">type</a>
|
||||
* property contains the type of the public key credential the caller is referring to.
|
||||
* @return the type
|
||||
*/
|
||||
public PublicKeyCredentialType getType() {
|
||||
return this.type;
|
||||
}
|
||||
|
||||
/**
|
||||
* The <a href=
|
||||
* "https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialdescriptor-id">id</a>
|
||||
* property contains the credential ID of the public key credential the caller is
|
||||
* referring to.
|
||||
* @return the id
|
||||
*/
|
||||
public Bytes getId() {
|
||||
return this.id;
|
||||
}
|
||||
|
||||
/**
|
||||
* The <a href=
|
||||
* "https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialdescriptor-transports">transports</a>
|
||||
* property is an OPTIONAL member that contains a hint as to how the client might
|
||||
* communicate with the managing authenticator of the public key credential the caller
|
||||
* is referring to.
|
||||
* @return the transports
|
||||
*/
|
||||
public Set<AuthenticatorTransport> getTransports() {
|
||||
return this.transports;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link PublicKeyCredentialDescriptorBuilder}
|
||||
* @return a new {@link PublicKeyCredentialDescriptorBuilder}
|
||||
*/
|
||||
public static PublicKeyCredentialDescriptorBuilder builder() {
|
||||
return new PublicKeyCredentialDescriptorBuilder();
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to create {@link PublicKeyCredentialDescriptor}
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 6.4
|
||||
*/
|
||||
public static final class PublicKeyCredentialDescriptorBuilder {
|
||||
|
||||
private PublicKeyCredentialType type = PublicKeyCredentialType.PUBLIC_KEY;
|
||||
|
||||
private Bytes id;
|
||||
|
||||
private Set<AuthenticatorTransport> transports;
|
||||
|
||||
private PublicKeyCredentialDescriptorBuilder() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link #getType()} property.
|
||||
* @param type the type
|
||||
* @return the {@link PublicKeyCredentialDescriptorBuilder}
|
||||
*/
|
||||
public PublicKeyCredentialDescriptorBuilder type(PublicKeyCredentialType type) {
|
||||
this.type = type;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link #getId()} property.
|
||||
* @param id the id
|
||||
* @return the {@link PublicKeyCredentialDescriptorBuilder}
|
||||
*/
|
||||
public PublicKeyCredentialDescriptorBuilder id(Bytes id) {
|
||||
this.id = id;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link #getTransports()} property.
|
||||
* @param transports the transports
|
||||
* @return the {@link PublicKeyCredentialDescriptorBuilder}
|
||||
*/
|
||||
public PublicKeyCredentialDescriptorBuilder transports(Set<AuthenticatorTransport> transports) {
|
||||
this.transports = transports;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link #getTransports()} property.
|
||||
* @param transports the transports
|
||||
* @return the {@link PublicKeyCredentialDescriptorBuilder}
|
||||
*/
|
||||
public PublicKeyCredentialDescriptorBuilder transports(AuthenticatorTransport... transports) {
|
||||
return transports(Set.of(transports));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link PublicKeyCredentialDescriptor}
|
||||
* @return a new {@link PublicKeyCredentialDescriptor}
|
||||
*/
|
||||
public PublicKeyCredentialDescriptor build() {
|
||||
return new PublicKeyCredentialDescriptor(this.type, this.id, this.transports);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
-99
@@ -1,99 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2024 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.
|
||||
*/
|
||||
|
||||
package org.springframework.security.web.webauthn.api;
|
||||
|
||||
/**
|
||||
* The <a href=
|
||||
* "https://www.w3.org/TR/webauthn-3/#dictdef-publickeycredentialparameters">PublicKeyCredentialParameters</a>
|
||||
* is used to supply additional parameters when creating a new credential.
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 6.4
|
||||
* @see PublicKeyCredentialCreationOptions#getPubKeyCredParams()
|
||||
*/
|
||||
public final class PublicKeyCredentialParameters {
|
||||
|
||||
public static final PublicKeyCredentialParameters EdDSA = new PublicKeyCredentialParameters(
|
||||
COSEAlgorithmIdentifier.EdDSA);
|
||||
|
||||
public static final PublicKeyCredentialParameters ES256 = new PublicKeyCredentialParameters(
|
||||
COSEAlgorithmIdentifier.ES256);
|
||||
|
||||
public static final PublicKeyCredentialParameters ES384 = new PublicKeyCredentialParameters(
|
||||
COSEAlgorithmIdentifier.ES384);
|
||||
|
||||
public static final PublicKeyCredentialParameters ES512 = new PublicKeyCredentialParameters(
|
||||
COSEAlgorithmIdentifier.ES512);
|
||||
|
||||
public static final PublicKeyCredentialParameters RS256 = new PublicKeyCredentialParameters(
|
||||
COSEAlgorithmIdentifier.RS256);
|
||||
|
||||
public static final PublicKeyCredentialParameters RS384 = new PublicKeyCredentialParameters(
|
||||
COSEAlgorithmIdentifier.RS384);
|
||||
|
||||
public static final PublicKeyCredentialParameters RS512 = new PublicKeyCredentialParameters(
|
||||
COSEAlgorithmIdentifier.RS512);
|
||||
|
||||
public static final PublicKeyCredentialParameters RS1 = new PublicKeyCredentialParameters(
|
||||
COSEAlgorithmIdentifier.RS1);
|
||||
|
||||
/**
|
||||
* This member specifies the type of credential to be created. The value SHOULD be a
|
||||
* member of PublicKeyCredentialType but client platforms MUST ignore unknown values,
|
||||
* ignoring any PublicKeyCredentialParameters with an unknown type.
|
||||
*/
|
||||
private final PublicKeyCredentialType type;
|
||||
|
||||
/**
|
||||
* This member specifies the cryptographic signature algorithm with which the newly
|
||||
* generated credential will be used, and thus also the type of asymmetric key pair to
|
||||
* be generated, e.g., RSA or Elliptic Curve.
|
||||
*/
|
||||
private final COSEAlgorithmIdentifier alg;
|
||||
|
||||
private PublicKeyCredentialParameters(COSEAlgorithmIdentifier alg) {
|
||||
this(PublicKeyCredentialType.PUBLIC_KEY, alg);
|
||||
}
|
||||
|
||||
private PublicKeyCredentialParameters(PublicKeyCredentialType type, COSEAlgorithmIdentifier alg) {
|
||||
this.type = type;
|
||||
this.alg = alg;
|
||||
}
|
||||
|
||||
/**
|
||||
* The <a href=
|
||||
* "https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialparameters-type">type</a>
|
||||
* property member specifies the type of credential to be created.
|
||||
* @return the type
|
||||
*/
|
||||
public PublicKeyCredentialType getType() {
|
||||
return this.type;
|
||||
}
|
||||
|
||||
/**
|
||||
* The <a href=
|
||||
* "https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialparameters-alg">alg</a>
|
||||
* member specifies the cryptographic signature algorithm with which the newly
|
||||
* generated credential will be used, and thus also the type of asymmetric key pair to
|
||||
* be generated, e.g., RSA or Elliptic Curve.
|
||||
* @return the algorithm
|
||||
*/
|
||||
public COSEAlgorithmIdentifier getAlg() {
|
||||
return this.alg;
|
||||
}
|
||||
|
||||
}
|
||||
-253
@@ -1,253 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2025 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.
|
||||
*/
|
||||
|
||||
package org.springframework.security.web.webauthn.api;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
import java.time.Duration;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* <a href=
|
||||
* "https://www.w3.org/TR/webauthn-3/#dictdef-publickeycredentialrequestoptions">PublicKeyCredentialRequestOptions</a>
|
||||
* contains the information to create an assertion used for authentication.
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 6.4
|
||||
*/
|
||||
public final class PublicKeyCredentialRequestOptions implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = -2970057592835694354L;
|
||||
|
||||
private final Bytes challenge;
|
||||
|
||||
private final Duration timeout;
|
||||
|
||||
private final String rpId;
|
||||
|
||||
private final List<PublicKeyCredentialDescriptor> allowCredentials;
|
||||
|
||||
private final UserVerificationRequirement userVerification;
|
||||
|
||||
private final AuthenticationExtensionsClientInputs extensions;
|
||||
|
||||
private PublicKeyCredentialRequestOptions(Bytes challenge, Duration timeout, String rpId,
|
||||
List<PublicKeyCredentialDescriptor> allowCredentials, UserVerificationRequirement userVerification,
|
||||
AuthenticationExtensionsClientInputs extensions) {
|
||||
Assert.notNull(challenge, "challenge cannot be null");
|
||||
Assert.hasText(rpId, "rpId cannot be empty");
|
||||
this.challenge = challenge;
|
||||
this.timeout = timeout;
|
||||
this.rpId = rpId;
|
||||
this.allowCredentials = allowCredentials;
|
||||
this.userVerification = userVerification;
|
||||
this.extensions = extensions;
|
||||
}
|
||||
|
||||
/**
|
||||
* The <a href=
|
||||
* "https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialrequestoptions-challenge">challenge</a>
|
||||
* property specifies a challenge that the authenticator signs, along with other data,
|
||||
* when producing an authentication assertion.
|
||||
* @return the challenge
|
||||
*/
|
||||
public Bytes getChallenge() {
|
||||
return this.challenge;
|
||||
}
|
||||
|
||||
/**
|
||||
* The <a href=
|
||||
* "https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialrequestoptions-timeout">timeout</a>
|
||||
* property is an OPTIONAL member specifies a time, in milliseconds, that the Relying
|
||||
* Party is willing to wait for the call to complete.
|
||||
* @return the timeout
|
||||
*/
|
||||
public Duration getTimeout() {
|
||||
return this.timeout;
|
||||
}
|
||||
|
||||
/**
|
||||
* The <a href=
|
||||
* "https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialrequestoptions-rpid">rpId</a>
|
||||
* is an OPTIONAL member specifies the RP ID claimed by the Relying Party. The client
|
||||
* MUST verify that the Relying Party's origin matches the scope of this RP ID.
|
||||
* @return the relying party id
|
||||
*/
|
||||
public String getRpId() {
|
||||
return this.rpId;
|
||||
}
|
||||
|
||||
/**
|
||||
* The <a href=
|
||||
* "https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialrequestoptions-allowcredentials">allowCredentials</a>
|
||||
* property is an OPTIONAL member is used by the client to find authenticators
|
||||
* eligible for this authentication ceremony.
|
||||
* @return the allowCredentials property
|
||||
*/
|
||||
public List<PublicKeyCredentialDescriptor> getAllowCredentials() {
|
||||
return this.allowCredentials;
|
||||
}
|
||||
|
||||
/**
|
||||
* The <a href=
|
||||
* "https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialrequestoptions-userverification">userVerification</a>
|
||||
* property is an OPTIONAL member specifies the Relying Party's requirements regarding
|
||||
* user verification for the get() operation.
|
||||
* @return the user verification
|
||||
*/
|
||||
public UserVerificationRequirement getUserVerification() {
|
||||
return this.userVerification;
|
||||
}
|
||||
|
||||
/**
|
||||
* The <a href=
|
||||
* "https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialrequestoptions-extensions">extensions</a>
|
||||
* is an OPTIONAL property used by the Relying Party to provide client extension
|
||||
* inputs requesting additional processing by the client and authenticator.
|
||||
* @return the extensions
|
||||
*/
|
||||
public AuthenticationExtensionsClientInputs getExtensions() {
|
||||
return this.extensions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@link PublicKeyCredentialRequestOptionsBuilder}
|
||||
* @return the {@link PublicKeyCredentialRequestOptionsBuilder}
|
||||
*/
|
||||
public static PublicKeyCredentialRequestOptionsBuilder builder() {
|
||||
return new PublicKeyCredentialRequestOptionsBuilder();
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to build a {@link PublicKeyCredentialCreationOptions}.
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 6.4
|
||||
*/
|
||||
public static final class PublicKeyCredentialRequestOptionsBuilder {
|
||||
|
||||
private Bytes challenge;
|
||||
|
||||
private Duration timeout = Duration.ofMinutes(5);
|
||||
|
||||
private String rpId;
|
||||
|
||||
private List<PublicKeyCredentialDescriptor> allowCredentials = Collections.emptyList();
|
||||
|
||||
private UserVerificationRequirement userVerification;
|
||||
|
||||
private AuthenticationExtensionsClientInputs extensions = new ImmutableAuthenticationExtensionsClientInputs(
|
||||
new ArrayList<>());
|
||||
|
||||
private PublicKeyCredentialRequestOptionsBuilder() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link #getChallenge()} property.
|
||||
* @param challenge the challenge
|
||||
* @return the {@link PublicKeyCredentialRequestOptionsBuilder}
|
||||
*/
|
||||
public PublicKeyCredentialRequestOptionsBuilder challenge(Bytes challenge) {
|
||||
this.challenge = challenge;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link #getTimeout()} property.
|
||||
* @param timeout the timeout
|
||||
* @return the {@link PublicKeyCredentialRequestOptionsBuilder}
|
||||
*/
|
||||
public PublicKeyCredentialRequestOptionsBuilder timeout(Duration timeout) {
|
||||
Assert.notNull(timeout, "timeout cannot be null");
|
||||
this.timeout = timeout;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link #getRpId()} property.
|
||||
* @param rpId the rpId property
|
||||
* @return the {@link PublicKeyCredentialRequestOptionsBuilder}
|
||||
*/
|
||||
public PublicKeyCredentialRequestOptionsBuilder rpId(String rpId) {
|
||||
this.rpId = rpId;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link #getAllowCredentials()} property
|
||||
* @param allowCredentials the allowed credentials
|
||||
* @return the {@link PublicKeyCredentialRequestOptionsBuilder}
|
||||
*/
|
||||
public PublicKeyCredentialRequestOptionsBuilder allowCredentials(
|
||||
List<PublicKeyCredentialDescriptor> allowCredentials) {
|
||||
Assert.notNull(allowCredentials, "allowCredentials cannot be null");
|
||||
this.allowCredentials = allowCredentials;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link #getUserVerification()} property.
|
||||
* @param userVerification the user verification
|
||||
* @return the {@link PublicKeyCredentialRequestOptionsBuilder}
|
||||
*/
|
||||
public PublicKeyCredentialRequestOptionsBuilder userVerification(UserVerificationRequirement userVerification) {
|
||||
this.userVerification = userVerification;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link #getExtensions()} property
|
||||
* @param extensions the extensions
|
||||
* @return the {@link PublicKeyCredentialRequestOptionsBuilder}
|
||||
*/
|
||||
public PublicKeyCredentialRequestOptionsBuilder extensions(AuthenticationExtensionsClientInputs extensions) {
|
||||
this.extensions = extensions;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows customizing the {@link PublicKeyCredentialRequestOptionsBuilder}
|
||||
* @param customizer the {@link Consumer} used to customize the builder
|
||||
* @return the {@link PublicKeyCredentialRequestOptionsBuilder}
|
||||
*/
|
||||
public PublicKeyCredentialRequestOptionsBuilder customize(
|
||||
Consumer<PublicKeyCredentialRequestOptionsBuilder> customizer) {
|
||||
customizer.accept(this);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a new {@link PublicKeyCredentialRequestOptions}
|
||||
* @return a new {@link PublicKeyCredentialRequestOptions}
|
||||
*/
|
||||
public PublicKeyCredentialRequestOptions build() {
|
||||
if (this.challenge == null) {
|
||||
this.challenge = Bytes.random();
|
||||
}
|
||||
return new PublicKeyCredentialRequestOptions(this.challenge, this.timeout, this.rpId, this.allowCredentials,
|
||||
this.userVerification, this.extensions);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
-115
@@ -1,115 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2024 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.
|
||||
*/
|
||||
|
||||
package org.springframework.security.web.webauthn.api;
|
||||
|
||||
/**
|
||||
* The <a href=
|
||||
* "https://www.w3.org/TR/webauthn-3/#dictdef-publickeycredentialrpentity">PublicKeyCredentialRpEntity</a>
|
||||
* dictionary is used to supply additional Relying Party attributes when creating a new
|
||||
* credential.
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 6.4
|
||||
*/
|
||||
public final class PublicKeyCredentialRpEntity {
|
||||
|
||||
private final String name;
|
||||
|
||||
private final String id;
|
||||
|
||||
private PublicKeyCredentialRpEntity(String name, String id) {
|
||||
this.name = name;
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
/**
|
||||
* The <a href=
|
||||
* "https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialentity-name">name</a>
|
||||
* property is a human-palatable name for the entity. Its function depends on what the
|
||||
* PublicKeyCredentialEntity represents for the Relying Party, intended only for
|
||||
* display.
|
||||
* @return the name
|
||||
*/
|
||||
public String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
/**
|
||||
* The <a href=
|
||||
* "https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialrpentity-id">id</a>
|
||||
* property is a unique identifier for the Relying Party entity, which sets the
|
||||
* <a href="https://www.w3.org/TR/webauthn-3/#rp-id">RP ID</a>.
|
||||
* @return the relying party id
|
||||
*/
|
||||
public String getId() {
|
||||
return this.id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link PublicKeyCredentialRpEntityBuilder}
|
||||
* @return a new {@link PublicKeyCredentialRpEntityBuilder}
|
||||
*/
|
||||
public static PublicKeyCredentialRpEntityBuilder builder() {
|
||||
return new PublicKeyCredentialRpEntityBuilder();
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to create a {@link PublicKeyCredentialRpEntity}.
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 6.4
|
||||
*/
|
||||
public static final class PublicKeyCredentialRpEntityBuilder {
|
||||
|
||||
private String name;
|
||||
|
||||
private String id;
|
||||
|
||||
private PublicKeyCredentialRpEntityBuilder() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link #getName()} property.
|
||||
* @param name the name property
|
||||
* @return the {@link PublicKeyCredentialRpEntityBuilder}
|
||||
*/
|
||||
public PublicKeyCredentialRpEntityBuilder name(String name) {
|
||||
this.name = name;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link #getId()} property.
|
||||
* @param id the id
|
||||
* @return the {@link PublicKeyCredentialRpEntityBuilder}
|
||||
*/
|
||||
public PublicKeyCredentialRpEntityBuilder id(String id) {
|
||||
this.id = id;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link PublicKeyCredentialRpEntity}.
|
||||
* @return a new {@link PublicKeyCredentialRpEntity}.
|
||||
*/
|
||||
public PublicKeyCredentialRpEntity build() {
|
||||
return new PublicKeyCredentialRpEntity(this.name, this.id);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
-61
@@ -1,61 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2025 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.
|
||||
*/
|
||||
|
||||
package org.springframework.security.web.webauthn.api;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* The <a href=
|
||||
* "https://www.w3.org/TR/webauthn-3/#enum-credentialType">PublicKeyCredentialType</a>
|
||||
* defines the credential types.
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 6.4
|
||||
*/
|
||||
public final class PublicKeyCredentialType implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 7025333122210061679L;
|
||||
|
||||
/**
|
||||
* The only credential type that currently exists.
|
||||
*/
|
||||
public static final PublicKeyCredentialType PUBLIC_KEY = new PublicKeyCredentialType("public-key");
|
||||
|
||||
private final String value;
|
||||
|
||||
private PublicKeyCredentialType(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value.
|
||||
* @return the value
|
||||
*/
|
||||
public String getValue() {
|
||||
return this.value;
|
||||
}
|
||||
|
||||
public static PublicKeyCredentialType valueOf(String value) {
|
||||
if (PUBLIC_KEY.getValue().equals(value)) {
|
||||
return PUBLIC_KEY;
|
||||
}
|
||||
return new PublicKeyCredentialType(value);
|
||||
}
|
||||
|
||||
}
|
||||
-59
@@ -1,59 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2025 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.
|
||||
*/
|
||||
|
||||
package org.springframework.security.web.webauthn.api;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* <a href=
|
||||
* "https://www.w3.org/TR/webauthn-3/#dictdef-publickeycredentialuserentity">PublicKeyCredentialUserEntity</a>
|
||||
* is used to supply additional
|
||||
* <a href="https://www.w3.org/TR/webauthn-3/#user-account">user account</a> attributes
|
||||
* when creating a new credential.
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 6.4
|
||||
* @see org.springframework.security.web.webauthn.management.WebAuthnRelyingPartyOperations#authenticate(org.springframework.security.web.webauthn.management.RelyingPartyAuthenticationRequest)
|
||||
*/
|
||||
public interface PublicKeyCredentialUserEntity extends Serializable {
|
||||
|
||||
/**
|
||||
* The <a href=
|
||||
* "https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialentity-name">name</a>
|
||||
* property is a human-palatable identifier for a user account.
|
||||
* @return the name
|
||||
*/
|
||||
String getName();
|
||||
|
||||
/**
|
||||
* The <a href=
|
||||
* "https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialuserentity-id">id</a> is
|
||||
* the user handle of the user account. A user handle is an opaque byte sequence with
|
||||
* a maximum size of 64 bytes, and is not meant to be displayed to the user.
|
||||
* @return the user handle of the user account
|
||||
*/
|
||||
Bytes getId();
|
||||
|
||||
/**
|
||||
* The <a href=
|
||||
* "https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialuserentity-displayname">displayName</a>
|
||||
* is a human-palatable name for the user account, intended only for display.
|
||||
* @return the display name
|
||||
*/
|
||||
String getDisplayName();
|
||||
|
||||
}
|
||||
-80
@@ -1,80 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2024 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.
|
||||
*/
|
||||
|
||||
package org.springframework.security.web.webauthn.api;
|
||||
|
||||
/**
|
||||
* The <a href=
|
||||
* "https://www.w3.org/TR/webauthn-3/#enumdef-residentkeyrequirement">ResidentKeyRequirement</a>
|
||||
* describes the Relying Partys requirements for client-side discoverable credentials.
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 6.4
|
||||
*/
|
||||
public final class ResidentKeyRequirement {
|
||||
|
||||
/**
|
||||
* The <a href=
|
||||
* "https://www.w3.org/TR/webauthn-3/#dom-residentkeyrequirement-discouraged">discouraged</a>
|
||||
* requirement indicates that the Relying Party prefers creating a server-side
|
||||
* credential, but will accept a client-side discoverable credential.
|
||||
*/
|
||||
public static final ResidentKeyRequirement DISCOURAGED = new ResidentKeyRequirement("discouraged");
|
||||
|
||||
/**
|
||||
* The <a href=
|
||||
* "https://www.w3.org/TR/webauthn-3/#dom-residentkeyrequirement-preferred">preferred</a>
|
||||
* requirement indicates that the Relying Party strongly prefers creating a
|
||||
* client-side discoverable credential, but will accept a server-side credential.
|
||||
*/
|
||||
public static final ResidentKeyRequirement PREFERRED = new ResidentKeyRequirement("preferred");
|
||||
|
||||
/**
|
||||
* The <a href=
|
||||
* "https://www.w3.org/TR/webauthn-3/#dom-residentkeyrequirement-required">required</a>
|
||||
* value indicates that the Relying Party requires a client-side discoverable
|
||||
* credential.
|
||||
*/
|
||||
public static final ResidentKeyRequirement REQUIRED = new ResidentKeyRequirement("required");
|
||||
|
||||
private final String value;
|
||||
|
||||
private ResidentKeyRequirement(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value.
|
||||
* @return the value
|
||||
*/
|
||||
public String getValue() {
|
||||
return this.value;
|
||||
}
|
||||
|
||||
public static ResidentKeyRequirement valueOf(String value) {
|
||||
if (DISCOURAGED.getValue().equals(value)) {
|
||||
return DISCOURAGED;
|
||||
}
|
||||
if (PREFERRED.getValue().equals(value)) {
|
||||
return PREFERRED;
|
||||
}
|
||||
if (REQUIRED.getValue().equals(value)) {
|
||||
return REQUIRED;
|
||||
}
|
||||
return new ResidentKeyRequirement(value);
|
||||
}
|
||||
|
||||
}
|
||||
-75
@@ -1,75 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2025 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.
|
||||
*/
|
||||
|
||||
package org.springframework.security.web.webauthn.api;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* <a href=
|
||||
* "https://www.w3.org/TR/webauthn-3/#enumdef-userverificationrequirement">UserVerificationRequirement</a>
|
||||
* is used by the Relying Party to indicate if user verification is needed.
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 6.4
|
||||
*/
|
||||
public final class UserVerificationRequirement implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = -2801001231345540040L;
|
||||
|
||||
/**
|
||||
* The <a href=
|
||||
* "https://www.w3.org/TR/webauthn-3/#dom-userverificationrequirement-discouraged">discouraged</a>
|
||||
* value indicates that the Relying Party does not want user verification employed
|
||||
* during the operation (e.g., in the interest of minimizing disruption to the user
|
||||
* interaction flow).
|
||||
*/
|
||||
public static final UserVerificationRequirement DISCOURAGED = new UserVerificationRequirement("discouraged");
|
||||
|
||||
/**
|
||||
* The <a href=
|
||||
* "https://www.w3.org/TR/webauthn-3/#dom-userverificationrequirement-preferred">preferred</a>
|
||||
* value indicates that the Relying Party prefers user verification for the operation
|
||||
* if possible, but will not fail the operation if the response does not have the UV
|
||||
* flag set.
|
||||
*/
|
||||
public static final UserVerificationRequirement PREFERRED = new UserVerificationRequirement("preferred");
|
||||
|
||||
/**
|
||||
* The <a href=
|
||||
* "https://www.w3.org/TR/webauthn-3/#dom-userverificationrequirement-required">required</a>
|
||||
* value indicates that the Relying Party requires user verification for the operation
|
||||
* and will fail the overall ceremony if the response does not have the UV flag set.
|
||||
*/
|
||||
public static final UserVerificationRequirement REQUIRED = new UserVerificationRequirement("required");
|
||||
|
||||
private final String value;
|
||||
|
||||
UserVerificationRequirement(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value
|
||||
* @return the value
|
||||
*/
|
||||
public String getValue() {
|
||||
return this.value;
|
||||
}
|
||||
|
||||
}
|
||||
-63
@@ -1,63 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2024 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.
|
||||
*/
|
||||
|
||||
package org.springframework.security.web.webauthn.authentication;
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import jakarta.servlet.http.HttpSession;
|
||||
|
||||
import org.springframework.security.web.webauthn.api.PublicKeyCredentialRequestOptions;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* A {@link PublicKeyCredentialRequestOptionsRepository} that stores the
|
||||
* {@link PublicKeyCredentialRequestOptions} in the
|
||||
* {@link jakarta.servlet.http.HttpSession}.
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 6.4
|
||||
*/
|
||||
public class HttpSessionPublicKeyCredentialRequestOptionsRepository
|
||||
implements PublicKeyCredentialRequestOptionsRepository {
|
||||
|
||||
static final String DEFAULT_ATTR_NAME = PublicKeyCredentialRequestOptionsRepository.class.getName()
|
||||
.concat(".ATTR_NAME");
|
||||
|
||||
private String attrName = DEFAULT_ATTR_NAME;
|
||||
|
||||
@Override
|
||||
public void save(HttpServletRequest request, HttpServletResponse response,
|
||||
PublicKeyCredentialRequestOptions options) {
|
||||
HttpSession session = request.getSession();
|
||||
session.setAttribute(this.attrName, options);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PublicKeyCredentialRequestOptions load(HttpServletRequest request) {
|
||||
HttpSession session = request.getSession(false);
|
||||
if (session == null) {
|
||||
return null;
|
||||
}
|
||||
return (PublicKeyCredentialRequestOptions) session.getAttribute(this.attrName);
|
||||
}
|
||||
|
||||
public void setAttrName(String attrName) {
|
||||
Assert.notNull(attrName, "attrName cannot be null");
|
||||
this.attrName = attrName;
|
||||
}
|
||||
|
||||
}
|
||||
-138
@@ -1,138 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2025 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.
|
||||
*/
|
||||
|
||||
package org.springframework.security.web.webauthn.authentication;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import jakarta.servlet.FilterChain;
|
||||
import jakarta.servlet.ServletException;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.converter.HttpMessageConverter;
|
||||
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
|
||||
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
|
||||
import org.springframework.http.server.ServletServerHttpResponse;
|
||||
import org.springframework.security.core.context.SecurityContext;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.security.core.context.SecurityContextHolderStrategy;
|
||||
import org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher;
|
||||
import org.springframework.security.web.util.matcher.RequestMatcher;
|
||||
import org.springframework.security.web.webauthn.api.PublicKeyCredentialRequestOptions;
|
||||
import org.springframework.security.web.webauthn.jackson.WebauthnJackson2Module;
|
||||
import org.springframework.security.web.webauthn.management.ImmutablePublicKeyCredentialRequestOptionsRequest;
|
||||
import org.springframework.security.web.webauthn.management.WebAuthnRelyingPartyOperations;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.web.filter.OncePerRequestFilter;
|
||||
|
||||
/**
|
||||
* A {@link jakarta.servlet.Filter} that renders the
|
||||
* {@link PublicKeyCredentialRequestOptions} in order to <a href=
|
||||
* "https://w3c.github.io/webappsec-credential-management/#dom-credentialscontainer-get">get</a>
|
||||
* a credential.
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 6.4
|
||||
*/
|
||||
public class PublicKeyCredentialRequestOptionsFilter extends OncePerRequestFilter {
|
||||
|
||||
private RequestMatcher matcher = PathPatternRequestMatcher.withDefaults()
|
||||
.matcher(HttpMethod.POST, "/webauthn/authenticate/options");
|
||||
|
||||
private SecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder
|
||||
.getContextHolderStrategy();
|
||||
|
||||
private final WebAuthnRelyingPartyOperations rpOptions;
|
||||
|
||||
private PublicKeyCredentialRequestOptionsRepository requestOptionsRepository = new HttpSessionPublicKeyCredentialRequestOptionsRepository();
|
||||
|
||||
private HttpMessageConverter<Object> converter = new MappingJackson2HttpMessageConverter(
|
||||
Jackson2ObjectMapperBuilder.json().modules(new WebauthnJackson2Module()).build());
|
||||
|
||||
/**
|
||||
* Creates a new instance with the provided {@link WebAuthnRelyingPartyOperations}.
|
||||
* @param rpOptions the {@link WebAuthnRelyingPartyOperations} to use. Cannot be null.
|
||||
*/
|
||||
public PublicKeyCredentialRequestOptionsFilter(WebAuthnRelyingPartyOperations rpOptions) {
|
||||
Assert.notNull(rpOptions, "rpOperations cannot be null");
|
||||
this.rpOptions = rpOptions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link RequestMatcher} used to trigger this filter. By default, the
|
||||
* {@link RequestMatcher} is {@code POST /webauthn/authenticate/options}.
|
||||
* @param requestMatcher the {@link RequestMatcher} to use
|
||||
* @since 6.5
|
||||
*/
|
||||
public void setRequestMatcher(RequestMatcher requestMatcher) {
|
||||
Assert.notNull(requestMatcher, "requestMatcher cannot be null");
|
||||
this.matcher = requestMatcher;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
|
||||
throws ServletException, IOException {
|
||||
if (!this.matcher.matches(request)) {
|
||||
filterChain.doFilter(request, response);
|
||||
return;
|
||||
}
|
||||
|
||||
SecurityContext context = this.securityContextHolderStrategy.getContext();
|
||||
ImmutablePublicKeyCredentialRequestOptionsRequest optionsRequest = new ImmutablePublicKeyCredentialRequestOptionsRequest(
|
||||
context.getAuthentication());
|
||||
PublicKeyCredentialRequestOptions credentialRequestOptions = this.rpOptions
|
||||
.createCredentialRequestOptions(optionsRequest);
|
||||
this.requestOptionsRepository.save(request, response, credentialRequestOptions);
|
||||
response.setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE);
|
||||
this.converter.write(credentialRequestOptions, MediaType.APPLICATION_JSON,
|
||||
new ServletServerHttpResponse(response));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link PublicKeyCredentialRequestOptionsRepository} to use.
|
||||
* @param requestOptionsRepository the
|
||||
* {@link PublicKeyCredentialRequestOptionsRepository} to use. Cannot be null.
|
||||
*/
|
||||
public void setRequestOptionsRepository(PublicKeyCredentialRequestOptionsRepository requestOptionsRepository) {
|
||||
Assert.notNull(requestOptionsRepository, "requestOptionsRepository cannot be null");
|
||||
this.requestOptionsRepository = requestOptionsRepository;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link HttpMessageConverter} to use.
|
||||
* @param converter the {@link HttpMessageConverter} to use. Cannot be null.
|
||||
*/
|
||||
public void setConverter(HttpMessageConverter<Object> converter) {
|
||||
Assert.notNull(converter, "converter cannot be null");
|
||||
this.converter = converter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link SecurityContextHolderStrategy} to use.
|
||||
* @param securityContextHolderStrategy the {@link SecurityContextHolderStrategy} to
|
||||
* use. Cannot be null.
|
||||
*/
|
||||
public void setSecurityContextHolderStrategy(SecurityContextHolderStrategy securityContextHolderStrategy) {
|
||||
Assert.notNull(securityContextHolderStrategy, "securityContextHolderStrategy cannot be null");
|
||||
this.securityContextHolderStrategy = securityContextHolderStrategy;
|
||||
}
|
||||
|
||||
}
|
||||
-52
@@ -1,52 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2024 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.
|
||||
*/
|
||||
|
||||
package org.springframework.security.web.webauthn.authentication;
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.security.web.webauthn.api.PublicKeyCredentialRequestOptions;
|
||||
|
||||
/**
|
||||
* Saves {@link PublicKeyCredentialRequestOptions} between a request to generate an
|
||||
* assertion and the validation of the assertion.
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 6.4
|
||||
*/
|
||||
public interface PublicKeyCredentialRequestOptionsRepository {
|
||||
|
||||
/**
|
||||
* Saves the provided {@link PublicKeyCredentialRequestOptions} or clears an existing
|
||||
* {@link PublicKeyCredentialRequestOptions} if {@code options} is null.
|
||||
* @param request the {@link HttpServletRequest}
|
||||
* @param response the {@link HttpServletResponse}
|
||||
* @param options the {@link PublicKeyCredentialRequestOptions} to save or null if an
|
||||
* existing {@link PublicKeyCredentialRequestOptions} should be removed.
|
||||
*/
|
||||
void save(HttpServletRequest request, HttpServletResponse response, PublicKeyCredentialRequestOptions options);
|
||||
|
||||
/**
|
||||
* Gets a saved {@link PublicKeyCredentialRequestOptions} if it exists, otherwise
|
||||
* null.
|
||||
* @param request the {@link HttpServletRequest}
|
||||
* @return the {@link PublicKeyCredentialRequestOptions} that was saved, otherwise
|
||||
* null.
|
||||
*/
|
||||
PublicKeyCredentialRequestOptions load(HttpServletRequest request);
|
||||
|
||||
}
|
||||
-70
@@ -1,70 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2025 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.
|
||||
*/
|
||||
|
||||
package org.springframework.security.web.webauthn.authentication;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.util.Collection;
|
||||
|
||||
import org.springframework.security.authentication.AbstractAuthenticationToken;
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.security.web.webauthn.api.PublicKeyCredentialUserEntity;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* A {@link WebAuthnAuthentication} is used to represent successful authentication with
|
||||
* WebAuthn.
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 6.4
|
||||
* @see WebAuthnAuthenticationRequestToken
|
||||
*/
|
||||
public class WebAuthnAuthentication extends AbstractAuthenticationToken {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = -4879907158750659197L;
|
||||
|
||||
private final PublicKeyCredentialUserEntity principal;
|
||||
|
||||
public WebAuthnAuthentication(PublicKeyCredentialUserEntity principal,
|
||||
Collection<? extends GrantedAuthority> authorities) {
|
||||
super(authorities);
|
||||
this.principal = principal;
|
||||
super.setAuthenticated(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAuthenticated(boolean authenticated) {
|
||||
Assert.isTrue(!authenticated, "Cannot set this token to trusted");
|
||||
super.setAuthenticated(authenticated);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getCredentials() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PublicKeyCredentialUserEntity getPrincipal() {
|
||||
return this.principal;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return this.principal.getName();
|
||||
}
|
||||
|
||||
}
|
||||
-136
@@ -1,136 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2025 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.
|
||||
*/
|
||||
|
||||
package org.springframework.security.web.webauthn.authentication;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import jakarta.servlet.ServletException;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.converter.GenericHttpMessageConverter;
|
||||
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
|
||||
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
|
||||
import org.springframework.http.server.ServletServerHttpRequest;
|
||||
import org.springframework.security.authentication.BadCredentialsException;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.AuthenticationException;
|
||||
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
|
||||
import org.springframework.security.web.authentication.AuthenticationEntryPointFailureHandler;
|
||||
import org.springframework.security.web.authentication.HttpMessageConverterAuthenticationSuccessHandler;
|
||||
import org.springframework.security.web.authentication.HttpStatusEntryPoint;
|
||||
import org.springframework.security.web.context.HttpSessionSecurityContextRepository;
|
||||
import org.springframework.security.web.webauthn.api.AuthenticatorAssertionResponse;
|
||||
import org.springframework.security.web.webauthn.api.PublicKeyCredential;
|
||||
import org.springframework.security.web.webauthn.api.PublicKeyCredentialRequestOptions;
|
||||
import org.springframework.security.web.webauthn.jackson.WebauthnJackson2Module;
|
||||
import org.springframework.security.web.webauthn.management.RelyingPartyAuthenticationRequest;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import static org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher.pathPattern;
|
||||
|
||||
/**
|
||||
* Authenticates {@code PublicKeyCredential<AuthenticatorAssertionResponse>} that is
|
||||
* parsed from the body of the {@link HttpServletRequest} using the
|
||||
* {@link #setConverter(GenericHttpMessageConverter)}. An example request is provided
|
||||
* below:
|
||||
*
|
||||
* <pre>
|
||||
* {
|
||||
* "id": "dYF7EGnRFFIXkpXi9XU2wg",
|
||||
* "rawId": "dYF7EGnRFFIXkpXi9XU2wg",
|
||||
* "response": {
|
||||
* "authenticatorData": "y9GqwTRaMpzVDbXq1dyEAXVOxrou08k22ggRC45MKNgdAAAAAA",
|
||||
* "clientDataJSON": "eyJ0eXBlIjoid2ViYXV0aG4uZ2V0IiwiY2hhbGxlbmdlIjoiRFVsRzRDbU9naWhKMG1vdXZFcE9HdUk0ZVJ6MGRRWmxUQmFtbjdHQ1FTNCIsIm9yaWdpbiI6Imh0dHBzOi8vZXhhbXBsZS5sb2NhbGhvc3Q6ODQ0MyIsImNyb3NzT3JpZ2luIjpmYWxzZX0",
|
||||
* "signature": "MEYCIQCW2BcUkRCAXDmGxwMi78jknenZ7_amWrUJEYoTkweldAIhAMD0EMp1rw2GfwhdrsFIeDsL7tfOXVPwOtfqJntjAo4z",
|
||||
* "userHandle": "Q3_0Xd64_HW0BlKRAJnVagJTpLKLgARCj8zjugpRnVo"
|
||||
* },
|
||||
* "clientExtensionResults": {},
|
||||
* "authenticatorAttachment": "platform"
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 6.4
|
||||
*/
|
||||
public class WebAuthnAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
|
||||
|
||||
private GenericHttpMessageConverter<Object> converter = new MappingJackson2HttpMessageConverter(
|
||||
Jackson2ObjectMapperBuilder.json().modules(new WebauthnJackson2Module()).build());
|
||||
|
||||
private PublicKeyCredentialRequestOptionsRepository requestOptionsRepository = new HttpSessionPublicKeyCredentialRequestOptionsRepository();
|
||||
|
||||
public WebAuthnAuthenticationFilter() {
|
||||
super(pathPattern(HttpMethod.POST, "/login/webauthn"));
|
||||
setSecurityContextRepository(new HttpSessionSecurityContextRepository());
|
||||
setAuthenticationFailureHandler(
|
||||
new AuthenticationEntryPointFailureHandler(new HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED)));
|
||||
setAuthenticationSuccessHandler(new HttpMessageConverterAuthenticationSuccessHandler());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
|
||||
throws AuthenticationException, IOException, ServletException {
|
||||
ServletServerHttpRequest httpRequest = new ServletServerHttpRequest(request);
|
||||
ResolvableType resolvableType = ResolvableType.forClassWithGenerics(PublicKeyCredential.class,
|
||||
AuthenticatorAssertionResponse.class);
|
||||
PublicKeyCredential<AuthenticatorAssertionResponse> publicKeyCredential = null;
|
||||
try {
|
||||
publicKeyCredential = (PublicKeyCredential<AuthenticatorAssertionResponse>) this.converter
|
||||
.read(resolvableType.getType(), getClass(), httpRequest);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new BadCredentialsException("Unable to authenticate the PublicKeyCredential", ex);
|
||||
}
|
||||
PublicKeyCredentialRequestOptions requestOptions = this.requestOptionsRepository.load(request);
|
||||
if (requestOptions == null) {
|
||||
throw new BadCredentialsException(
|
||||
"Unable to authenticate the PublicKeyCredential. No PublicKeyCredentialRequestOptions found.");
|
||||
}
|
||||
this.requestOptionsRepository.save(request, response, null);
|
||||
RelyingPartyAuthenticationRequest authenticationRequest = new RelyingPartyAuthenticationRequest(requestOptions,
|
||||
publicKeyCredential);
|
||||
WebAuthnAuthenticationRequestToken token = new WebAuthnAuthenticationRequestToken(authenticationRequest);
|
||||
return getAuthenticationManager().authenticate(token);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link GenericHttpMessageConverter} to use for writing
|
||||
* {@code PublicKeyCredential<AuthenticatorAssertionResponse>} to the response. The
|
||||
* default is @{code MappingJackson2HttpMessageConverter}
|
||||
* @param converter the {@link GenericHttpMessageConverter} to use. Cannot be null.
|
||||
*/
|
||||
public void setConverter(GenericHttpMessageConverter<Object> converter) {
|
||||
Assert.notNull(converter, "converter cannot be null");
|
||||
this.converter = converter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link PublicKeyCredentialRequestOptionsRepository} to use. The default is
|
||||
* {@link HttpSessionPublicKeyCredentialRequestOptionsRepository}.
|
||||
* @param requestOptionsRepository the
|
||||
* {@link PublicKeyCredentialRequestOptionsRepository} to use. Cannot be null.
|
||||
*/
|
||||
public void setRequestOptionsRepository(PublicKeyCredentialRequestOptionsRepository requestOptionsRepository) {
|
||||
Assert.notNull(requestOptionsRepository, "requestOptionsRepository cannot be null");
|
||||
this.requestOptionsRepository = requestOptionsRepository;
|
||||
}
|
||||
|
||||
}
|
||||
-80
@@ -1,80 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2024 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.
|
||||
*/
|
||||
|
||||
package org.springframework.security.web.webauthn.authentication;
|
||||
|
||||
import org.springframework.security.authentication.AuthenticationProvider;
|
||||
import org.springframework.security.authentication.BadCredentialsException;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.AuthenticationException;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||
import org.springframework.security.web.webauthn.api.PublicKeyCredentialUserEntity;
|
||||
import org.springframework.security.web.webauthn.management.RelyingPartyAuthenticationRequest;
|
||||
import org.springframework.security.web.webauthn.management.WebAuthnRelyingPartyOperations;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* An {@link AuthenticationProvider} that uses {@link WebAuthnRelyingPartyOperations} for
|
||||
* authentication using an {@link WebAuthnAuthenticationRequestToken}. First
|
||||
* {@link WebAuthnRelyingPartyOperations#authenticate(RelyingPartyAuthenticationRequest)}
|
||||
* is invoked. The result is a username passed into {@link UserDetailsService}. The
|
||||
* {@link UserDetails} is used to create an {@link Authentication}.
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 6.4
|
||||
*/
|
||||
public class WebAuthnAuthenticationProvider implements AuthenticationProvider {
|
||||
|
||||
private final WebAuthnRelyingPartyOperations relyingPartyOperations;
|
||||
|
||||
private final UserDetailsService userDetailsService;
|
||||
|
||||
/**
|
||||
* Creates a new instance.
|
||||
* @param relyingPartyOperations the {@link WebAuthnRelyingPartyOperations} to use.
|
||||
* Cannot be null.
|
||||
* @param userDetailsService the {@link UserDetailsService} to use. Cannot be null.
|
||||
*/
|
||||
public WebAuthnAuthenticationProvider(WebAuthnRelyingPartyOperations relyingPartyOperations,
|
||||
UserDetailsService userDetailsService) {
|
||||
Assert.notNull(relyingPartyOperations, "relyingPartyOperations cannot be null");
|
||||
Assert.notNull(userDetailsService, "userDetailsService cannot be null");
|
||||
this.relyingPartyOperations = relyingPartyOperations;
|
||||
this.userDetailsService = userDetailsService;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
|
||||
WebAuthnAuthenticationRequestToken webAuthnRequest = (WebAuthnAuthenticationRequestToken) authentication;
|
||||
try {
|
||||
PublicKeyCredentialUserEntity userEntity = this.relyingPartyOperations
|
||||
.authenticate(webAuthnRequest.getWebAuthnRequest());
|
||||
String username = userEntity.getName();
|
||||
UserDetails userDetails = this.userDetailsService.loadUserByUsername(username);
|
||||
return new WebAuthnAuthentication(userEntity, userDetails.getAuthorities());
|
||||
}
|
||||
catch (RuntimeException ex) {
|
||||
throw new BadCredentialsException(ex.getMessage(), ex);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supports(Class<?> authentication) {
|
||||
return WebAuthnAuthenticationRequestToken.class.isAssignableFrom(authentication);
|
||||
}
|
||||
|
||||
}
|
||||
-75
@@ -1,75 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2025 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.
|
||||
*/
|
||||
|
||||
package org.springframework.security.web.webauthn.authentication;
|
||||
|
||||
import java.io.Serial;
|
||||
|
||||
import org.springframework.security.authentication.AbstractAuthenticationToken;
|
||||
import org.springframework.security.core.authority.AuthorityUtils;
|
||||
import org.springframework.security.web.webauthn.management.RelyingPartyAuthenticationRequest;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* An {@link org.springframework.security.core.Authentication} used in
|
||||
* {@link WebAuthnAuthenticationProvider} for authenticating via WebAuthn.
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 6.4
|
||||
*/
|
||||
public class WebAuthnAuthenticationRequestToken extends AbstractAuthenticationToken {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = -1682693433877522403L;
|
||||
|
||||
private final RelyingPartyAuthenticationRequest webAuthnRequest;
|
||||
|
||||
/**
|
||||
* Creates a new instance.
|
||||
* @param webAuthnRequest the {@link RelyingPartyAuthenticationRequest} to use for
|
||||
* authentication. Cannot be null.
|
||||
*/
|
||||
public WebAuthnAuthenticationRequestToken(RelyingPartyAuthenticationRequest webAuthnRequest) {
|
||||
super(AuthorityUtils.NO_AUTHORITIES);
|
||||
Assert.notNull(webAuthnRequest, "webAuthnRequest cannot be null");
|
||||
this.webAuthnRequest = webAuthnRequest;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the {@link RelyingPartyAuthenticationRequest}
|
||||
* @return the {@link RelyingPartyAuthenticationRequest}
|
||||
*/
|
||||
public RelyingPartyAuthenticationRequest getWebAuthnRequest() {
|
||||
return this.webAuthnRequest;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAuthenticated(boolean authenticated) {
|
||||
Assert.isTrue(!authenticated, "Cannot set this token to trusted");
|
||||
super.setAuthenticated(authenticated);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getCredentials() {
|
||||
return this.webAuthnRequest.getPublicKey();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getPrincipal() {
|
||||
return this.webAuthnRequest.getPublicKey().getResponse().getUserHandle();
|
||||
}
|
||||
|
||||
}
|
||||
-32
@@ -1,32 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2024 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.
|
||||
*/
|
||||
|
||||
package org.springframework.security.web.webauthn.jackson;
|
||||
|
||||
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
||||
|
||||
import org.springframework.security.web.webauthn.api.AttestationConveyancePreference;
|
||||
|
||||
/**
|
||||
* Jackson mixin for {@link AttestationConveyancePreference}
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 6.4
|
||||
*/
|
||||
@JsonSerialize(using = AttestationConveyancePreferenceSerializer.class)
|
||||
class AttestationConveyancePreferenceMixin {
|
||||
|
||||
}
|
||||
-46
@@ -1,46 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2024 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.
|
||||
*/
|
||||
|
||||
package org.springframework.security.web.webauthn.jackson;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
import com.fasterxml.jackson.databind.SerializerProvider;
|
||||
import com.fasterxml.jackson.databind.ser.std.StdSerializer;
|
||||
|
||||
import org.springframework.security.web.webauthn.api.AttestationConveyancePreference;
|
||||
|
||||
/**
|
||||
* Jackson serializer for {@link AttestationConveyancePreference}
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 6.4
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
class AttestationConveyancePreferenceSerializer extends StdSerializer<AttestationConveyancePreference> {
|
||||
|
||||
AttestationConveyancePreferenceSerializer() {
|
||||
super(AttestationConveyancePreference.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void serialize(AttestationConveyancePreference preference, JsonGenerator jgen, SerializerProvider provider)
|
||||
throws IOException {
|
||||
jgen.writeString(preference.getValue());
|
||||
}
|
||||
|
||||
}
|
||||
-32
@@ -1,32 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2024 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.
|
||||
*/
|
||||
|
||||
package org.springframework.security.web.webauthn.jackson;
|
||||
|
||||
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
||||
|
||||
import org.springframework.security.web.webauthn.api.AuthenticationExtensionsClientInputs;
|
||||
|
||||
/**
|
||||
* Jackson mixin for {@link AuthenticationExtensionsClientInputs}
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 6.4
|
||||
*/
|
||||
@JsonSerialize(using = AuthenticationExtensionsClientInputSerializer.class)
|
||||
class AuthenticationExtensionsClientInputMixin {
|
||||
|
||||
}
|
||||
-49
@@ -1,49 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2024 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.
|
||||
*/
|
||||
|
||||
package org.springframework.security.web.webauthn.jackson;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
import com.fasterxml.jackson.databind.SerializerProvider;
|
||||
import com.fasterxml.jackson.databind.ser.std.StdSerializer;
|
||||
|
||||
import org.springframework.security.web.webauthn.api.AuthenticationExtensionsClientInput;
|
||||
|
||||
/**
|
||||
* Provides Jackson serialization of {@link AuthenticationExtensionsClientInput}.
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 6.4
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
class AuthenticationExtensionsClientInputSerializer extends StdSerializer<AuthenticationExtensionsClientInput> {
|
||||
|
||||
/**
|
||||
* Creates a new instance.
|
||||
*/
|
||||
AuthenticationExtensionsClientInputSerializer() {
|
||||
super(AuthenticationExtensionsClientInput.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void serialize(AuthenticationExtensionsClientInput input, JsonGenerator jgen, SerializerProvider provider)
|
||||
throws IOException {
|
||||
jgen.writeObjectField(input.getExtensionId(), input.getInput());
|
||||
}
|
||||
|
||||
}
|
||||
-32
@@ -1,32 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2024 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.
|
||||
*/
|
||||
|
||||
package org.springframework.security.web.webauthn.jackson;
|
||||
|
||||
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
||||
|
||||
import org.springframework.security.web.webauthn.api.AuthenticationExtensionsClientInputs;
|
||||
|
||||
/**
|
||||
* Jackson mixin for {@link AuthenticationExtensionsClientInputs}
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 6.4
|
||||
*/
|
||||
@JsonSerialize(using = AuthenticationExtensionsClientInputsSerializer.class)
|
||||
class AuthenticationExtensionsClientInputsMixin {
|
||||
|
||||
}
|
||||
-54
@@ -1,54 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2024 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.
|
||||
*/
|
||||
|
||||
package org.springframework.security.web.webauthn.jackson;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
import com.fasterxml.jackson.databind.SerializerProvider;
|
||||
import com.fasterxml.jackson.databind.ser.std.StdSerializer;
|
||||
|
||||
import org.springframework.security.web.webauthn.api.AuthenticationExtensionsClientInput;
|
||||
import org.springframework.security.web.webauthn.api.AuthenticationExtensionsClientInputs;
|
||||
|
||||
/**
|
||||
* Provides Jackson serialization of {@link AuthenticationExtensionsClientInputs}.
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 6.4
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
class AuthenticationExtensionsClientInputsSerializer extends StdSerializer<AuthenticationExtensionsClientInputs> {
|
||||
|
||||
/**
|
||||
* Creates a new instance.
|
||||
*/
|
||||
AuthenticationExtensionsClientInputsSerializer() {
|
||||
super(AuthenticationExtensionsClientInputs.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void serialize(AuthenticationExtensionsClientInputs inputs, JsonGenerator jgen, SerializerProvider provider)
|
||||
throws IOException {
|
||||
jgen.writeStartObject();
|
||||
for (AuthenticationExtensionsClientInput input : inputs.getInputs()) {
|
||||
jgen.writeObject(input);
|
||||
}
|
||||
jgen.writeEndObject();
|
||||
}
|
||||
|
||||
}
|
||||
-78
@@ -1,78 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2024 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.
|
||||
*/
|
||||
|
||||
package org.springframework.security.web.webauthn.jackson;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import com.fasterxml.jackson.core.JacksonException;
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
import com.fasterxml.jackson.core.JsonToken;
|
||||
import com.fasterxml.jackson.databind.DeserializationContext;
|
||||
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.security.web.webauthn.api.AuthenticationExtensionsClientOutput;
|
||||
import org.springframework.security.web.webauthn.api.AuthenticationExtensionsClientOutputs;
|
||||
import org.springframework.security.web.webauthn.api.CredentialPropertiesOutput;
|
||||
import org.springframework.security.web.webauthn.api.ImmutableAuthenticationExtensionsClientOutputs;
|
||||
|
||||
/**
|
||||
* Provides Jackson deserialization of {@link AuthenticationExtensionsClientOutputs}.
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 6.4
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
class AuthenticationExtensionsClientOutputsDeserializer extends StdDeserializer<AuthenticationExtensionsClientOutputs> {
|
||||
|
||||
private static final Log logger = LogFactory.getLog(AuthenticationExtensionsClientOutputsDeserializer.class);
|
||||
|
||||
/**
|
||||
* Creates a new instance.
|
||||
*/
|
||||
AuthenticationExtensionsClientOutputsDeserializer() {
|
||||
super(AuthenticationExtensionsClientOutputs.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AuthenticationExtensionsClientOutputs deserialize(JsonParser parser, DeserializationContext ctxt)
|
||||
throws IOException, JacksonException {
|
||||
List<AuthenticationExtensionsClientOutput<?>> outputs = new ArrayList<>();
|
||||
for (String key = parser.nextFieldName(); key != null; key = parser.nextFieldName()) {
|
||||
JsonToken startObject = parser.nextValue();
|
||||
if (startObject != JsonToken.START_OBJECT) {
|
||||
break;
|
||||
}
|
||||
if (CredentialPropertiesOutput.EXTENSION_ID.equals(key)) {
|
||||
CredentialPropertiesOutput output = parser.readValueAs(CredentialPropertiesOutput.class);
|
||||
outputs.add(output);
|
||||
}
|
||||
else {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Skipping unknown extension with id " + key);
|
||||
}
|
||||
parser.nextValue();
|
||||
}
|
||||
}
|
||||
|
||||
return new ImmutableAuthenticationExtensionsClientOutputs(outputs);
|
||||
}
|
||||
|
||||
}
|
||||
-32
@@ -1,32 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2024 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.
|
||||
*/
|
||||
|
||||
package org.springframework.security.web.webauthn.jackson;
|
||||
|
||||
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
|
||||
|
||||
import org.springframework.security.web.webauthn.api.AuthenticationExtensionsClientOutputs;
|
||||
|
||||
/**
|
||||
* Jackson mixin for {@link AuthenticationExtensionsClientOutputs}
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 6.4
|
||||
*/
|
||||
@JsonDeserialize(using = AuthenticationExtensionsClientOutputsDeserializer.class)
|
||||
class AuthenticationExtensionsClientOutputsMixin {
|
||||
|
||||
}
|
||||
-38
@@ -1,38 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2024 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.
|
||||
*/
|
||||
|
||||
package org.springframework.security.web.webauthn.jackson;
|
||||
|
||||
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
|
||||
import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder;
|
||||
|
||||
import org.springframework.security.web.webauthn.api.AuthenticatorAssertionResponse;
|
||||
|
||||
/**
|
||||
* Jackson mixin for {@link AuthenticatorAssertionResponse}
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 6.4
|
||||
*/
|
||||
@JsonDeserialize(builder = AuthenticatorAssertionResponse.AuthenticatorAssertionResponseBuilder.class)
|
||||
class AuthenticatorAssertionResponseMixin {
|
||||
|
||||
@JsonPOJOBuilder(withPrefix = "")
|
||||
abstract class AuthenticatorAssertionResponseBuilderMixin {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
-53
@@ -1,53 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2024 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.
|
||||
*/
|
||||
|
||||
package org.springframework.security.web.webauthn.jackson;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import com.fasterxml.jackson.core.JacksonException;
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
import com.fasterxml.jackson.databind.DeserializationContext;
|
||||
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
|
||||
|
||||
import org.springframework.security.web.webauthn.api.AuthenticatorAttachment;
|
||||
|
||||
/**
|
||||
* Jackson deserializer for {@link AuthenticatorAttachment}
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 6.4
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
class AuthenticatorAttachmentDeserializer extends StdDeserializer<AuthenticatorAttachment> {
|
||||
|
||||
AuthenticatorAttachmentDeserializer() {
|
||||
super(AuthenticatorAttachment.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AuthenticatorAttachment deserialize(JsonParser parser, DeserializationContext ctxt)
|
||||
throws IOException, JacksonException {
|
||||
String type = parser.readValueAs(String.class);
|
||||
for (AuthenticatorAttachment publicKeyCredentialType : AuthenticatorAttachment.values()) {
|
||||
if (publicKeyCredentialType.getValue().equals(type)) {
|
||||
return publicKeyCredentialType;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
-34
@@ -1,34 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2024 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.
|
||||
*/
|
||||
|
||||
package org.springframework.security.web.webauthn.jackson;
|
||||
|
||||
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
|
||||
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
||||
|
||||
import org.springframework.security.web.webauthn.api.AuthenticatorAttachment;
|
||||
|
||||
/**
|
||||
* Jackson mixin for {@link AuthenticatorAttachment}
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 6.4
|
||||
*/
|
||||
@JsonDeserialize(using = AuthenticatorAttachmentDeserializer.class)
|
||||
@JsonSerialize(using = AuthenticatorAttachmentSerializer.class)
|
||||
class AuthenticatorAttachmentMixin {
|
||||
|
||||
}
|
||||
-46
@@ -1,46 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2024 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.
|
||||
*/
|
||||
|
||||
package org.springframework.security.web.webauthn.jackson;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
import com.fasterxml.jackson.databind.SerializerProvider;
|
||||
import com.fasterxml.jackson.databind.ser.std.StdSerializer;
|
||||
|
||||
import org.springframework.security.web.webauthn.api.AuthenticatorAttachment;
|
||||
|
||||
/**
|
||||
* Jackson serializer for {@link AuthenticatorAttachment}
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 6.4
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
class AuthenticatorAttachmentSerializer extends StdSerializer<AuthenticatorAttachment> {
|
||||
|
||||
AuthenticatorAttachmentSerializer() {
|
||||
super(AuthenticatorAttachment.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void serialize(AuthenticatorAttachment attachment, JsonGenerator jgen, SerializerProvider provider)
|
||||
throws IOException {
|
||||
jgen.writeString(attachment.getValue());
|
||||
}
|
||||
|
||||
}
|
||||
-48
@@ -1,48 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2024 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.
|
||||
*/
|
||||
|
||||
package org.springframework.security.web.webauthn.jackson;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.fasterxml.jackson.annotation.JsonSetter;
|
||||
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
|
||||
import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder;
|
||||
|
||||
import org.springframework.security.web.webauthn.api.AuthenticatorAttestationResponse;
|
||||
import org.springframework.security.web.webauthn.api.AuthenticatorTransport;
|
||||
|
||||
/**
|
||||
* Jackson mixin for {@link AuthenticatorAttestationResponse}
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 6.4
|
||||
*/
|
||||
@JsonDeserialize(builder = AuthenticatorAttestationResponse.AuthenticatorAttestationResponseBuilder.class)
|
||||
class AuthenticatorAttestationResponseMixin {
|
||||
|
||||
@JsonPOJOBuilder(withPrefix = "")
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
abstract class AuthenticatorAttestationResponseBuilderMixin {
|
||||
|
||||
@JsonSetter
|
||||
abstract AuthenticatorAttestationResponse.AuthenticatorAttestationResponseBuilder transports(
|
||||
List<AuthenticatorTransport> transports);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
-32
@@ -1,32 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2024 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.
|
||||
*/
|
||||
|
||||
package org.springframework.security.web.webauthn.jackson;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
|
||||
import org.springframework.security.web.webauthn.api.AuthenticatorSelectionCriteria;
|
||||
|
||||
/**
|
||||
* Jackson mixin for {@link AuthenticatorSelectionCriteria}
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 6.4
|
||||
*/
|
||||
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||
abstract class AuthenticatorSelectionCriteriaMixin {
|
||||
|
||||
}
|
||||
-53
@@ -1,53 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2024 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.
|
||||
*/
|
||||
|
||||
package org.springframework.security.web.webauthn.jackson;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import com.fasterxml.jackson.core.JacksonException;
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
import com.fasterxml.jackson.databind.DeserializationContext;
|
||||
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
|
||||
|
||||
import org.springframework.security.web.webauthn.api.AuthenticatorTransport;
|
||||
|
||||
/**
|
||||
* Jackson deserializer for {@link AuthenticatorTransport}
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 6.4
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
class AuthenticatorTransportDeserializer extends StdDeserializer<AuthenticatorTransport> {
|
||||
|
||||
AuthenticatorTransportDeserializer() {
|
||||
super(AuthenticatorTransport.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AuthenticatorTransport deserialize(JsonParser parser, DeserializationContext ctxt)
|
||||
throws IOException, JacksonException {
|
||||
String transportValue = parser.readValueAs(String.class);
|
||||
for (AuthenticatorTransport transport : AuthenticatorTransport.values()) {
|
||||
if (transport.getValue().equals(transportValue)) {
|
||||
return transport;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
-34
@@ -1,34 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2024 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.
|
||||
*/
|
||||
|
||||
package org.springframework.security.web.webauthn.jackson;
|
||||
|
||||
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
|
||||
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
||||
|
||||
import org.springframework.security.web.webauthn.api.AuthenticatorTransport;
|
||||
|
||||
/**
|
||||
* Jackson mixin for {@link AuthenticatorTransport}
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 6.4
|
||||
*/
|
||||
@JsonDeserialize(using = AuthenticatorTransportDeserializer.class)
|
||||
@JsonSerialize(using = AuthenticatorTransportSerializer.class)
|
||||
class AuthenticatorTransportMixin {
|
||||
|
||||
}
|
||||
-41
@@ -1,41 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2024 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.
|
||||
*/
|
||||
|
||||
package org.springframework.security.web.webauthn.jackson;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
import com.fasterxml.jackson.databind.JsonSerializer;
|
||||
import com.fasterxml.jackson.databind.SerializerProvider;
|
||||
|
||||
import org.springframework.security.web.webauthn.api.AuthenticatorTransport;
|
||||
|
||||
/**
|
||||
* Jackson serializer for {@link AuthenticatorTransport}
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 6.4
|
||||
*/
|
||||
class AuthenticatorTransportSerializer extends JsonSerializer<AuthenticatorTransport> {
|
||||
|
||||
@Override
|
||||
public void serialize(AuthenticatorTransport transport, JsonGenerator jgen, SerializerProvider provider)
|
||||
throws IOException {
|
||||
jgen.writeString(transport.getValue());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2024 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.
|
||||
*/
|
||||
|
||||
package org.springframework.security.web.webauthn.jackson;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
||||
|
||||
import org.springframework.security.web.webauthn.api.Bytes;
|
||||
|
||||
/**
|
||||
* Jackson mixin for {@link Bytes}
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 6.4
|
||||
*/
|
||||
@JsonSerialize(using = BytesSerializer.class)
|
||||
final class BytesMixin {
|
||||
|
||||
@JsonCreator
|
||||
static Bytes fromBase64(String value) {
|
||||
return Bytes.fromBase64(value);
|
||||
}
|
||||
|
||||
private BytesMixin() {
|
||||
}
|
||||
|
||||
}
|
||||
-48
@@ -1,48 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2024 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.
|
||||
*/
|
||||
|
||||
package org.springframework.security.web.webauthn.jackson;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
import com.fasterxml.jackson.databind.SerializerProvider;
|
||||
import com.fasterxml.jackson.databind.ser.std.StdSerializer;
|
||||
|
||||
import org.springframework.security.web.webauthn.api.Bytes;
|
||||
|
||||
/**
|
||||
* Jackson serializer for {@link Bytes}
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 6.4
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
class BytesSerializer extends StdSerializer<Bytes> {
|
||||
|
||||
/**
|
||||
* Creates a new instance.
|
||||
*/
|
||||
BytesSerializer() {
|
||||
super(Bytes.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void serialize(Bytes bytes, JsonGenerator jgen, SerializerProvider provider) throws IOException {
|
||||
jgen.writeString(bytes.toBase64UrlString());
|
||||
}
|
||||
|
||||
}
|
||||
-53
@@ -1,53 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2024 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.
|
||||
*/
|
||||
|
||||
package org.springframework.security.web.webauthn.jackson;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import com.fasterxml.jackson.core.JacksonException;
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
import com.fasterxml.jackson.databind.DeserializationContext;
|
||||
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
|
||||
|
||||
import org.springframework.security.web.webauthn.api.COSEAlgorithmIdentifier;
|
||||
|
||||
/**
|
||||
* Jackson serializer for {@link COSEAlgorithmIdentifier}
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 6.4
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
class COSEAlgorithmIdentifierDeserializer extends StdDeserializer<COSEAlgorithmIdentifier> {
|
||||
|
||||
COSEAlgorithmIdentifierDeserializer() {
|
||||
super(COSEAlgorithmIdentifier.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public COSEAlgorithmIdentifier deserialize(JsonParser parser, DeserializationContext ctxt)
|
||||
throws IOException, JacksonException {
|
||||
Long transportValue = parser.readValueAs(Long.class);
|
||||
for (COSEAlgorithmIdentifier identifier : COSEAlgorithmIdentifier.values()) {
|
||||
if (identifier.getValue() == transportValue.longValue()) {
|
||||
return identifier;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
-34
@@ -1,34 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2024 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.
|
||||
*/
|
||||
|
||||
package org.springframework.security.web.webauthn.jackson;
|
||||
|
||||
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
|
||||
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
||||
|
||||
import org.springframework.security.web.webauthn.api.COSEAlgorithmIdentifier;
|
||||
|
||||
/**
|
||||
* Jackson mixin for {@link COSEAlgorithmIdentifier}
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 6.4
|
||||
*/
|
||||
@JsonSerialize(using = COSEAlgorithmIdentifierSerializer.class)
|
||||
@JsonDeserialize(using = COSEAlgorithmIdentifierDeserializer.class)
|
||||
abstract class COSEAlgorithmIdentifierMixin {
|
||||
|
||||
}
|
||||
-46
@@ -1,46 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2024 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.
|
||||
*/
|
||||
|
||||
package org.springframework.security.web.webauthn.jackson;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
import com.fasterxml.jackson.databind.SerializerProvider;
|
||||
import com.fasterxml.jackson.databind.ser.std.StdSerializer;
|
||||
|
||||
import org.springframework.security.web.webauthn.api.COSEAlgorithmIdentifier;
|
||||
|
||||
/**
|
||||
* Jackson serializer for {@link COSEAlgorithmIdentifier}
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 6.4
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
class COSEAlgorithmIdentifierSerializer extends StdSerializer<COSEAlgorithmIdentifier> {
|
||||
|
||||
COSEAlgorithmIdentifierSerializer() {
|
||||
super(COSEAlgorithmIdentifier.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void serialize(COSEAlgorithmIdentifier identifier, JsonGenerator jgen, SerializerProvider provider)
|
||||
throws IOException {
|
||||
jgen.writeNumber(identifier.getValue());
|
||||
}
|
||||
|
||||
}
|
||||
-24
@@ -1,24 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2024 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.
|
||||
*/
|
||||
|
||||
package org.springframework.security.web.webauthn.jackson;
|
||||
|
||||
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
||||
|
||||
@JsonSerialize(using = CredProtectAuthenticationExtensionsClientInputSerializer.class)
|
||||
class CredProtectAuthenticationExtensionsClientInputMixin {
|
||||
|
||||
}
|
||||
-64
@@ -1,64 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2024 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.
|
||||
*/
|
||||
|
||||
package org.springframework.security.web.webauthn.jackson;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
import com.fasterxml.jackson.databind.SerializerProvider;
|
||||
import com.fasterxml.jackson.databind.ser.std.StdSerializer;
|
||||
|
||||
import org.springframework.security.web.webauthn.api.CredProtectAuthenticationExtensionsClientInput;
|
||||
|
||||
/**
|
||||
* Serializes <a href=
|
||||
* "https://fidoalliance.org/specs/fido-v2.2-rd-20230321/fido-client-to-authenticator-protocol-v2.2-rd-20230321.html#sctn-credProtect-extension">credProtect
|
||||
* extension</a>.
|
||||
*
|
||||
* @author Rob Winch
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
class CredProtectAuthenticationExtensionsClientInputSerializer
|
||||
extends StdSerializer<CredProtectAuthenticationExtensionsClientInput> {
|
||||
|
||||
protected CredProtectAuthenticationExtensionsClientInputSerializer() {
|
||||
super(CredProtectAuthenticationExtensionsClientInput.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void serialize(CredProtectAuthenticationExtensionsClientInput input, JsonGenerator jgen,
|
||||
SerializerProvider provider) throws IOException {
|
||||
CredProtectAuthenticationExtensionsClientInput.CredProtect credProtect = input.getInput();
|
||||
String policy = toString(credProtect.getCredProtectionPolicy());
|
||||
jgen.writeObjectField("credentialProtectionPolicy", policy);
|
||||
jgen.writeObjectField("enforceCredentialProtectionPolicy", credProtect.isEnforceCredentialProtectionPolicy());
|
||||
}
|
||||
|
||||
private static String toString(CredProtectAuthenticationExtensionsClientInput.CredProtect.ProtectionPolicy policy) {
|
||||
switch (policy) {
|
||||
case USER_VERIFICATION_OPTIONAL:
|
||||
return "userVerificationOptional";
|
||||
case USER_VERIFICATION_OPTIONAL_WITH_CREDENTIAL_ID_LIST:
|
||||
return "userVerificationOptionalWithCredentialIdList";
|
||||
case USER_VERIFICATION_REQUIRED:
|
||||
return "userVerificationRequired";
|
||||
default:
|
||||
throw new IllegalArgumentException("Unsupported ProtectionPolicy " + policy);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
-36
@@ -1,36 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2024 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.
|
||||
*/
|
||||
|
||||
package org.springframework.security.web.webauthn.jackson;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
import org.springframework.security.web.webauthn.api.CredentialPropertiesOutput;
|
||||
|
||||
/**
|
||||
* Jackson mixin for {@link CredentialPropertiesOutput}
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 6.4
|
||||
*/
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
abstract class CredentialPropertiesOutputMixin {
|
||||
|
||||
CredentialPropertiesOutputMixin(@JsonProperty("rk") boolean rk) {
|
||||
}
|
||||
|
||||
}
|
||||
-47
@@ -1,47 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2024 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.
|
||||
*/
|
||||
|
||||
package org.springframework.security.web.webauthn.jackson;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.time.Duration;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
import com.fasterxml.jackson.databind.SerializerProvider;
|
||||
import com.fasterxml.jackson.databind.ser.std.StdSerializer;
|
||||
|
||||
/**
|
||||
* Jackson serializer for {@link Duration}
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 6.4
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
class DurationSerializer extends StdSerializer<Duration> {
|
||||
|
||||
/**
|
||||
* Creates an instance.
|
||||
*/
|
||||
DurationSerializer() {
|
||||
super(Duration.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void serialize(Duration duration, JsonGenerator jgen, SerializerProvider provider) throws IOException {
|
||||
jgen.writeNumber(duration.toMillis());
|
||||
}
|
||||
|
||||
}
|
||||
-38
@@ -1,38 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2024 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.
|
||||
*/
|
||||
|
||||
package org.springframework.security.web.webauthn.jackson;
|
||||
|
||||
import java.time.Duration;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
||||
|
||||
import org.springframework.security.web.webauthn.api.PublicKeyCredentialCreationOptions;
|
||||
|
||||
/**
|
||||
* Jackson mixin for {@link PublicKeyCredentialCreationOptions}
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 6.4
|
||||
*/
|
||||
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||
abstract class PublicKeyCredentialCreationOptionsMixin {
|
||||
|
||||
@JsonSerialize(using = DurationSerializer.class)
|
||||
private Duration timeout;
|
||||
|
||||
}
|
||||
-38
@@ -1,38 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2024 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.
|
||||
*/
|
||||
|
||||
package org.springframework.security.web.webauthn.jackson;
|
||||
|
||||
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
|
||||
import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder;
|
||||
|
||||
import org.springframework.security.web.webauthn.api.PublicKeyCredential;
|
||||
|
||||
/**
|
||||
* Jackson mixin for {@link PublicKeyCredential}
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 6.4
|
||||
*/
|
||||
@JsonDeserialize(builder = PublicKeyCredential.PublicKeyCredentialBuilder.class)
|
||||
class PublicKeyCredentialMixin {
|
||||
|
||||
@JsonPOJOBuilder(withPrefix = "")
|
||||
static class PublicKeyCredentialBuilderMixin {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
-38
@@ -1,38 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2024 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.
|
||||
*/
|
||||
|
||||
package org.springframework.security.web.webauthn.jackson;
|
||||
|
||||
import java.time.Duration;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
||||
|
||||
import org.springframework.security.web.webauthn.api.PublicKeyCredentialRequestOptions;
|
||||
|
||||
/**
|
||||
* Jackson mixin for {@link PublicKeyCredentialRequestOptions}
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 6.4
|
||||
*/
|
||||
@JsonInclude(content = JsonInclude.Include.NON_NULL)
|
||||
class PublicKeyCredentialRequestOptionsMixin {
|
||||
|
||||
@JsonSerialize(using = DurationSerializer.class)
|
||||
private final Duration timeout = null;
|
||||
|
||||
}
|
||||
-51
@@ -1,51 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2024 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.
|
||||
*/
|
||||
|
||||
package org.springframework.security.web.webauthn.jackson;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import com.fasterxml.jackson.core.JacksonException;
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
import com.fasterxml.jackson.databind.DeserializationContext;
|
||||
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
|
||||
|
||||
import org.springframework.security.web.webauthn.api.PublicKeyCredentialType;
|
||||
|
||||
/**
|
||||
* Jackson deserializer for {@link PublicKeyCredentialType}
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 6.4
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
class PublicKeyCredentialTypeDeserializer extends StdDeserializer<PublicKeyCredentialType> {
|
||||
|
||||
/**
|
||||
* Creates a new instance.
|
||||
*/
|
||||
PublicKeyCredentialTypeDeserializer() {
|
||||
super(PublicKeyCredentialType.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PublicKeyCredentialType deserialize(JsonParser parser, DeserializationContext ctxt)
|
||||
throws IOException, JacksonException {
|
||||
String type = parser.readValueAs(String.class);
|
||||
return PublicKeyCredentialType.valueOf(type);
|
||||
}
|
||||
|
||||
}
|
||||
-34
@@ -1,34 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2024 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.
|
||||
*/
|
||||
|
||||
package org.springframework.security.web.webauthn.jackson;
|
||||
|
||||
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
|
||||
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
||||
|
||||
import org.springframework.security.web.webauthn.api.PublicKeyCredentialType;
|
||||
|
||||
/**
|
||||
* Jackson mixin for {@link PublicKeyCredentialType}
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 6.4
|
||||
*/
|
||||
@JsonSerialize(using = PublicKeyCredentialTypeSerializer.class)
|
||||
@JsonDeserialize(using = PublicKeyCredentialTypeDeserializer.class)
|
||||
abstract class PublicKeyCredentialTypeMixin {
|
||||
|
||||
}
|
||||
-49
@@ -1,49 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2024 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.
|
||||
*/
|
||||
|
||||
package org.springframework.security.web.webauthn.jackson;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
import com.fasterxml.jackson.databind.SerializerProvider;
|
||||
import com.fasterxml.jackson.databind.ser.std.StdSerializer;
|
||||
|
||||
import org.springframework.security.web.webauthn.api.PublicKeyCredentialType;
|
||||
|
||||
/**
|
||||
* Jackson serializer for {@link PublicKeyCredentialType}
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 6.4
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
class PublicKeyCredentialTypeSerializer extends StdSerializer<PublicKeyCredentialType> {
|
||||
|
||||
/**
|
||||
* Creates a new instance.
|
||||
*/
|
||||
PublicKeyCredentialTypeSerializer() {
|
||||
super(PublicKeyCredentialType.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void serialize(PublicKeyCredentialType type, JsonGenerator jgen, SerializerProvider provider)
|
||||
throws IOException {
|
||||
jgen.writeString(type.getValue());
|
||||
}
|
||||
|
||||
}
|
||||
-38
@@ -1,38 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2024 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.
|
||||
*/
|
||||
|
||||
package org.springframework.security.web.webauthn.jackson;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
import org.springframework.security.web.webauthn.api.AuthenticatorAttestationResponse;
|
||||
import org.springframework.security.web.webauthn.api.PublicKeyCredential;
|
||||
import org.springframework.security.web.webauthn.management.RelyingPartyPublicKey;
|
||||
|
||||
/**
|
||||
* Jackson mixin for {@link RelyingPartyPublicKey}
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 6.4
|
||||
*/
|
||||
abstract class RelyingPartyPublicKeyMixin {
|
||||
|
||||
RelyingPartyPublicKeyMixin(
|
||||
@JsonProperty("credential") PublicKeyCredential<AuthenticatorAttestationResponse> credential,
|
||||
@JsonProperty("label") String label) {
|
||||
}
|
||||
|
||||
}
|
||||
-32
@@ -1,32 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2024 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.
|
||||
*/
|
||||
|
||||
package org.springframework.security.web.webauthn.jackson;
|
||||
|
||||
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
||||
|
||||
import org.springframework.security.web.webauthn.api.ResidentKeyRequirement;
|
||||
|
||||
/**
|
||||
* Jackson mixin for {@link ResidentKeyRequirement}
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 6.4
|
||||
*/
|
||||
@JsonSerialize(using = ResidentKeyRequirementSerializer.class)
|
||||
abstract class ResidentKeyRequirementMixin {
|
||||
|
||||
}
|
||||
-49
@@ -1,49 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2024 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.
|
||||
*/
|
||||
|
||||
package org.springframework.security.web.webauthn.jackson;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
import com.fasterxml.jackson.databind.SerializerProvider;
|
||||
import com.fasterxml.jackson.databind.ser.std.StdSerializer;
|
||||
|
||||
import org.springframework.security.web.webauthn.api.ResidentKeyRequirement;
|
||||
|
||||
/**
|
||||
* Jackson serializer for {@link ResidentKeyRequirement}
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 6.4
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
class ResidentKeyRequirementSerializer extends StdSerializer<ResidentKeyRequirement> {
|
||||
|
||||
/**
|
||||
* Creates a new instance.
|
||||
*/
|
||||
ResidentKeyRequirementSerializer() {
|
||||
super(ResidentKeyRequirement.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void serialize(ResidentKeyRequirement requirement, JsonGenerator jgen, SerializerProvider provider)
|
||||
throws IOException {
|
||||
jgen.writeString(requirement.getValue());
|
||||
}
|
||||
|
||||
}
|
||||
-32
@@ -1,32 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2024 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.
|
||||
*/
|
||||
|
||||
package org.springframework.security.web.webauthn.jackson;
|
||||
|
||||
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
||||
|
||||
import org.springframework.security.web.webauthn.api.UserVerificationRequirement;
|
||||
|
||||
/**
|
||||
* Jackson mixin for {@link UserVerificationRequirement}
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 6.4
|
||||
*/
|
||||
@JsonSerialize(using = UserVerificationRequirementSerializer.class)
|
||||
abstract class UserVerificationRequirementMixin {
|
||||
|
||||
}
|
||||
-49
@@ -1,49 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2024 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.
|
||||
*/
|
||||
|
||||
package org.springframework.security.web.webauthn.jackson;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
import com.fasterxml.jackson.databind.SerializerProvider;
|
||||
import com.fasterxml.jackson.databind.ser.std.StdSerializer;
|
||||
|
||||
import org.springframework.security.web.webauthn.api.UserVerificationRequirement;
|
||||
|
||||
/**
|
||||
* Jackson serializer for {@link UserVerificationRequirement}
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 6.4
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
class UserVerificationRequirementSerializer extends StdSerializer<UserVerificationRequirement> {
|
||||
|
||||
/**
|
||||
* Creates a new instance.
|
||||
*/
|
||||
UserVerificationRequirementSerializer() {
|
||||
super(UserVerificationRequirement.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void serialize(UserVerificationRequirement requirement, JsonGenerator jgen, SerializerProvider provider)
|
||||
throws IOException {
|
||||
jgen.writeString(requirement.getValue());
|
||||
}
|
||||
|
||||
}
|
||||
-97
@@ -1,97 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2024 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.
|
||||
*/
|
||||
|
||||
package org.springframework.security.web.webauthn.jackson;
|
||||
|
||||
import com.fasterxml.jackson.core.Version;
|
||||
import com.fasterxml.jackson.databind.module.SimpleModule;
|
||||
|
||||
import org.springframework.security.web.webauthn.api.AttestationConveyancePreference;
|
||||
import org.springframework.security.web.webauthn.api.AuthenticationExtensionsClientInput;
|
||||
import org.springframework.security.web.webauthn.api.AuthenticationExtensionsClientInputs;
|
||||
import org.springframework.security.web.webauthn.api.AuthenticationExtensionsClientOutputs;
|
||||
import org.springframework.security.web.webauthn.api.AuthenticatorAssertionResponse;
|
||||
import org.springframework.security.web.webauthn.api.AuthenticatorAttachment;
|
||||
import org.springframework.security.web.webauthn.api.AuthenticatorAttestationResponse;
|
||||
import org.springframework.security.web.webauthn.api.AuthenticatorSelectionCriteria;
|
||||
import org.springframework.security.web.webauthn.api.AuthenticatorTransport;
|
||||
import org.springframework.security.web.webauthn.api.Bytes;
|
||||
import org.springframework.security.web.webauthn.api.COSEAlgorithmIdentifier;
|
||||
import org.springframework.security.web.webauthn.api.CredProtectAuthenticationExtensionsClientInput;
|
||||
import org.springframework.security.web.webauthn.api.CredentialPropertiesOutput;
|
||||
import org.springframework.security.web.webauthn.api.PublicKeyCredential;
|
||||
import org.springframework.security.web.webauthn.api.PublicKeyCredentialCreationOptions;
|
||||
import org.springframework.security.web.webauthn.api.PublicKeyCredentialRequestOptions;
|
||||
import org.springframework.security.web.webauthn.api.PublicKeyCredentialType;
|
||||
import org.springframework.security.web.webauthn.api.ResidentKeyRequirement;
|
||||
import org.springframework.security.web.webauthn.api.UserVerificationRequirement;
|
||||
import org.springframework.security.web.webauthn.management.RelyingPartyPublicKey;
|
||||
|
||||
/**
|
||||
* Adds Jackson support for Spring Security WebAuthn. It is automatically registered when
|
||||
* using Jackson's SPI support.
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 6.4
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class WebauthnJackson2Module extends SimpleModule {
|
||||
|
||||
/**
|
||||
* Creates a new instance.
|
||||
*/
|
||||
public WebauthnJackson2Module() {
|
||||
super(WebauthnJackson2Module.class.getName(), new Version(1, 0, 0, null, null, null));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setupModule(SetupContext context) {
|
||||
context.setMixInAnnotations(Bytes.class, BytesMixin.class);
|
||||
context.setMixInAnnotations(AttestationConveyancePreference.class, AttestationConveyancePreferenceMixin.class);
|
||||
context.setMixInAnnotations(AuthenticationExtensionsClientInput.class,
|
||||
AuthenticationExtensionsClientInputMixin.class);
|
||||
context.setMixInAnnotations(AuthenticationExtensionsClientInputs.class,
|
||||
AuthenticationExtensionsClientInputsMixin.class);
|
||||
context.setMixInAnnotations(AuthenticationExtensionsClientOutputs.class,
|
||||
AuthenticationExtensionsClientOutputsMixin.class);
|
||||
context.setMixInAnnotations(AuthenticatorAssertionResponse.AuthenticatorAssertionResponseBuilder.class,
|
||||
AuthenticatorAssertionResponseMixin.AuthenticatorAssertionResponseBuilderMixin.class);
|
||||
context.setMixInAnnotations(AuthenticatorAssertionResponse.class, AuthenticatorAssertionResponseMixin.class);
|
||||
context.setMixInAnnotations(AuthenticatorAttachment.class, AuthenticatorAttachmentMixin.class);
|
||||
context.setMixInAnnotations(AuthenticatorAttestationResponse.class,
|
||||
AuthenticatorAttestationResponseMixin.class);
|
||||
context.setMixInAnnotations(AuthenticatorAttestationResponse.AuthenticatorAttestationResponseBuilder.class,
|
||||
AuthenticatorAttestationResponseMixin.AuthenticatorAttestationResponseBuilderMixin.class);
|
||||
context.setMixInAnnotations(AuthenticatorSelectionCriteria.class, AuthenticatorSelectionCriteriaMixin.class);
|
||||
context.setMixInAnnotations(AuthenticatorTransport.class, AuthenticatorTransportMixin.class);
|
||||
context.setMixInAnnotations(COSEAlgorithmIdentifier.class, COSEAlgorithmIdentifierMixin.class);
|
||||
context.setMixInAnnotations(CredentialPropertiesOutput.class, CredentialPropertiesOutputMixin.class);
|
||||
context.setMixInAnnotations(CredProtectAuthenticationExtensionsClientInput.class,
|
||||
CredProtectAuthenticationExtensionsClientInputMixin.class);
|
||||
context.setMixInAnnotations(PublicKeyCredential.PublicKeyCredentialBuilder.class,
|
||||
PublicKeyCredentialMixin.PublicKeyCredentialBuilderMixin.class);
|
||||
context.setMixInAnnotations(PublicKeyCredential.class, PublicKeyCredentialMixin.class);
|
||||
context.setMixInAnnotations(PublicKeyCredentialCreationOptions.class,
|
||||
PublicKeyCredentialCreationOptionsMixin.class);
|
||||
context.setMixInAnnotations(PublicKeyCredentialRequestOptions.class,
|
||||
PublicKeyCredentialRequestOptionsMixin.class);
|
||||
context.setMixInAnnotations(PublicKeyCredentialType.class, PublicKeyCredentialTypeMixin.class);
|
||||
context.setMixInAnnotations(RelyingPartyPublicKey.class, RelyingPartyPublicKeyMixin.class);
|
||||
context.setMixInAnnotations(ResidentKeyRequirement.class, ResidentKeyRequirementMixin.class);
|
||||
context.setMixInAnnotations(UserVerificationRequirement.class, UserVerificationRequirementMixin.class);
|
||||
}
|
||||
|
||||
}
|
||||
-42
@@ -1,42 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2024 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.
|
||||
*/
|
||||
|
||||
package org.springframework.security.web.webauthn.management;
|
||||
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* An immutable implementation of {@link PublicKeyCredentialCreationOptionsRequest}.
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 6.4
|
||||
*/
|
||||
public class ImmutablePublicKeyCredentialCreationOptionsRequest implements PublicKeyCredentialCreationOptionsRequest {
|
||||
|
||||
private final Authentication authentication;
|
||||
|
||||
public ImmutablePublicKeyCredentialCreationOptionsRequest(Authentication authentication) {
|
||||
Assert.notNull(authentication, "authentication cannot be null");
|
||||
this.authentication = authentication;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Authentication getAuthentication() {
|
||||
return this.authentication;
|
||||
}
|
||||
|
||||
}
|
||||
-34
@@ -1,34 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2024 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.
|
||||
*/
|
||||
|
||||
package org.springframework.security.web.webauthn.management;
|
||||
|
||||
import org.springframework.security.core.Authentication;
|
||||
|
||||
public class ImmutablePublicKeyCredentialRequestOptionsRequest implements PublicKeyCredentialRequestOptionsRequest {
|
||||
|
||||
private final Authentication authentication;
|
||||
|
||||
public ImmutablePublicKeyCredentialRequestOptionsRequest(Authentication authentication) {
|
||||
this.authentication = authentication;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Authentication getAuthentication() {
|
||||
return this.authentication;
|
||||
}
|
||||
|
||||
}
|
||||
-60
@@ -1,60 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2024 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.
|
||||
*/
|
||||
|
||||
package org.springframework.security.web.webauthn.management;
|
||||
|
||||
import org.springframework.security.web.webauthn.api.PublicKeyCredentialCreationOptions;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Contains the information necessary to register a new Credential.
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 6.4
|
||||
* @see Webauthn4JRelyingPartyOperations#registerCredential(RelyingPartyRegistrationRequest)
|
||||
*/
|
||||
public class ImmutableRelyingPartyRegistrationRequest implements RelyingPartyRegistrationRequest {
|
||||
|
||||
private final PublicKeyCredentialCreationOptions options;
|
||||
|
||||
private final RelyingPartyPublicKey publicKey;
|
||||
|
||||
/**
|
||||
* Creates a new instance.
|
||||
* @param options the {@link PublicKeyCredentialCreationOptions} that were saved when
|
||||
* {@link WebAuthnRelyingPartyOperations#createCredentialRequestOptions(PublicKeyCredentialRequestOptionsRequest)}
|
||||
* was called.
|
||||
* @param publicKey this is submitted by the client and if validated stored.
|
||||
*/
|
||||
public ImmutableRelyingPartyRegistrationRequest(PublicKeyCredentialCreationOptions options,
|
||||
RelyingPartyPublicKey publicKey) {
|
||||
Assert.notNull(options, "options cannot be null");
|
||||
Assert.notNull(publicKey, "publicKey cannot be null");
|
||||
this.options = options;
|
||||
this.publicKey = publicKey;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PublicKeyCredentialCreationOptions getCreationOptions() {
|
||||
return this.options;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RelyingPartyPublicKey getPublicKey() {
|
||||
return this.publicKey;
|
||||
}
|
||||
|
||||
}
|
||||
-184
@@ -1,184 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2025 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.
|
||||
*/
|
||||
|
||||
package org.springframework.security.web.webauthn.management;
|
||||
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Types;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.springframework.jdbc.core.ArgumentPreparedStatementSetter;
|
||||
import org.springframework.jdbc.core.JdbcOperations;
|
||||
import org.springframework.jdbc.core.PreparedStatementSetter;
|
||||
import org.springframework.jdbc.core.RowMapper;
|
||||
import org.springframework.jdbc.core.SqlParameterValue;
|
||||
import org.springframework.security.web.webauthn.api.Bytes;
|
||||
import org.springframework.security.web.webauthn.api.ImmutablePublicKeyCredentialUserEntity;
|
||||
import org.springframework.security.web.webauthn.api.PublicKeyCredentialUserEntity;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* A JDBC implementation of an {@link PublicKeyCredentialUserEntityRepository} that uses a
|
||||
* {@link JdbcOperations} for {@link PublicKeyCredentialUserEntity} persistence.
|
||||
*
|
||||
* <b>NOTE:</b> This {@code PublicKeyCredentialUserEntityRepository} depends on the table
|
||||
* definition described in
|
||||
* "classpath:org/springframework/security/user-entities-schema.sql" and therefore MUST be
|
||||
* defined in the database schema.
|
||||
*
|
||||
* @author Max Batischev
|
||||
* @since 6.5
|
||||
* @see PublicKeyCredentialUserEntityRepository
|
||||
* @see PublicKeyCredentialUserEntity
|
||||
* @see JdbcOperations
|
||||
* @see RowMapper
|
||||
*/
|
||||
public final class JdbcPublicKeyCredentialUserEntityRepository implements PublicKeyCredentialUserEntityRepository {
|
||||
|
||||
private RowMapper<PublicKeyCredentialUserEntity> userEntityRowMapper = new UserEntityRecordRowMapper();
|
||||
|
||||
private Function<PublicKeyCredentialUserEntity, List<SqlParameterValue>> userEntityParametersMapper = new UserEntityParametersMapper();
|
||||
|
||||
private final JdbcOperations jdbcOperations;
|
||||
|
||||
private static final String TABLE_NAME = "user_entities";
|
||||
|
||||
// @formatter:off
|
||||
private static final String COLUMN_NAMES = "id, "
|
||||
+ "name, "
|
||||
+ "display_name ";
|
||||
// @formatter:on
|
||||
|
||||
// @formatter:off
|
||||
private static final String SAVE_USER_SQL = "INSERT INTO " + TABLE_NAME
|
||||
+ " (" + COLUMN_NAMES + ") VALUES (?, ?, ?)";
|
||||
// @formatter:on
|
||||
|
||||
private static final String ID_FILTER = "id = ? ";
|
||||
|
||||
private static final String USER_NAME_FILTER = "name = ? ";
|
||||
|
||||
// @formatter:off
|
||||
private static final String FIND_USER_BY_ID_SQL = "SELECT " + COLUMN_NAMES
|
||||
+ " FROM " + TABLE_NAME
|
||||
+ " WHERE " + ID_FILTER;
|
||||
// @formatter:on
|
||||
|
||||
// @formatter:off
|
||||
private static final String FIND_USER_BY_NAME_SQL = "SELECT " + COLUMN_NAMES
|
||||
+ " FROM " + TABLE_NAME
|
||||
+ " WHERE " + USER_NAME_FILTER;
|
||||
// @formatter:on
|
||||
|
||||
private static final String DELETE_USER_SQL = "DELETE FROM " + TABLE_NAME + " WHERE " + ID_FILTER;
|
||||
|
||||
// @formatter:off
|
||||
private static final String UPDATE_USER_SQL = "UPDATE " + TABLE_NAME
|
||||
+ " SET name = ?, display_name = ? "
|
||||
+ " WHERE " + ID_FILTER;
|
||||
// @formatter:on
|
||||
|
||||
/**
|
||||
* Constructs a {@code JdbcPublicKeyCredentialUserEntityRepository} using the provided
|
||||
* parameters.
|
||||
* @param jdbcOperations the JDBC operations
|
||||
*/
|
||||
public JdbcPublicKeyCredentialUserEntityRepository(JdbcOperations jdbcOperations) {
|
||||
Assert.notNull(jdbcOperations, "jdbcOperations cannot be null");
|
||||
this.jdbcOperations = jdbcOperations;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PublicKeyCredentialUserEntity findById(Bytes id) {
|
||||
Assert.notNull(id, "id cannot be null");
|
||||
List<PublicKeyCredentialUserEntity> result = this.jdbcOperations.query(FIND_USER_BY_ID_SQL,
|
||||
this.userEntityRowMapper, id.toBase64UrlString());
|
||||
return !result.isEmpty() ? result.get(0) : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PublicKeyCredentialUserEntity findByUsername(String username) {
|
||||
Assert.hasText(username, "name cannot be null or empty");
|
||||
List<PublicKeyCredentialUserEntity> result = this.jdbcOperations.query(FIND_USER_BY_NAME_SQL,
|
||||
this.userEntityRowMapper, username);
|
||||
return !result.isEmpty() ? result.get(0) : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save(PublicKeyCredentialUserEntity userEntity) {
|
||||
Assert.notNull(userEntity, "userEntity cannot be null");
|
||||
int rows = updateUserEntity(userEntity);
|
||||
if (rows == 0) {
|
||||
insertUserEntity(userEntity);
|
||||
}
|
||||
}
|
||||
|
||||
private void insertUserEntity(PublicKeyCredentialUserEntity userEntity) {
|
||||
List<SqlParameterValue> parameters = this.userEntityParametersMapper.apply(userEntity);
|
||||
PreparedStatementSetter pss = new ArgumentPreparedStatementSetter(parameters.toArray());
|
||||
this.jdbcOperations.update(SAVE_USER_SQL, pss);
|
||||
}
|
||||
|
||||
private int updateUserEntity(PublicKeyCredentialUserEntity userEntity) {
|
||||
List<SqlParameterValue> parameters = this.userEntityParametersMapper.apply(userEntity);
|
||||
SqlParameterValue userEntityId = parameters.remove(0);
|
||||
parameters.add(userEntityId);
|
||||
PreparedStatementSetter pss = new ArgumentPreparedStatementSetter(parameters.toArray());
|
||||
return this.jdbcOperations.update(UPDATE_USER_SQL, pss);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete(Bytes id) {
|
||||
Assert.notNull(id, "id cannot be null");
|
||||
SqlParameterValue[] parameters = new SqlParameterValue[] {
|
||||
new SqlParameterValue(Types.VARCHAR, id.toBase64UrlString()), };
|
||||
PreparedStatementSetter pss = new ArgumentPreparedStatementSetter(parameters);
|
||||
this.jdbcOperations.update(DELETE_USER_SQL, pss);
|
||||
}
|
||||
|
||||
private static class UserEntityParametersMapper
|
||||
implements Function<PublicKeyCredentialUserEntity, List<SqlParameterValue>> {
|
||||
|
||||
@Override
|
||||
public List<SqlParameterValue> apply(PublicKeyCredentialUserEntity userEntity) {
|
||||
List<SqlParameterValue> parameters = new ArrayList<>();
|
||||
|
||||
parameters.add(new SqlParameterValue(Types.VARCHAR, userEntity.getId().toBase64UrlString()));
|
||||
parameters.add(new SqlParameterValue(Types.VARCHAR, userEntity.getName()));
|
||||
parameters.add(new SqlParameterValue(Types.VARCHAR, userEntity.getDisplayName()));
|
||||
|
||||
return parameters;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class UserEntityRecordRowMapper implements RowMapper<PublicKeyCredentialUserEntity> {
|
||||
|
||||
@Override
|
||||
public PublicKeyCredentialUserEntity mapRow(ResultSet rs, int rowNum) throws SQLException {
|
||||
Bytes id = Bytes.fromBase64(new String(rs.getString("id").getBytes()));
|
||||
String name = rs.getString("name");
|
||||
String displayName = rs.getString("display_name");
|
||||
|
||||
return ImmutablePublicKeyCredentialUserEntity.builder().id(id).name(name).displayName(displayName).build();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
-357
@@ -1,357 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2025 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.
|
||||
*/
|
||||
|
||||
package org.springframework.security.web.webauthn.management;
|
||||
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Timestamp;
|
||||
import java.sql.Types;
|
||||
import java.time.Instant;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.springframework.jdbc.core.ArgumentPreparedStatementSetter;
|
||||
import org.springframework.jdbc.core.JdbcOperations;
|
||||
import org.springframework.jdbc.core.PreparedStatementSetter;
|
||||
import org.springframework.jdbc.core.RowMapper;
|
||||
import org.springframework.jdbc.core.SqlParameterValue;
|
||||
import org.springframework.jdbc.support.lob.LobCreator;
|
||||
import org.springframework.jdbc.support.lob.LobHandler;
|
||||
import org.springframework.security.web.webauthn.api.AuthenticatorTransport;
|
||||
import org.springframework.security.web.webauthn.api.Bytes;
|
||||
import org.springframework.security.web.webauthn.api.CredentialRecord;
|
||||
import org.springframework.security.web.webauthn.api.ImmutableCredentialRecord;
|
||||
import org.springframework.security.web.webauthn.api.ImmutablePublicKeyCose;
|
||||
import org.springframework.security.web.webauthn.api.PublicKeyCredentialType;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
/**
|
||||
* A JDBC implementation of an {@link UserCredentialRepository} that uses a
|
||||
* {@link JdbcOperations} for {@link CredentialRecord} persistence.
|
||||
*
|
||||
* <b>NOTE:</b> This {@code UserCredentialRepository} depends on the table definition
|
||||
* described in "classpath:org/springframework/security/user-credentials-schema.sql" and
|
||||
* therefore MUST be defined in the database schema.
|
||||
*
|
||||
* @author Max Batischev
|
||||
* @since 6.5
|
||||
* @see UserCredentialRepository
|
||||
* @see CredentialRecord
|
||||
* @see JdbcOperations
|
||||
* @see RowMapper
|
||||
*/
|
||||
public final class JdbcUserCredentialRepository implements UserCredentialRepository {
|
||||
|
||||
private RowMapper<CredentialRecord> credentialRecordRowMapper = new CredentialRecordRowMapper(ResultSet::getBytes);
|
||||
|
||||
private Function<CredentialRecord, List<SqlParameterValue>> credentialRecordParametersMapper = new CredentialRecordParametersMapper();
|
||||
|
||||
private SetBytes setBytes = PreparedStatement::setBytes;
|
||||
|
||||
private final JdbcOperations jdbcOperations;
|
||||
|
||||
private static final String TABLE_NAME = "user_credentials";
|
||||
|
||||
// @formatter:off
|
||||
private static final String COLUMN_NAMES = "credential_id, "
|
||||
+ "user_entity_user_id, "
|
||||
+ "public_key, "
|
||||
+ "signature_count, "
|
||||
+ "uv_initialized, "
|
||||
+ "backup_eligible, "
|
||||
+ "authenticator_transports, "
|
||||
+ "public_key_credential_type, "
|
||||
+ "backup_state, "
|
||||
+ "attestation_object, "
|
||||
+ "attestation_client_data_json, "
|
||||
+ "created, "
|
||||
+ "last_used, "
|
||||
+ "label ";
|
||||
// @formatter:on
|
||||
|
||||
// @formatter:off
|
||||
private static final String SAVE_CREDENTIAL_RECORD_SQL = "INSERT INTO " + TABLE_NAME
|
||||
+ " (" + COLUMN_NAMES + ") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
|
||||
// @formatter:on
|
||||
|
||||
private static final String ID_FILTER = "credential_id = ? ";
|
||||
|
||||
private static final String USER_ID_FILTER = "user_entity_user_id = ? ";
|
||||
|
||||
// @formatter:off
|
||||
private static final String FIND_CREDENTIAL_RECORD_BY_ID_SQL = "SELECT " + COLUMN_NAMES
|
||||
+ " FROM " + TABLE_NAME
|
||||
+ " WHERE " + ID_FILTER;
|
||||
// @formatter:on
|
||||
|
||||
// @formatter:off
|
||||
private static final String FIND_CREDENTIAL_RECORD_BY_USER_ID_SQL = "SELECT " + COLUMN_NAMES
|
||||
+ " FROM " + TABLE_NAME
|
||||
+ " WHERE " + USER_ID_FILTER;
|
||||
// @formatter:on
|
||||
|
||||
private static final String DELETE_CREDENTIAL_RECORD_SQL = "DELETE FROM " + TABLE_NAME + " WHERE " + ID_FILTER;
|
||||
|
||||
// @formatter:off
|
||||
private static final String UPDATE_CREDENTIAL_RECORD_SQL = "UPDATE " + TABLE_NAME
|
||||
+ " SET user_entity_user_id = ?, " +
|
||||
"public_key = ?, " +
|
||||
"signature_count = ?, " +
|
||||
"uv_initialized = ?, " +
|
||||
"backup_eligible = ? ," +
|
||||
"authenticator_transports = ?, " +
|
||||
"public_key_credential_type = ?, " +
|
||||
"backup_state = ?, " +
|
||||
"attestation_object = ?, " +
|
||||
"attestation_client_data_json = ?, " +
|
||||
"created = ?, " +
|
||||
"last_used = ?, " +
|
||||
"label = ?"
|
||||
+ " WHERE " + ID_FILTER;
|
||||
// @formatter:on
|
||||
|
||||
/**
|
||||
* Constructs a {@code JdbcUserCredentialRepository} using the provided parameters.
|
||||
* @param jdbcOperations the JDBC operations
|
||||
*/
|
||||
public JdbcUserCredentialRepository(JdbcOperations jdbcOperations) {
|
||||
Assert.notNull(jdbcOperations, "jdbcOperations cannot be null");
|
||||
this.jdbcOperations = jdbcOperations;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete(Bytes credentialId) {
|
||||
Assert.notNull(credentialId, "credentialId cannot be null");
|
||||
SqlParameterValue[] parameters = new SqlParameterValue[] {
|
||||
new SqlParameterValue(Types.VARCHAR, credentialId.toBase64UrlString()), };
|
||||
PreparedStatementSetter pss = new ArgumentPreparedStatementSetter(parameters);
|
||||
this.jdbcOperations.update(DELETE_CREDENTIAL_RECORD_SQL, pss);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save(CredentialRecord record) {
|
||||
Assert.notNull(record, "record cannot be null");
|
||||
int rows = updateCredentialRecord(record);
|
||||
if (rows == 0) {
|
||||
insertCredentialRecord(record);
|
||||
}
|
||||
}
|
||||
|
||||
private void insertCredentialRecord(CredentialRecord record) {
|
||||
List<SqlParameterValue> parameters = this.credentialRecordParametersMapper.apply(record);
|
||||
PreparedStatementSetter pss = new BlobArgumentPreparedStatementSetter(this.setBytes, parameters.toArray());
|
||||
this.jdbcOperations.update(SAVE_CREDENTIAL_RECORD_SQL, pss);
|
||||
}
|
||||
|
||||
private int updateCredentialRecord(CredentialRecord record) {
|
||||
List<SqlParameterValue> parameters = this.credentialRecordParametersMapper.apply(record);
|
||||
SqlParameterValue credentialId = parameters.remove(0);
|
||||
parameters.add(credentialId);
|
||||
PreparedStatementSetter pss = new BlobArgumentPreparedStatementSetter(this.setBytes, parameters.toArray());
|
||||
return this.jdbcOperations.update(UPDATE_CREDENTIAL_RECORD_SQL, pss);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CredentialRecord findByCredentialId(Bytes credentialId) {
|
||||
Assert.notNull(credentialId, "credentialId cannot be null");
|
||||
List<CredentialRecord> result = this.jdbcOperations.query(FIND_CREDENTIAL_RECORD_BY_ID_SQL,
|
||||
this.credentialRecordRowMapper, credentialId.toBase64UrlString());
|
||||
return !result.isEmpty() ? result.get(0) : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<CredentialRecord> findByUserId(Bytes userId) {
|
||||
Assert.notNull(userId, "userId cannot be null");
|
||||
return this.jdbcOperations.query(FIND_CREDENTIAL_RECORD_BY_USER_ID_SQL, this.credentialRecordRowMapper,
|
||||
userId.toBase64UrlString());
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a {@link LobHandler} for large binary fields and large text field parameters.
|
||||
* @param lobHandler the lob handler
|
||||
* @deprecated {@link LobHandler} is deprecated without replacement, as such this
|
||||
* method will also be removed without replacement
|
||||
*/
|
||||
@Deprecated(since = "6.5", forRemoval = true)
|
||||
public void setLobHandler(LobHandler lobHandler) {
|
||||
Assert.notNull(lobHandler, "lobHandler cannot be null");
|
||||
this.setBytes = (ps, index, bytes) -> {
|
||||
try (LobCreator creator = lobHandler.getLobCreator()) {
|
||||
creator.setBlobAsBytes(ps, index, bytes);
|
||||
}
|
||||
};
|
||||
this.credentialRecordRowMapper = new CredentialRecordRowMapper(lobHandler::getBlobAsBytes);
|
||||
}
|
||||
|
||||
private static class CredentialRecordParametersMapper
|
||||
implements Function<CredentialRecord, List<SqlParameterValue>> {
|
||||
|
||||
@Override
|
||||
public List<SqlParameterValue> apply(CredentialRecord record) {
|
||||
List<SqlParameterValue> parameters = new ArrayList<>();
|
||||
|
||||
List<String> transports = new ArrayList<>();
|
||||
if (!CollectionUtils.isEmpty(record.getTransports())) {
|
||||
for (AuthenticatorTransport transport : record.getTransports()) {
|
||||
transports.add(transport.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
parameters.add(new SqlParameterValue(Types.VARCHAR, record.getCredentialId().toBase64UrlString()));
|
||||
parameters.add(new SqlParameterValue(Types.VARCHAR, record.getUserEntityUserId().toBase64UrlString()));
|
||||
parameters.add(new SqlParameterValue(Types.BLOB, record.getPublicKey().getBytes()));
|
||||
parameters.add(new SqlParameterValue(Types.BIGINT, record.getSignatureCount()));
|
||||
parameters.add(new SqlParameterValue(Types.BOOLEAN, record.isUvInitialized()));
|
||||
parameters.add(new SqlParameterValue(Types.BOOLEAN, record.isBackupEligible()));
|
||||
parameters.add(new SqlParameterValue(Types.VARCHAR,
|
||||
(!CollectionUtils.isEmpty(record.getTransports())) ? String.join(",", transports) : ""));
|
||||
parameters.add(new SqlParameterValue(Types.VARCHAR,
|
||||
(record.getCredentialType() != null) ? record.getCredentialType().getValue() : null));
|
||||
parameters.add(new SqlParameterValue(Types.BOOLEAN, record.isBackupState()));
|
||||
parameters.add(new SqlParameterValue(Types.BLOB,
|
||||
(record.getAttestationObject() != null) ? record.getAttestationObject().getBytes() : null));
|
||||
parameters.add(new SqlParameterValue(Types.BLOB, (record.getAttestationClientDataJSON() != null)
|
||||
? record.getAttestationClientDataJSON().getBytes() : null));
|
||||
parameters.add(new SqlParameterValue(Types.TIMESTAMP, fromInstant(record.getCreated())));
|
||||
parameters.add(new SqlParameterValue(Types.TIMESTAMP, fromInstant(record.getLastUsed())));
|
||||
parameters.add(new SqlParameterValue(Types.VARCHAR, record.getLabel()));
|
||||
|
||||
return parameters;
|
||||
}
|
||||
|
||||
private Timestamp fromInstant(Instant instant) {
|
||||
if (instant == null) {
|
||||
return null;
|
||||
}
|
||||
return Timestamp.from(instant);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private interface SetBytes {
|
||||
|
||||
void setBytes(PreparedStatement ps, int index, byte[] bytes) throws SQLException;
|
||||
|
||||
}
|
||||
|
||||
private interface GetBytes {
|
||||
|
||||
byte[] getBytes(ResultSet rs, String columnName) throws SQLException;
|
||||
|
||||
}
|
||||
|
||||
private static final class BlobArgumentPreparedStatementSetter extends ArgumentPreparedStatementSetter {
|
||||
|
||||
private final SetBytes setBytes;
|
||||
|
||||
private BlobArgumentPreparedStatementSetter(SetBytes setBytes, Object[] args) {
|
||||
super(args);
|
||||
this.setBytes = setBytes;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doSetValue(PreparedStatement ps, int parameterPosition, Object argValue) throws SQLException {
|
||||
if (argValue instanceof SqlParameterValue paramValue) {
|
||||
if (paramValue.getSqlType() == Types.BLOB) {
|
||||
if (paramValue.getValue() != null) {
|
||||
Assert.isInstanceOf(byte[].class, paramValue.getValue(),
|
||||
"Value of blob parameter must be byte[]");
|
||||
}
|
||||
byte[] valueBytes = (byte[]) paramValue.getValue();
|
||||
this.setBytes.setBytes(ps, parameterPosition, valueBytes);
|
||||
return;
|
||||
}
|
||||
}
|
||||
super.doSetValue(ps, parameterPosition, argValue);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class CredentialRecordRowMapper implements RowMapper<CredentialRecord> {
|
||||
|
||||
private final GetBytes getBytes;
|
||||
|
||||
CredentialRecordRowMapper(GetBytes getBytes) {
|
||||
this.getBytes = getBytes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CredentialRecord mapRow(ResultSet rs, int rowNum) throws SQLException {
|
||||
Bytes credentialId = Bytes.fromBase64(new String(rs.getString("credential_id").getBytes()));
|
||||
Bytes userEntityUserId = Bytes.fromBase64(new String(rs.getString("user_entity_user_id").getBytes()));
|
||||
ImmutablePublicKeyCose publicKey = new ImmutablePublicKeyCose(this.getBytes.getBytes(rs, "public_key"));
|
||||
long signatureCount = rs.getLong("signature_count");
|
||||
boolean uvInitialized = rs.getBoolean("uv_initialized");
|
||||
boolean backupEligible = rs.getBoolean("backup_eligible");
|
||||
PublicKeyCredentialType credentialType = PublicKeyCredentialType
|
||||
.valueOf(rs.getString("public_key_credential_type"));
|
||||
boolean backupState = rs.getBoolean("backup_state");
|
||||
|
||||
Bytes attestationObject = null;
|
||||
byte[] rawAttestationObject = this.getBytes.getBytes(rs, "attestation_object");
|
||||
if (rawAttestationObject != null) {
|
||||
attestationObject = new Bytes(rawAttestationObject);
|
||||
}
|
||||
|
||||
Bytes attestationClientDataJson = null;
|
||||
byte[] rawAttestationClientDataJson = this.getBytes.getBytes(rs, "attestation_client_data_json");
|
||||
if (rawAttestationClientDataJson != null) {
|
||||
attestationClientDataJson = new Bytes(rawAttestationClientDataJson);
|
||||
}
|
||||
|
||||
Instant created = fromTimestamp(rs.getTimestamp("created"));
|
||||
Instant lastUsed = fromTimestamp(rs.getTimestamp("last_used"));
|
||||
String label = rs.getString("label");
|
||||
String[] transports = rs.getString("authenticator_transports").split(",");
|
||||
|
||||
Set<AuthenticatorTransport> authenticatorTransports = new HashSet<>();
|
||||
for (String transport : transports) {
|
||||
authenticatorTransports.add(AuthenticatorTransport.valueOf(transport));
|
||||
}
|
||||
return ImmutableCredentialRecord.builder()
|
||||
.credentialId(credentialId)
|
||||
.userEntityUserId(userEntityUserId)
|
||||
.publicKey(publicKey)
|
||||
.signatureCount(signatureCount)
|
||||
.uvInitialized(uvInitialized)
|
||||
.backupEligible(backupEligible)
|
||||
.credentialType(credentialType)
|
||||
.backupState(backupState)
|
||||
.attestationObject(attestationObject)
|
||||
.attestationClientDataJSON(attestationClientDataJson)
|
||||
.created(created)
|
||||
.label(label)
|
||||
.lastUsed(lastUsed)
|
||||
.transports(authenticatorTransports)
|
||||
.build();
|
||||
}
|
||||
|
||||
private Instant fromTimestamp(Timestamp timestamp) {
|
||||
if (timestamp == null) {
|
||||
return null;
|
||||
}
|
||||
return timestamp.toInstant();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
-67
@@ -1,67 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2024 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.
|
||||
*/
|
||||
|
||||
package org.springframework.security.web.webauthn.management;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.security.web.webauthn.api.Bytes;
|
||||
import org.springframework.security.web.webauthn.api.PublicKeyCredentialUserEntity;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* A {@link Map} based implementation of {@link PublicKeyCredentialUserEntityRepository}.
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 6.4
|
||||
*/
|
||||
public class MapPublicKeyCredentialUserEntityRepository implements PublicKeyCredentialUserEntityRepository {
|
||||
|
||||
private final Map<String, PublicKeyCredentialUserEntity> usernameToUserEntity = new HashMap<>();
|
||||
|
||||
private final Map<Bytes, PublicKeyCredentialUserEntity> idToUserEntity = new HashMap<>();
|
||||
|
||||
@Override
|
||||
public PublicKeyCredentialUserEntity findById(Bytes id) {
|
||||
Assert.notNull(id, "id cannot be null");
|
||||
return this.idToUserEntity.get(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PublicKeyCredentialUserEntity findByUsername(String username) {
|
||||
Assert.notNull(username, "username cannot be null");
|
||||
return this.usernameToUserEntity.get(username);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save(PublicKeyCredentialUserEntity userEntity) {
|
||||
if (userEntity == null) {
|
||||
throw new IllegalArgumentException("userEntity cannot be null");
|
||||
}
|
||||
this.usernameToUserEntity.put(userEntity.getName(), userEntity);
|
||||
this.idToUserEntity.put(userEntity.getId(), userEntity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete(Bytes id) {
|
||||
PublicKeyCredentialUserEntity existing = this.idToUserEntity.remove(id);
|
||||
if (existing != null) {
|
||||
this.usernameToUserEntity.remove(existing.getName());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
-77
@@ -1,77 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2024 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.
|
||||
*/
|
||||
|
||||
package org.springframework.security.web.webauthn.management;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.springframework.security.web.webauthn.api.Bytes;
|
||||
import org.springframework.security.web.webauthn.api.CredentialRecord;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* A {@link Map} based implementation of {@link UserCredentialRepository}.
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 6.4
|
||||
*/
|
||||
public class MapUserCredentialRepository implements UserCredentialRepository {
|
||||
|
||||
private final Map<Bytes, CredentialRecord> credentialIdToUserCredential = new HashMap<>();
|
||||
|
||||
private final Map<Bytes, Set<Bytes>> userEntityIdToUserCredentialIds = new HashMap<>();
|
||||
|
||||
@Override
|
||||
public void delete(Bytes credentialId) {
|
||||
Assert.notNull(credentialId, "credentialId cannot be null");
|
||||
CredentialRecord credentialRecord = this.credentialIdToUserCredential.remove(credentialId);
|
||||
if (credentialRecord != null) {
|
||||
Set<Bytes> credentialIds = this.userEntityIdToUserCredentialIds.get(credentialRecord.getUserEntityUserId());
|
||||
if (credentialIds != null) {
|
||||
credentialIds.remove(credentialId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save(CredentialRecord credentialRecord) {
|
||||
Assert.notNull(credentialRecord, "credentialRecord cannot be null");
|
||||
this.credentialIdToUserCredential.put(credentialRecord.getCredentialId(), credentialRecord);
|
||||
this.userEntityIdToUserCredentialIds
|
||||
.computeIfAbsent(credentialRecord.getUserEntityUserId(), (id) -> new HashSet<>())
|
||||
.add(credentialRecord.getCredentialId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public CredentialRecord findByCredentialId(Bytes credentialId) {
|
||||
Assert.notNull(credentialId, "credentialId cannot be null");
|
||||
return this.credentialIdToUserCredential.get(credentialId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<CredentialRecord> findByUserId(Bytes userId) {
|
||||
Assert.notNull(userId, "userId cannot be null");
|
||||
Set<Bytes> credentialIds = this.userEntityIdToUserCredentialIds.getOrDefault(userId, Collections.emptySet());
|
||||
return credentialIds.stream().map(this::findByCredentialId).collect(Collectors.toUnmodifiableList());
|
||||
}
|
||||
|
||||
}
|
||||
-38
@@ -1,38 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2024 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.
|
||||
*/
|
||||
|
||||
package org.springframework.security.web.webauthn.management;
|
||||
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.web.webauthn.api.PublicKeyCredentialCreationOptions;
|
||||
|
||||
/**
|
||||
* A request to create a new {@link PublicKeyCredentialCreationOptions}.
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 6.4
|
||||
* @see WebAuthnRelyingPartyOperations#createPublicKeyCredentialCreationOptions(PublicKeyCredentialCreationOptionsRequest)
|
||||
*/
|
||||
public interface PublicKeyCredentialCreationOptionsRequest {
|
||||
|
||||
/**
|
||||
* The current {@link Authentication}. It must be authenticated to associate the
|
||||
* credential to a user.
|
||||
* @return the {@link Authentication} to use.
|
||||
*/
|
||||
Authentication getAuthentication();
|
||||
|
||||
}
|
||||
-29
@@ -1,29 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2024 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.
|
||||
*/
|
||||
|
||||
package org.springframework.security.web.webauthn.management;
|
||||
|
||||
import org.springframework.security.core.Authentication;
|
||||
|
||||
public interface PublicKeyCredentialRequestOptionsRequest {
|
||||
|
||||
/**
|
||||
* The current {@link Authentication}. Possibly null or an anonymous.
|
||||
* @return the current {@link Authentication}
|
||||
*/
|
||||
Authentication getAuthentication();
|
||||
|
||||
}
|
||||
-54
@@ -1,54 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2024 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.
|
||||
*/
|
||||
|
||||
package org.springframework.security.web.webauthn.management;
|
||||
|
||||
import org.springframework.security.web.webauthn.api.Bytes;
|
||||
import org.springframework.security.web.webauthn.api.PublicKeyCredentialUserEntity;
|
||||
|
||||
/**
|
||||
* A repository for managing {@link PublicKeyCredentialUserEntity} instances.
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 6.4
|
||||
*/
|
||||
public interface PublicKeyCredentialUserEntityRepository {
|
||||
|
||||
/**
|
||||
* Finds the {@link PublicKeyCredentialUserEntity} by
|
||||
* {@link PublicKeyCredentialUserEntity#getId()}
|
||||
* @param id the id to lookup the username by
|
||||
* @return the username or null if not found.
|
||||
*/
|
||||
PublicKeyCredentialUserEntity findById(Bytes id);
|
||||
|
||||
/**
|
||||
* Finds the {@link PublicKeyCredentialUserEntity} by the username.
|
||||
* @param username the username to lookup the {@link PublicKeyCredentialUserEntity}
|
||||
* @return the {@link PublicKeyCredentialUserEntity} or null if not found.
|
||||
*/
|
||||
PublicKeyCredentialUserEntity findByUsername(String username);
|
||||
|
||||
/**
|
||||
* Saves the {@link PublicKeyCredentialUserEntity} to the associated username.
|
||||
* @param userEntity the {@link PublicKeyCredentialUserEntity} to associate to the
|
||||
* provided username. If null, any existing entry is deleted.
|
||||
*/
|
||||
void save(PublicKeyCredentialUserEntity userEntity);
|
||||
|
||||
void delete(Bytes id);
|
||||
|
||||
}
|
||||
-73
@@ -1,73 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2025 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.
|
||||
*/
|
||||
|
||||
package org.springframework.security.web.webauthn.management;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
|
||||
import org.springframework.security.web.webauthn.api.AuthenticatorAssertionResponse;
|
||||
import org.springframework.security.web.webauthn.api.PublicKeyCredential;
|
||||
import org.springframework.security.web.webauthn.api.PublicKeyCredentialRequestOptions;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* The data object used to provide the information necessary to authenticate a user with
|
||||
* WebAuthn.
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 6.4
|
||||
* @see WebAuthnRelyingPartyOperations#authenticate(RelyingPartyAuthenticationRequest)
|
||||
*/
|
||||
public class RelyingPartyAuthenticationRequest implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = -928083091875202086L;
|
||||
|
||||
private final PublicKeyCredentialRequestOptions requestOptions;
|
||||
|
||||
private final PublicKeyCredential<AuthenticatorAssertionResponse> publicKey;
|
||||
|
||||
/**
|
||||
* Creates a new instance.
|
||||
* @param requestOptions the {@link PublicKeyCredentialRequestOptions}
|
||||
* @param publicKey the {@link PublicKeyCredential}
|
||||
*/
|
||||
public RelyingPartyAuthenticationRequest(PublicKeyCredentialRequestOptions requestOptions,
|
||||
PublicKeyCredential<AuthenticatorAssertionResponse> publicKey) {
|
||||
Assert.notNull(requestOptions, "requestOptions cannot be null");
|
||||
Assert.notNull(publicKey, "publicKey cannot be null");
|
||||
this.requestOptions = requestOptions;
|
||||
this.publicKey = publicKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ges the request options.
|
||||
* @return the request options.
|
||||
*/
|
||||
public PublicKeyCredentialRequestOptions getRequestOptions() {
|
||||
return this.requestOptions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the public key.
|
||||
* @return the public key.
|
||||
*/
|
||||
public PublicKeyCredential<AuthenticatorAssertionResponse> getPublicKey() {
|
||||
return this.publicKey;
|
||||
}
|
||||
|
||||
}
|
||||
-55
@@ -1,55 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2024 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.
|
||||
*/
|
||||
|
||||
package org.springframework.security.web.webauthn.management;
|
||||
|
||||
import org.springframework.security.web.webauthn.api.AuthenticatorAttestationResponse;
|
||||
import org.springframework.security.web.webauthn.api.PublicKeyCredential;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Submitted by a client to request registration of a new credential.
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 6.4
|
||||
*/
|
||||
public class RelyingPartyPublicKey {
|
||||
|
||||
private final PublicKeyCredential<AuthenticatorAttestationResponse> credential;
|
||||
|
||||
private final String label;
|
||||
|
||||
/**
|
||||
* Creates a new instance.
|
||||
* @param credential the credential
|
||||
* @param label a human readable label that will be associated to the credential
|
||||
*/
|
||||
public RelyingPartyPublicKey(PublicKeyCredential<AuthenticatorAttestationResponse> credential, String label) {
|
||||
Assert.notNull(credential, "credential cannot be null");
|
||||
Assert.notNull(label, "label cannot be null");
|
||||
this.credential = credential;
|
||||
this.label = label;
|
||||
}
|
||||
|
||||
public PublicKeyCredential<AuthenticatorAttestationResponse> getCredential() {
|
||||
return this.credential;
|
||||
}
|
||||
|
||||
public String getLabel() {
|
||||
return this.label;
|
||||
}
|
||||
|
||||
}
|
||||
-32
@@ -1,32 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2024 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.
|
||||
*/
|
||||
|
||||
package org.springframework.security.web.webauthn.management;
|
||||
|
||||
import org.springframework.security.web.webauthn.api.PublicKeyCredentialCreationOptions;
|
||||
|
||||
/**
|
||||
* @author Rob Winch
|
||||
* @since 6.4
|
||||
* @see WebAuthnRelyingPartyOperations#registerCredential(RelyingPartyRegistrationRequest)
|
||||
*/
|
||||
public interface RelyingPartyRegistrationRequest {
|
||||
|
||||
PublicKeyCredentialCreationOptions getCreationOptions();
|
||||
|
||||
RelyingPartyPublicKey getPublicKey();
|
||||
|
||||
}
|
||||
-62
@@ -1,62 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2024 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.
|
||||
*/
|
||||
|
||||
package org.springframework.security.web.webauthn.management;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.security.web.webauthn.api.Bytes;
|
||||
import org.springframework.security.web.webauthn.api.CredentialRecord;
|
||||
import org.springframework.security.web.webauthn.api.PublicKeyCredentialUserEntity;
|
||||
|
||||
/**
|
||||
* A repository for managing {@link CredentialRecord}s associated to a user.
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 6.4
|
||||
*/
|
||||
public interface UserCredentialRepository {
|
||||
|
||||
/**
|
||||
* Deletes an entry by credential id
|
||||
* @param credentialId {@link CredentialRecord#getCredentialId()}
|
||||
*/
|
||||
void delete(Bytes credentialId);
|
||||
|
||||
/**
|
||||
* Saves a {@link CredentialRecord}
|
||||
* @param credentialRecord the {@link CredentialRecord} to save.
|
||||
*/
|
||||
void save(CredentialRecord credentialRecord);
|
||||
|
||||
/**
|
||||
* Finds an entry by credential id.
|
||||
* @param credentialId {@link CredentialRecord#getCredentialId()}
|
||||
* @return the {@link CredentialRecord} or null if not found.
|
||||
*/
|
||||
CredentialRecord findByCredentialId(Bytes credentialId);
|
||||
|
||||
/**
|
||||
* Finds all {@link CredentialRecord} instances for a specific user.
|
||||
* @param userId the {@link PublicKeyCredentialUserEntity#getId()} to search for a
|
||||
* user.
|
||||
* @return all {@link CredentialRecord} instances for a specific user or empty if no
|
||||
* results found. Never null.
|
||||
* @see PublicKeyCredentialUserEntityRepository
|
||||
*/
|
||||
List<CredentialRecord> findByUserId(Bytes userId);
|
||||
|
||||
}
|
||||
-72
@@ -1,72 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2024 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.
|
||||
*/
|
||||
|
||||
package org.springframework.security.web.webauthn.management;
|
||||
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.web.webauthn.api.CredentialRecord;
|
||||
import org.springframework.security.web.webauthn.api.PublicKeyCredentialCreationOptions;
|
||||
import org.springframework.security.web.webauthn.api.PublicKeyCredentialRequestOptions;
|
||||
import org.springframework.security.web.webauthn.api.PublicKeyCredentialUserEntity;
|
||||
|
||||
/**
|
||||
* An API for <a href="https://www.w3.org/TR/webauthn-3/#sctn-rp-operations">WebAuthn
|
||||
* Relying Party Operations</a>
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 6.4
|
||||
*/
|
||||
public interface WebAuthnRelyingPartyOperations {
|
||||
|
||||
/**
|
||||
* Creates the {@link PublicKeyCredentialCreationOptions} used to register new
|
||||
* credentials.
|
||||
* @param request the {@link PublicKeyCredentialCreationOptionsRequest} to create the
|
||||
* {@link PublicKeyCredentialCreationOptions}
|
||||
* @return the {@link PublicKeyCredentialCreationOptions} for the
|
||||
* {@link Authentication} passed in. Cannot be null.
|
||||
*/
|
||||
PublicKeyCredentialCreationOptions createPublicKeyCredentialCreationOptions(
|
||||
PublicKeyCredentialCreationOptionsRequest request);
|
||||
|
||||
/**
|
||||
* If {@link RelyingPartyRegistrationRequest} is valid, will <a href=
|
||||
* "https://www.w3.org/TR/webauthn-3/#sctn-registering-a-new-credential">register</a>
|
||||
* and return a new {@link CredentialRecord}.
|
||||
* @param relyingPartyRegistrationRequest the {@link RelyingPartyRegistrationRequest}
|
||||
* to process.
|
||||
* @return a new {@link CredentialRecord}
|
||||
* @throws RuntimeException if the {@link RelyingPartyRegistrationRequest} is not
|
||||
* valid.
|
||||
*/
|
||||
CredentialRecord registerCredential(RelyingPartyRegistrationRequest relyingPartyRegistrationRequest);
|
||||
|
||||
/**
|
||||
* Creates the {@link PublicKeyCredentialRequestOptions} used to authenticate a user.
|
||||
* @param request the {@link PublicKeyCredentialRequestOptionsRequest}.
|
||||
* @return the {@link PublicKeyCredentialRequestOptions} used to authenticate a user.
|
||||
*/
|
||||
PublicKeyCredentialRequestOptions createCredentialRequestOptions(PublicKeyCredentialRequestOptionsRequest request);
|
||||
|
||||
/**
|
||||
* Authenticates the {@link RelyingPartyAuthenticationRequest} passed in
|
||||
* @param request the {@link RelyingPartyAuthenticationRequest}
|
||||
* @return the principal name (e.g. username) if authentication was successful
|
||||
* @throws RuntimeException if authentication fails
|
||||
*/
|
||||
PublicKeyCredentialUserEntity authenticate(RelyingPartyAuthenticationRequest request);
|
||||
|
||||
}
|
||||
-403
@@ -1,403 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2025 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.
|
||||
*/
|
||||
|
||||
package org.springframework.security.web.webauthn.management;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import com.webauthn4j.WebAuthnManager;
|
||||
import com.webauthn4j.authenticator.Authenticator;
|
||||
import com.webauthn4j.authenticator.AuthenticatorImpl;
|
||||
import com.webauthn4j.converter.util.CborConverter;
|
||||
import com.webauthn4j.converter.util.ObjectConverter;
|
||||
import com.webauthn4j.data.AuthenticationData;
|
||||
import com.webauthn4j.data.AuthenticationParameters;
|
||||
import com.webauthn4j.data.RegistrationData;
|
||||
import com.webauthn4j.data.RegistrationParameters;
|
||||
import com.webauthn4j.data.RegistrationRequest;
|
||||
import com.webauthn4j.data.attestation.AttestationObject;
|
||||
import com.webauthn4j.data.attestation.authenticator.AttestedCredentialData;
|
||||
import com.webauthn4j.data.attestation.authenticator.AuthenticatorData;
|
||||
import com.webauthn4j.data.attestation.authenticator.COSEKey;
|
||||
import com.webauthn4j.data.client.Origin;
|
||||
import com.webauthn4j.data.client.challenge.Challenge;
|
||||
import com.webauthn4j.data.client.challenge.DefaultChallenge;
|
||||
import com.webauthn4j.data.extension.authenticator.RegistrationExtensionAuthenticatorOutput;
|
||||
import com.webauthn4j.server.ServerProperty;
|
||||
|
||||
import org.springframework.security.authentication.AuthenticationTrustResolver;
|
||||
import org.springframework.security.authentication.AuthenticationTrustResolverImpl;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.web.webauthn.api.AttestationConveyancePreference;
|
||||
import org.springframework.security.web.webauthn.api.AuthenticatorAssertionResponse;
|
||||
import org.springframework.security.web.webauthn.api.AuthenticatorAttestationResponse;
|
||||
import org.springframework.security.web.webauthn.api.AuthenticatorSelectionCriteria;
|
||||
import org.springframework.security.web.webauthn.api.AuthenticatorTransport;
|
||||
import org.springframework.security.web.webauthn.api.Bytes;
|
||||
import org.springframework.security.web.webauthn.api.CredentialRecord;
|
||||
import org.springframework.security.web.webauthn.api.ImmutableAuthenticationExtensionsClientInput;
|
||||
import org.springframework.security.web.webauthn.api.ImmutableAuthenticationExtensionsClientInputs;
|
||||
import org.springframework.security.web.webauthn.api.ImmutableCredentialRecord;
|
||||
import org.springframework.security.web.webauthn.api.ImmutablePublicKeyCose;
|
||||
import org.springframework.security.web.webauthn.api.ImmutablePublicKeyCredentialUserEntity;
|
||||
import org.springframework.security.web.webauthn.api.PublicKeyCredential;
|
||||
import org.springframework.security.web.webauthn.api.PublicKeyCredentialCreationOptions;
|
||||
import org.springframework.security.web.webauthn.api.PublicKeyCredentialCreationOptions.PublicKeyCredentialCreationOptionsBuilder;
|
||||
import org.springframework.security.web.webauthn.api.PublicKeyCredentialDescriptor;
|
||||
import org.springframework.security.web.webauthn.api.PublicKeyCredentialParameters;
|
||||
import org.springframework.security.web.webauthn.api.PublicKeyCredentialRequestOptions;
|
||||
import org.springframework.security.web.webauthn.api.PublicKeyCredentialRequestOptions.PublicKeyCredentialRequestOptionsBuilder;
|
||||
import org.springframework.security.web.webauthn.api.PublicKeyCredentialRpEntity;
|
||||
import org.springframework.security.web.webauthn.api.PublicKeyCredentialType;
|
||||
import org.springframework.security.web.webauthn.api.PublicKeyCredentialUserEntity;
|
||||
import org.springframework.security.web.webauthn.api.ResidentKeyRequirement;
|
||||
import org.springframework.security.web.webauthn.api.UserVerificationRequirement;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* A <a href="https://webauthn4j.github.io/webauthn4j/en/">WebAuthn4j</a> implementation
|
||||
* of {@link WebAuthnRelyingPartyOperations}.
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 6.4
|
||||
*/
|
||||
public class Webauthn4JRelyingPartyOperations implements WebAuthnRelyingPartyOperations {
|
||||
|
||||
private final PublicKeyCredentialUserEntityRepository userEntities;
|
||||
|
||||
private final UserCredentialRepository userCredentials;
|
||||
|
||||
private final Set<String> allowedOrigins;
|
||||
|
||||
private final PublicKeyCredentialRpEntity rp;
|
||||
|
||||
private final ObjectConverter objectConverter = new ObjectConverter();
|
||||
|
||||
private final AuthenticationTrustResolver trustResolver = new AuthenticationTrustResolverImpl();
|
||||
|
||||
private WebAuthnManager webAuthnManager = WebAuthnManager.createNonStrictWebAuthnManager();
|
||||
|
||||
private Consumer<PublicKeyCredentialCreationOptionsBuilder> customizeCreationOptions = (options) -> {
|
||||
};
|
||||
|
||||
private Consumer<PublicKeyCredentialRequestOptionsBuilder> customizeRequestOptions = (options) -> {
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates a new instance.
|
||||
* @param userEntities the {@link PublicKeyCredentialUserEntityRepository} to use.
|
||||
* @param userCredentials the {@link UserCredentialRepository} to use.
|
||||
* @param rpEntity the {@link PublicKeyCredentialRpEntity} to use.
|
||||
* @param allowedOrigins the allowed origins.
|
||||
*/
|
||||
public Webauthn4JRelyingPartyOperations(PublicKeyCredentialUserEntityRepository userEntities,
|
||||
UserCredentialRepository userCredentials, PublicKeyCredentialRpEntity rpEntity,
|
||||
Set<String> allowedOrigins) {
|
||||
Assert.notNull(userEntities, "userEntities cannot be null");
|
||||
Assert.notNull(userCredentials, "userCredentials cannot be null");
|
||||
Assert.notNull(rpEntity, "rpEntity cannot be null");
|
||||
Assert.notNull(allowedOrigins, "allowedOrigins cannot be null");
|
||||
this.userEntities = userEntities;
|
||||
this.userCredentials = userCredentials;
|
||||
this.rp = rpEntity;
|
||||
this.allowedOrigins = allowedOrigins;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link WebAuthnManager} to use. The default is
|
||||
* {@link WebAuthnManager#createNonStrictWebAuthnManager()}
|
||||
* @param webAuthnManager the {@link WebAuthnManager}.
|
||||
*/
|
||||
public void setWebAuthnManager(WebAuthnManager webAuthnManager) {
|
||||
Assert.notNull(webAuthnManager, "webAuthnManager cannot be null");
|
||||
this.webAuthnManager = webAuthnManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a {@link Consumer} used to customize the
|
||||
* {@link PublicKeyCredentialCreationOptionsBuilder} for
|
||||
* {@link #createPublicKeyCredentialCreationOptions(PublicKeyCredentialCreationOptionsRequest)}.
|
||||
* The default values are always populated, but can be overridden with this property.
|
||||
* @param customizeCreationOptions the {@link Consumer} to customize the
|
||||
* {@link PublicKeyCredentialCreationOptionsBuilder}
|
||||
*/
|
||||
public void setCustomizeCreationOptions(
|
||||
Consumer<PublicKeyCredentialCreationOptionsBuilder> customizeCreationOptions) {
|
||||
Assert.notNull(customizeCreationOptions, "customizeCreationOptions must not be null");
|
||||
this.customizeCreationOptions = customizeCreationOptions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a {@link Consumer} used to customize the
|
||||
* {@link PublicKeyCredentialRequestOptionsBuilder} for
|
||||
* {@link #createCredentialRequestOptions(PublicKeyCredentialRequestOptionsRequest)}.The
|
||||
* default values are always populated, but can be overridden with this property.
|
||||
* @param customizeRequestOptions the {@link Consumer} to customize the
|
||||
* {@link PublicKeyCredentialRequestOptionsBuilder}
|
||||
*/
|
||||
public void setCustomizeRequestOptions(Consumer<PublicKeyCredentialRequestOptionsBuilder> customizeRequestOptions) {
|
||||
Assert.notNull(customizeRequestOptions, "customizeRequestOptions cannot be null");
|
||||
this.customizeRequestOptions = customizeRequestOptions;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PublicKeyCredentialCreationOptions createPublicKeyCredentialCreationOptions(
|
||||
PublicKeyCredentialCreationOptionsRequest request) {
|
||||
if (request == null) {
|
||||
throw new IllegalArgumentException("request cannot be null");
|
||||
}
|
||||
Authentication authentication = request.getAuthentication();
|
||||
if (!this.trustResolver.isAuthenticated(authentication)) {
|
||||
throw new IllegalArgumentException("Authentication must be authenticated");
|
||||
}
|
||||
AuthenticatorSelectionCriteria authenticatorSelection = AuthenticatorSelectionCriteria.builder()
|
||||
.userVerification(UserVerificationRequirement.PREFERRED)
|
||||
.residentKey(ResidentKeyRequirement.REQUIRED)
|
||||
.build();
|
||||
|
||||
ImmutableAuthenticationExtensionsClientInputs clientInputs = new ImmutableAuthenticationExtensionsClientInputs(
|
||||
ImmutableAuthenticationExtensionsClientInput.credProps);
|
||||
|
||||
PublicKeyCredentialUserEntity userEntity = findUserEntityOrCreateAndSave(authentication.getName());
|
||||
List<CredentialRecord> credentialRecords = this.userCredentials.findByUserId(userEntity.getId());
|
||||
|
||||
PublicKeyCredentialCreationOptions options = PublicKeyCredentialCreationOptions.builder()
|
||||
.attestation(AttestationConveyancePreference.NONE)
|
||||
.pubKeyCredParams(PublicKeyCredentialParameters.EdDSA, PublicKeyCredentialParameters.ES256,
|
||||
PublicKeyCredentialParameters.RS256)
|
||||
.authenticatorSelection(authenticatorSelection)
|
||||
.challenge(Bytes.random())
|
||||
.extensions(clientInputs)
|
||||
.timeout(Duration.ofMinutes(5))
|
||||
.user(userEntity)
|
||||
.rp(this.rp)
|
||||
.excludeCredentials(credentialDescriptors(credentialRecords))
|
||||
.customize(this.customizeCreationOptions)
|
||||
.build();
|
||||
return options;
|
||||
}
|
||||
|
||||
private static List<PublicKeyCredentialDescriptor> credentialDescriptors(List<CredentialRecord> credentialRecords) {
|
||||
List<PublicKeyCredentialDescriptor> result = new ArrayList<>();
|
||||
for (CredentialRecord credentialRecord : credentialRecords) {
|
||||
Bytes id = Bytes.fromBase64(credentialRecord.getCredentialId().toBase64UrlString());
|
||||
PublicKeyCredentialDescriptor credentialDescriptor = PublicKeyCredentialDescriptor.builder()
|
||||
.id(id)
|
||||
.transports(credentialRecord.getTransports())
|
||||
.build();
|
||||
result.add(credentialDescriptor);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private PublicKeyCredentialUserEntity findUserEntityOrCreateAndSave(String username) {
|
||||
final PublicKeyCredentialUserEntity foundUserEntity = this.userEntities.findByUsername(username);
|
||||
if (foundUserEntity != null) {
|
||||
return foundUserEntity;
|
||||
}
|
||||
|
||||
PublicKeyCredentialUserEntity userEntity = ImmutablePublicKeyCredentialUserEntity.builder()
|
||||
.displayName(username)
|
||||
.id(Bytes.random())
|
||||
.name(username)
|
||||
.build();
|
||||
this.userEntities.save(userEntity);
|
||||
return userEntity;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CredentialRecord registerCredential(RelyingPartyRegistrationRequest rpRegistrationRequest) {
|
||||
Assert.notNull(rpRegistrationRequest, "rpRegistrationRequest cannot be null");
|
||||
Bytes credentialId = rpRegistrationRequest.getPublicKey().getCredential().getRawId();
|
||||
CredentialRecord existingCredential = this.userCredentials.findByCredentialId(credentialId);
|
||||
if (existingCredential != null) {
|
||||
throw new IllegalArgumentException("Credential with id " + credentialId + " already exists");
|
||||
}
|
||||
PublicKeyCredentialCreationOptions creationOptions = rpRegistrationRequest.getCreationOptions();
|
||||
String rpId = creationOptions.getRp().getId();
|
||||
RelyingPartyPublicKey publicKey = rpRegistrationRequest.getPublicKey();
|
||||
PublicKeyCredential<AuthenticatorAttestationResponse> credential = publicKey.getCredential();
|
||||
AuthenticatorAttestationResponse response = credential.getResponse();
|
||||
// Server properties
|
||||
Set<Origin> origins = toOrigins();
|
||||
byte[] base64Challenge = creationOptions.getChallenge().getBytes();
|
||||
byte[] attestationObject = response.getAttestationObject().getBytes();
|
||||
byte[] clientDataJSON = response.getClientDataJSON().getBytes();
|
||||
Challenge challenge = new DefaultChallenge(base64Challenge);
|
||||
byte[] tokenBindingId = null /* set tokenBindingId */; // FIXME:
|
||||
// https://www.w3.org/TR/webauthn-1/#dom-collectedclientdata-tokenbinding
|
||||
ServerProperty serverProperty = new ServerProperty(origins, rpId, challenge, tokenBindingId);
|
||||
boolean userVerificationRequired = creationOptions.getAuthenticatorSelection()
|
||||
.getUserVerification() == UserVerificationRequirement.REQUIRED;
|
||||
// requireUserPresence The constant Boolean value true
|
||||
// https://www.w3.org/TR/webauthn-3/#sctn-op-make-cred
|
||||
boolean userPresenceRequired = true;
|
||||
List<com.webauthn4j.data.PublicKeyCredentialParameters> pubKeyCredParams = convertCredentialParamsToWebauthn4j(
|
||||
creationOptions.getPubKeyCredParams());
|
||||
Set<String> transports = convertTransportsToString(response);
|
||||
RegistrationRequest webauthn4jRegistrationRequest = new RegistrationRequest(attestationObject, clientDataJSON,
|
||||
transports);
|
||||
RegistrationParameters registrationParameters = new RegistrationParameters(serverProperty, pubKeyCredParams,
|
||||
userVerificationRequired, userPresenceRequired);
|
||||
RegistrationData registrationData = this.webAuthnManager.validate(webauthn4jRegistrationRequest,
|
||||
registrationParameters);
|
||||
AuthenticatorData<RegistrationExtensionAuthenticatorOutput> authData = registrationData.getAttestationObject()
|
||||
.getAuthenticatorData();
|
||||
|
||||
CborConverter cborConverter = this.objectConverter.getCborConverter();
|
||||
COSEKey coseKey = authData.getAttestedCredentialData().getCOSEKey();
|
||||
byte[] rawCoseKey = cborConverter.writeValueAsBytes(coseKey);
|
||||
ImmutableCredentialRecord userCredential = ImmutableCredentialRecord.builder()
|
||||
.userEntityUserId(creationOptions.getUser().getId())
|
||||
.credentialType(credential.getType())
|
||||
.credentialId(credential.getRawId())
|
||||
.publicKey(new ImmutablePublicKeyCose(rawCoseKey))
|
||||
.signatureCount(authData.getSignCount())
|
||||
.uvInitialized(authData.isFlagUV())
|
||||
.transports(convertTransports(registrationData.getTransports()))
|
||||
.backupEligible(authData.isFlagBE())
|
||||
.backupState(authData.isFlagBS())
|
||||
.label(publicKey.getLabel())
|
||||
.attestationClientDataJSON(credential.getResponse().getClientDataJSON())
|
||||
.attestationObject(credential.getResponse().getAttestationObject())
|
||||
.build();
|
||||
this.userCredentials.save(userCredential);
|
||||
return userCredential;
|
||||
}
|
||||
|
||||
private static Set<String> convertTransportsToString(AuthenticatorAttestationResponse response) {
|
||||
if (response.getTransports() == null) {
|
||||
return null;
|
||||
}
|
||||
Set<String> transports = new HashSet<>(response.getTransports().size());
|
||||
for (AuthenticatorTransport transport : response.getTransports()) {
|
||||
transports.add(transport.getValue());
|
||||
}
|
||||
return transports;
|
||||
}
|
||||
|
||||
private List<com.webauthn4j.data.PublicKeyCredentialParameters> convertCredentialParamsToWebauthn4j(
|
||||
List<PublicKeyCredentialParameters> parameters) {
|
||||
return parameters.stream().map(this::convertParamToWebauthn4j).collect(Collectors.toUnmodifiableList());
|
||||
}
|
||||
|
||||
private com.webauthn4j.data.PublicKeyCredentialParameters convertParamToWebauthn4j(
|
||||
PublicKeyCredentialParameters parameter) {
|
||||
if (parameter.getType() != PublicKeyCredentialType.PUBLIC_KEY) {
|
||||
throw new IllegalArgumentException(
|
||||
"Cannot convert unknown credential type " + parameter.getType() + " to webauthn4j");
|
||||
}
|
||||
long algValue = parameter.getAlg().getValue();
|
||||
com.webauthn4j.data.attestation.statement.COSEAlgorithmIdentifier alg = com.webauthn4j.data.attestation.statement.COSEAlgorithmIdentifier
|
||||
.create(algValue);
|
||||
return new com.webauthn4j.data.PublicKeyCredentialParameters(
|
||||
com.webauthn4j.data.PublicKeyCredentialType.PUBLIC_KEY, alg);
|
||||
}
|
||||
|
||||
private Set<Origin> toOrigins() {
|
||||
return this.allowedOrigins.stream().map(Origin::new).collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
private static Set<AuthenticatorTransport> convertTransports(
|
||||
Set<com.webauthn4j.data.AuthenticatorTransport> transports) {
|
||||
if (transports == null) {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
return transports.stream()
|
||||
.map((t) -> AuthenticatorTransport.valueOf(t.getValue()))
|
||||
.collect(Collectors.toUnmodifiableSet());
|
||||
}
|
||||
|
||||
@Override
|
||||
public PublicKeyCredentialRequestOptions createCredentialRequestOptions(
|
||||
PublicKeyCredentialRequestOptionsRequest request) {
|
||||
Authentication authentication = request.getAuthentication();
|
||||
List<CredentialRecord> credentialRecords = findCredentialRecords(authentication);
|
||||
return PublicKeyCredentialRequestOptions.builder()
|
||||
.allowCredentials(credentialDescriptors(credentialRecords))
|
||||
.challenge(Bytes.random())
|
||||
.rpId(this.rp.getId())
|
||||
.timeout(Duration.ofMinutes(5))
|
||||
.userVerification(UserVerificationRequirement.PREFERRED)
|
||||
.customize(this.customizeRequestOptions)
|
||||
.build();
|
||||
}
|
||||
|
||||
private List<CredentialRecord> findCredentialRecords(Authentication authentication) {
|
||||
if (!this.trustResolver.isAuthenticated(authentication)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
PublicKeyCredentialUserEntity userEntity = this.userEntities.findByUsername(authentication.getName());
|
||||
if (userEntity == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return this.userCredentials.findByUserId(userEntity.getId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public PublicKeyCredentialUserEntity authenticate(RelyingPartyAuthenticationRequest request) {
|
||||
PublicKeyCredentialRequestOptions requestOptions = request.getRequestOptions();
|
||||
AuthenticatorAssertionResponse assertionResponse = request.getPublicKey().getResponse();
|
||||
Bytes keyId = request.getPublicKey().getRawId();
|
||||
CredentialRecord credentialRecord = this.userCredentials.findByCredentialId(keyId);
|
||||
|
||||
CborConverter cborConverter = this.objectConverter.getCborConverter();
|
||||
AttestationObject attestationObject = cborConverter
|
||||
.readValue(credentialRecord.getAttestationObject().getBytes(), AttestationObject.class);
|
||||
|
||||
AuthenticatorData<RegistrationExtensionAuthenticatorOutput> authData = attestationObject.getAuthenticatorData();
|
||||
AttestedCredentialData data = new AttestedCredentialData(authData.getAttestedCredentialData().getAaguid(),
|
||||
keyId.getBytes(), authData.getAttestedCredentialData().getCOSEKey());
|
||||
|
||||
Authenticator authenticator = new AuthenticatorImpl(data, attestationObject.getAttestationStatement(),
|
||||
credentialRecord.getSignatureCount());
|
||||
Set<Origin> origins = toOrigins();
|
||||
Challenge challenge = new DefaultChallenge(requestOptions.getChallenge().getBytes());
|
||||
// FIXME: should populate this
|
||||
byte[] tokenBindingId = null /* set tokenBindingId */;
|
||||
ServerProperty serverProperty = new ServerProperty(origins, requestOptions.getRpId(), challenge,
|
||||
tokenBindingId);
|
||||
boolean userVerificationRequired = request.getRequestOptions()
|
||||
.getUserVerification() == UserVerificationRequirement.REQUIRED;
|
||||
|
||||
com.webauthn4j.data.AuthenticationRequest authenticationRequest = new com.webauthn4j.data.AuthenticationRequest(
|
||||
request.getPublicKey().getId().getBytes(), assertionResponse.getAuthenticatorData().getBytes(),
|
||||
assertionResponse.getClientDataJSON().getBytes(), assertionResponse.getSignature().getBytes());
|
||||
AuthenticationParameters authenticationParameters = new AuthenticationParameters(serverProperty, authenticator,
|
||||
userVerificationRequired);
|
||||
|
||||
AuthenticationData authenticationData = this.webAuthnManager.validate(authenticationRequest,
|
||||
authenticationParameters);
|
||||
|
||||
long updatedSignCount = authenticationData.getAuthenticatorData().getSignCount();
|
||||
ImmutableCredentialRecord updatedRecord = ImmutableCredentialRecord.fromCredentialRecord(credentialRecord)
|
||||
.lastUsed(Instant.now())
|
||||
.signatureCount(updatedSignCount)
|
||||
.build();
|
||||
this.userCredentials.save(updatedRecord);
|
||||
|
||||
return this.userEntities.findById(credentialRecord.getUserEntityUserId());
|
||||
}
|
||||
|
||||
}
|
||||
-218
@@ -1,218 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2025 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.
|
||||
*/
|
||||
|
||||
package org.springframework.security.web.webauthn.registration;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.time.Instant;
|
||||
import java.time.ZoneId;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import jakarta.servlet.FilterChain;
|
||||
import jakarta.servlet.ServletException;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.security.web.csrf.CsrfToken;
|
||||
import org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher;
|
||||
import org.springframework.security.web.util.matcher.RequestMatcher;
|
||||
import org.springframework.security.web.webauthn.api.CredentialRecord;
|
||||
import org.springframework.security.web.webauthn.api.PublicKeyCredentialUserEntity;
|
||||
import org.springframework.security.web.webauthn.management.PublicKeyCredentialUserEntityRepository;
|
||||
import org.springframework.security.web.webauthn.management.UserCredentialRepository;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.web.filter.OncePerRequestFilter;
|
||||
|
||||
/**
|
||||
* A {@link jakarta.servlet.Filter} that renders a default WebAuthn registration page.
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @author Daniel Garnier-Moiroux
|
||||
* @since 6.4
|
||||
*/
|
||||
public class DefaultWebAuthnRegistrationPageGeneratingFilter extends OncePerRequestFilter {
|
||||
|
||||
private RequestMatcher matcher = PathPatternRequestMatcher.withDefaults()
|
||||
.matcher(HttpMethod.GET, "/webauthn/register");
|
||||
|
||||
private final PublicKeyCredentialUserEntityRepository userEntities;
|
||||
|
||||
private final UserCredentialRepository userCredentials;
|
||||
|
||||
/**
|
||||
* Creates a new instance.
|
||||
* @param userEntities the {@link PublicKeyCredentialUserEntity}
|
||||
* @param userCredentials
|
||||
*/
|
||||
public DefaultWebAuthnRegistrationPageGeneratingFilter(PublicKeyCredentialUserEntityRepository userEntities,
|
||||
UserCredentialRepository userCredentials) {
|
||||
Assert.notNull(userEntities, "userEntities cannot be null");
|
||||
Assert.notNull(userCredentials, "userCredentials cannot be null");
|
||||
this.userEntities = userEntities;
|
||||
this.userCredentials = userCredentials;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
|
||||
throws ServletException, IOException {
|
||||
if (!this.matcher.matches(request)) {
|
||||
filterChain.doFilter(request, response);
|
||||
return;
|
||||
}
|
||||
|
||||
CsrfToken csrfToken = (CsrfToken) request.getAttribute(CsrfToken.class.getName());
|
||||
response.setContentType(MediaType.TEXT_HTML_VALUE);
|
||||
response.setStatus(HttpServletResponse.SC_OK);
|
||||
String processedTemplate = HtmlTemplates.fromTemplate(HTML_TEMPLATE)
|
||||
.withValue("contextPath", request.getContextPath())
|
||||
.withRawHtml("csrfHeaders", renderCsrfHeader(csrfToken))
|
||||
.withRawHtml("passkeys", passkeyRows(request.getRemoteUser(), request.getContextPath(), csrfToken))
|
||||
.render();
|
||||
|
||||
response.getWriter().write(processedTemplate);
|
||||
}
|
||||
|
||||
private String passkeyRows(String username, String contextPath, CsrfToken csrfToken) {
|
||||
PublicKeyCredentialUserEntity userEntity = this.userEntities.findByUsername(username);
|
||||
List<CredentialRecord> credentials = (userEntity != null)
|
||||
? this.userCredentials.findByUserId(userEntity.getId()) : Collections.emptyList();
|
||||
if (credentials.isEmpty()) {
|
||||
return """
|
||||
<tr><td colspan="5">No Passkeys</td></tr>
|
||||
""";
|
||||
}
|
||||
return credentials.stream()
|
||||
.map((credentialRecord) -> renderPasskeyRow(credentialRecord, contextPath, csrfToken))
|
||||
.collect(Collectors.joining("\n"));
|
||||
}
|
||||
|
||||
private String renderPasskeyRow(CredentialRecord credential, String contextPath, CsrfToken csrfToken) {
|
||||
return HtmlTemplates.fromTemplate(PASSKEY_ROW_TEMPLATE)
|
||||
.withValue("label", credential.getLabel())
|
||||
.withValue("created", formatInstant(credential.getCreated()))
|
||||
.withValue("lastUsed", formatInstant(credential.getLastUsed()))
|
||||
.withValue("signatureCount", credential.getSignatureCount())
|
||||
.withValue("credentialId", credential.getCredentialId().toBase64UrlString())
|
||||
.withValue("csrfParameterName", csrfToken.getParameterName())
|
||||
.withValue("csrfToken", csrfToken.getToken())
|
||||
.withValue("contextPath", contextPath)
|
||||
.render();
|
||||
}
|
||||
|
||||
private static String formatInstant(Instant created) {
|
||||
return ZonedDateTime.ofInstant(created, ZoneId.of("UTC"))
|
||||
.truncatedTo(ChronoUnit.SECONDS)
|
||||
.format(DateTimeFormatter.ISO_INSTANT);
|
||||
}
|
||||
|
||||
private String renderCsrfHeader(CsrfToken csrfToken) {
|
||||
return HtmlTemplates.fromTemplate(CSRF_HEADERS)
|
||||
.withValue("headerName", csrfToken.getHeaderName())
|
||||
.withValue("headerValue", csrfToken.getToken())
|
||||
.render();
|
||||
}
|
||||
|
||||
private static final String HTML_TEMPLATE = """
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||
<meta name="description" content="">
|
||||
<meta name="author" content="">
|
||||
<title>WebAuthn Registration</title>
|
||||
<link href="{{contextPath}}/default-ui.css" rel="stylesheet" />
|
||||
<script type="text/javascript" src="{{contextPath}}/login/webauthn.js"></script>
|
||||
<script type="text/javascript">
|
||||
<!--
|
||||
const ui = {
|
||||
getRegisterButton: function() {
|
||||
return document.getElementById('register')
|
||||
},
|
||||
getSuccess: function() {
|
||||
return document.getElementById('success')
|
||||
},
|
||||
getError: function() {
|
||||
return document.getElementById('error')
|
||||
},
|
||||
getLabelInput: function() {
|
||||
return document.getElementById('label')
|
||||
},
|
||||
getDeleteForms: function() {
|
||||
return Array.from(document.getElementsByClassName("delete-form"))
|
||||
},
|
||||
}
|
||||
document.addEventListener("DOMContentLoaded",() => setupRegistration({{csrfHeaders}}, "{{contextPath}}", ui));
|
||||
//-->
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="content">
|
||||
<h2 class="center">WebAuthn Registration</h2>
|
||||
<form class="default-form" method="post" action="#" onclick="return false">
|
||||
<div id="success" class="alert alert-success" role="alert">Success!</div>
|
||||
<div id="error" class="alert alert-danger" role="alert"></div>
|
||||
<p>
|
||||
<label for="label" class="screenreader">Passkey Label</label>
|
||||
<input type="text" id="label" name="label" placeholder="Passkey Label" required autofocus>
|
||||
</p>
|
||||
<button id="register" class="primary" type="submit">Register</button>
|
||||
</form>
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
<tr class="table-header">
|
||||
<th>Label</th>
|
||||
<th>Created</th>
|
||||
<th>Last Used</th>
|
||||
<th>Signature Count</th>
|
||||
<th>Delete</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{{passkeys}}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
""";
|
||||
|
||||
private static final String PASSKEY_ROW_TEMPLATE = """
|
||||
<tr class="v-middle">
|
||||
<td>{{label}}</td>
|
||||
<td>{{created}}</td>
|
||||
<td>{{lastUsed}}</td>
|
||||
<td class="center">{{signatureCount}}</td>
|
||||
<td>
|
||||
<form class="delete-form no-margin" method="post" action="{{contextPath}}/webauthn/register/{{credentialId}}">
|
||||
<input type="hidden" name="method" value="delete">
|
||||
<input type="hidden" name="{{csrfParameterName}}" value="{{csrfToken}}">
|
||||
<button class="primary small" type="submit">Delete</button>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
""";
|
||||
|
||||
private static final String CSRF_HEADERS = """
|
||||
{"{{headerName}}" : "{{headerValue}}"}""";
|
||||
|
||||
}
|
||||
-110
@@ -1,110 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2024 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.
|
||||
*/
|
||||
|
||||
package org.springframework.security.web.webauthn.registration;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.util.HtmlUtils;
|
||||
|
||||
/**
|
||||
* Render HTML templates using string substitution. Intended for internal use. Variables
|
||||
* can be templated using double curly-braces: {@code {{name}}}.
|
||||
*
|
||||
* @author Daniel Garnier-Moiroux
|
||||
* @since 6.4
|
||||
* @see org.springframework.security.web.authentication.ui.HtmlTemplates
|
||||
*/
|
||||
final class HtmlTemplates {
|
||||
|
||||
private HtmlTemplates() {
|
||||
}
|
||||
|
||||
static HtmlTemplates.Builder fromTemplate(String template) {
|
||||
return new HtmlTemplates.Builder(template);
|
||||
}
|
||||
|
||||
static final class Builder {
|
||||
|
||||
private final String template;
|
||||
|
||||
private final Map<String, String> values = new HashMap<>();
|
||||
|
||||
private Builder(String template) {
|
||||
this.template = template;
|
||||
}
|
||||
|
||||
/**
|
||||
* HTML-escape, and inject value {@code value} in every {@code {{key}}}
|
||||
* placeholder.
|
||||
* @param key the placeholder name
|
||||
* @param value the value to inject
|
||||
* @return this instance for further templating
|
||||
*/
|
||||
HtmlTemplates.Builder withValue(String key, Object value) {
|
||||
Assert.notNull(value, "value cannot be null");
|
||||
this.values.put(key, HtmlUtils.htmlEscape(value.toString()));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inject value {@code value} in every {@code {{key}}} placeholder without
|
||||
* HTML-escaping. Useful for injecting "sub-templates".
|
||||
* @param key the placeholder name
|
||||
* @param value the value to inject
|
||||
* @return this instance for further templating
|
||||
*/
|
||||
HtmlTemplates.Builder withRawHtml(String key, String value) {
|
||||
if (!value.isEmpty() && value.charAt(value.length() - 1) == '\n') {
|
||||
value = value.substring(0, value.length() - 1);
|
||||
}
|
||||
this.values.put(key, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the template. All placeholders MUST have a corresponding value. If a
|
||||
* placeholder does not have a corresponding value, throws
|
||||
* {@link IllegalStateException}.
|
||||
* @return the rendered template
|
||||
*/
|
||||
String render() {
|
||||
String template = this.template;
|
||||
for (String key : this.values.keySet()) {
|
||||
String pattern = Pattern.quote("{{" + key + "}}");
|
||||
template = template.replaceAll(pattern, this.values.get(key));
|
||||
}
|
||||
|
||||
String unusedPlaceholders = Pattern.compile("\\{\\{([a-zA-Z0-9]+)}}")
|
||||
.matcher(template)
|
||||
.results()
|
||||
.map((result) -> result.group(1))
|
||||
.collect(Collectors.joining(", "));
|
||||
if (StringUtils.hasLength(unusedPlaceholders)) {
|
||||
throw new IllegalStateException("Unused placeholders in template: [%s]".formatted(unusedPlaceholders));
|
||||
}
|
||||
|
||||
return template;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
-52
@@ -1,52 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2024 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.
|
||||
*/
|
||||
|
||||
package org.springframework.security.web.webauthn.registration;
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import jakarta.servlet.http.HttpSession;
|
||||
|
||||
import org.springframework.security.web.webauthn.api.PublicKeyCredentialCreationOptions;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
public class HttpSessionPublicKeyCredentialCreationOptionsRepository
|
||||
implements PublicKeyCredentialCreationOptionsRepository {
|
||||
|
||||
static final String DEFAULT_ATTR_NAME = PublicKeyCredentialCreationOptions.class.getName().concat("ATTR_NAME");
|
||||
|
||||
private String attrName = DEFAULT_ATTR_NAME;
|
||||
|
||||
@Override
|
||||
public void save(HttpServletRequest request, HttpServletResponse response,
|
||||
PublicKeyCredentialCreationOptions options) {
|
||||
request.getSession().setAttribute(this.attrName, options);
|
||||
}
|
||||
|
||||
public PublicKeyCredentialCreationOptions load(HttpServletRequest request) {
|
||||
HttpSession session = request.getSession(false);
|
||||
if (session == null) {
|
||||
return null;
|
||||
}
|
||||
return (PublicKeyCredentialCreationOptions) session.getAttribute(this.attrName);
|
||||
}
|
||||
|
||||
public void setAttrName(String attrName) {
|
||||
Assert.notNull(attrName, "attrName cannot be null");
|
||||
this.attrName = attrName;
|
||||
}
|
||||
|
||||
}
|
||||
-142
@@ -1,142 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2025 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.
|
||||
*/
|
||||
|
||||
package org.springframework.security.web.webauthn.registration;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import jakarta.servlet.FilterChain;
|
||||
import jakarta.servlet.ServletException;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.converter.HttpMessageConverter;
|
||||
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
|
||||
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
|
||||
import org.springframework.http.server.ServletServerHttpResponse;
|
||||
import org.springframework.security.authorization.AuthenticatedAuthorizationManager;
|
||||
import org.springframework.security.authorization.AuthorizationManager;
|
||||
import org.springframework.security.authorization.AuthorizationResult;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.context.SecurityContext;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.security.core.context.SecurityContextHolderStrategy;
|
||||
import org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher;
|
||||
import org.springframework.security.web.util.matcher.RequestMatcher;
|
||||
import org.springframework.security.web.webauthn.api.PublicKeyCredentialCreationOptions;
|
||||
import org.springframework.security.web.webauthn.jackson.WebauthnJackson2Module;
|
||||
import org.springframework.security.web.webauthn.management.ImmutablePublicKeyCredentialCreationOptionsRequest;
|
||||
import org.springframework.security.web.webauthn.management.WebAuthnRelyingPartyOperations;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.web.filter.OncePerRequestFilter;
|
||||
|
||||
/**
|
||||
* A {@link jakarta.servlet.Filter} that renders the
|
||||
* {@link PublicKeyCredentialCreationOptions} for <a href=
|
||||
* "https://w3c.github.io/webappsec-credential-management/#dom-credentialscontainer-create">creating</a>
|
||||
* a new credential.
|
||||
*
|
||||
* @author DingHao
|
||||
*/
|
||||
public class PublicKeyCredentialCreationOptionsFilter extends OncePerRequestFilter {
|
||||
|
||||
private PublicKeyCredentialCreationOptionsRepository repository = new HttpSessionPublicKeyCredentialCreationOptionsRepository();
|
||||
|
||||
private SecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder
|
||||
.getContextHolderStrategy();
|
||||
|
||||
private RequestMatcher matcher = PathPatternRequestMatcher.withDefaults()
|
||||
.matcher(HttpMethod.POST, "/webauthn/register/options");
|
||||
|
||||
private AuthorizationManager<HttpServletRequest> authorization = AuthenticatedAuthorizationManager.authenticated();
|
||||
|
||||
private final WebAuthnRelyingPartyOperations rpOperations;
|
||||
|
||||
private HttpMessageConverter<Object> converter = new MappingJackson2HttpMessageConverter(
|
||||
Jackson2ObjectMapperBuilder.json().modules(new WebauthnJackson2Module()).build());
|
||||
|
||||
/**
|
||||
* Creates a new instance.
|
||||
* @param rpOperations the {@link WebAuthnRelyingPartyOperations} to use. Cannot be
|
||||
* null.
|
||||
*/
|
||||
public PublicKeyCredentialCreationOptionsFilter(WebAuthnRelyingPartyOperations rpOperations) {
|
||||
Assert.notNull(rpOperations, "rpOperations cannot be null");
|
||||
this.rpOperations = rpOperations;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link RequestMatcher} used to trigger this filter.
|
||||
* <p>
|
||||
* By default, the {@link RequestMatcher} is {@code POST /webauthn/register/options}.
|
||||
* @param requestMatcher the {@link RequestMatcher} to use
|
||||
* @since 6.5
|
||||
*/
|
||||
public void setRequestMatcher(RequestMatcher requestMatcher) {
|
||||
Assert.notNull(requestMatcher, "requestMatcher cannot be null");
|
||||
this.matcher = requestMatcher;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
|
||||
throws ServletException, IOException {
|
||||
if (!this.matcher.matches(request)) {
|
||||
filterChain.doFilter(request, response);
|
||||
return;
|
||||
}
|
||||
|
||||
Supplier<SecurityContext> context = this.securityContextHolderStrategy.getDeferredContext();
|
||||
Supplier<Authentication> authentication = () -> context.get().getAuthentication();
|
||||
AuthorizationResult result = this.authorization.authorize(authentication, request);
|
||||
if (result == null || !result.isGranted()) {
|
||||
response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
|
||||
return;
|
||||
}
|
||||
PublicKeyCredentialCreationOptions options = this.rpOperations.createPublicKeyCredentialCreationOptions(
|
||||
new ImmutablePublicKeyCredentialCreationOptionsRequest(authentication.get()));
|
||||
this.repository.save(request, response, options);
|
||||
response.setStatus(HttpServletResponse.SC_OK);
|
||||
response.setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE);
|
||||
this.converter.write(options, MediaType.APPLICATION_JSON, new ServletServerHttpResponse(response));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link PublicKeyCredentialCreationOptionsRepository} to use. The default
|
||||
* is {@link HttpSessionPublicKeyCredentialCreationOptionsRepository}.
|
||||
* @param creationOptionsRepository the
|
||||
* {@link PublicKeyCredentialCreationOptionsRepository} to use. Cannot be null.
|
||||
*/
|
||||
public void setCreationOptionsRepository(PublicKeyCredentialCreationOptionsRepository creationOptionsRepository) {
|
||||
Assert.notNull(creationOptionsRepository, "creationOptionsRepository cannot be null");
|
||||
this.repository = creationOptionsRepository;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the {@link HttpMessageConverter} to read the
|
||||
* {@link WebAuthnRegistrationFilter.WebAuthnRegistrationRequest} and write the
|
||||
* response. The default is {@link MappingJackson2HttpMessageConverter}.
|
||||
* @param converter the {@link HttpMessageConverter} to use. Cannot be null.
|
||||
*/
|
||||
public void setConverter(HttpMessageConverter<Object> converter) {
|
||||
Assert.notNull(converter, "converter cannot be null");
|
||||
this.converter = converter;
|
||||
}
|
||||
|
||||
}
|
||||
-52
@@ -1,52 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2024 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.
|
||||
*/
|
||||
|
||||
package org.springframework.security.web.webauthn.registration;
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.security.web.webauthn.api.PublicKeyCredentialCreationOptions;
|
||||
|
||||
/**
|
||||
* Saves {@link PublicKeyCredentialCreationOptions} between a request to generate an
|
||||
* assertion and the validation of the assertion.
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 6.4
|
||||
*/
|
||||
public interface PublicKeyCredentialCreationOptionsRepository {
|
||||
|
||||
/**
|
||||
* Saves the provided {@link PublicKeyCredentialCreationOptions} or clears an existing
|
||||
* {@link PublicKeyCredentialCreationOptions} if {@code options} is null.
|
||||
* @param request the {@link HttpServletRequest}
|
||||
* @param response the {@link HttpServletResponse}
|
||||
* @param options the {@link PublicKeyCredentialCreationOptions} to save or null if an
|
||||
* existing {@link PublicKeyCredentialCreationOptions} should be removed.
|
||||
*/
|
||||
void save(HttpServletRequest request, HttpServletResponse response, PublicKeyCredentialCreationOptions options);
|
||||
|
||||
/**
|
||||
* Gets a saved {@link PublicKeyCredentialCreationOptions} if it exists, otherwise
|
||||
* null.
|
||||
* @param request the {@link HttpServletRequest}
|
||||
* @return the {@link PublicKeyCredentialCreationOptions} that was saved, otherwise
|
||||
* null.
|
||||
*/
|
||||
PublicKeyCredentialCreationOptions load(HttpServletRequest request);
|
||||
|
||||
}
|
||||
-238
@@ -1,238 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2025 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.
|
||||
*/
|
||||
|
||||
package org.springframework.security.web.webauthn.registration;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import com.fasterxml.jackson.databind.json.JsonMapper;
|
||||
import jakarta.servlet.FilterChain;
|
||||
import jakarta.servlet.ServletException;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.http.HttpInputMessage;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.converter.HttpMessageConverter;
|
||||
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
|
||||
import org.springframework.http.server.ServletServerHttpRequest;
|
||||
import org.springframework.http.server.ServletServerHttpResponse;
|
||||
import org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher;
|
||||
import org.springframework.security.web.util.matcher.RequestMatcher;
|
||||
import org.springframework.security.web.webauthn.api.Bytes;
|
||||
import org.springframework.security.web.webauthn.api.CredentialRecord;
|
||||
import org.springframework.security.web.webauthn.api.PublicKeyCredentialCreationOptions;
|
||||
import org.springframework.security.web.webauthn.jackson.WebauthnJackson2Module;
|
||||
import org.springframework.security.web.webauthn.management.ImmutableRelyingPartyRegistrationRequest;
|
||||
import org.springframework.security.web.webauthn.management.RelyingPartyPublicKey;
|
||||
import org.springframework.security.web.webauthn.management.UserCredentialRepository;
|
||||
import org.springframework.security.web.webauthn.management.WebAuthnRelyingPartyOperations;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.web.filter.OncePerRequestFilter;
|
||||
|
||||
/**
|
||||
* Authenticates {@code PublicKeyCredential<AuthenticatorAssertionResponse>} that is
|
||||
* parsed from the body of the {@link HttpServletRequest} using the
|
||||
* {@link #setConverter(HttpMessageConverter)}. An example request is provided below:
|
||||
*
|
||||
* <pre>
|
||||
* {
|
||||
* "publicKey": {
|
||||
* "credential": {
|
||||
* "id": "dYF7EGnRFFIXkpXi9XU2wg",
|
||||
* "rawId": "dYF7EGnRFFIXkpXi9XU2wg",
|
||||
* "response": {
|
||||
* "attestationObject": "o2NmbXRkbm9uZWdhdHRTdG10oGhhdXRoRGF0YViUy9GqwTRaMpzVDbXq1dyEAXVOxrou08k22ggRC45MKNhdAAAAALraVWanqkAfvZZFYZpVEg0AEHWBexBp0RRSF5KV4vV1NsKlAQIDJiABIVggQjmrekPGzyqtoKK9HPUH-8Z2FLpoqkklFpFPQVICQ3IiWCD6I9Jvmor685fOZOyGXqUd87tXfvJk8rxj9OhuZvUALA",
|
||||
* "clientDataJSON": "eyJ0eXBlIjoid2ViYXV0aG4uY3JlYXRlIiwiY2hhbGxlbmdlIjoiSl9RTi10SFJYRWVKYjlNcUNrWmFPLUdOVmlibXpGVGVWMk43Z0ptQUdrQSIsIm9yaWdpbiI6Imh0dHBzOi8vZXhhbXBsZS5sb2NhbGhvc3Q6ODQ0MyIsImNyb3NzT3JpZ2luIjpmYWxzZX0",
|
||||
* "transports": [
|
||||
* "internal",
|
||||
* "hybrid"
|
||||
* ]
|
||||
* },
|
||||
* "type": "public-key",
|
||||
* "clientExtensionResults": {},
|
||||
* "authenticatorAttachment": "platform"
|
||||
* },
|
||||
* "label": "1password"
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 6.4
|
||||
*/
|
||||
public class WebAuthnRegistrationFilter extends OncePerRequestFilter {
|
||||
|
||||
static final String DEFAULT_REGISTER_CREDENTIAL_URL = "/webauthn/register";
|
||||
|
||||
private static final Log logger = LogFactory.getLog(WebAuthnRegistrationFilter.class);
|
||||
|
||||
private final WebAuthnRelyingPartyOperations rpOptions;
|
||||
|
||||
private final UserCredentialRepository userCredentials;
|
||||
|
||||
private HttpMessageConverter<Object> converter = new MappingJackson2HttpMessageConverter(
|
||||
JsonMapper.builder().addModule(new WebauthnJackson2Module()).build());
|
||||
|
||||
private PublicKeyCredentialCreationOptionsRepository creationOptionsRepository = new HttpSessionPublicKeyCredentialCreationOptionsRepository();
|
||||
|
||||
private RequestMatcher registerCredentialMatcher = PathPatternRequestMatcher.withDefaults()
|
||||
.matcher(HttpMethod.POST, DEFAULT_REGISTER_CREDENTIAL_URL);
|
||||
|
||||
private RequestMatcher removeCredentialMatcher = PathPatternRequestMatcher.withDefaults()
|
||||
.matcher(HttpMethod.DELETE, "/webauthn/register/{id}");
|
||||
|
||||
public WebAuthnRegistrationFilter(UserCredentialRepository userCredentials,
|
||||
WebAuthnRelyingPartyOperations rpOptions) {
|
||||
Assert.notNull(userCredentials, "userCredentials must not be null");
|
||||
Assert.notNull(rpOptions, "rpOptions must not be null");
|
||||
this.userCredentials = userCredentials;
|
||||
this.rpOptions = rpOptions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link RequestMatcher} to trigger this filter's the credential
|
||||
* registration operation .
|
||||
* <p/>
|
||||
* By default, the {@link RequestMatcher} is {@code POST /webauthn/register}.
|
||||
* @param registerCredentialMatcher the {@link RequestMatcher} to use
|
||||
* @since 6.5
|
||||
*/
|
||||
public void setRegisterCredentialMatcher(RequestMatcher registerCredentialMatcher) {
|
||||
Assert.notNull(registerCredentialMatcher, "registerCredentialMatcher cannot be null");
|
||||
this.registerCredentialMatcher = registerCredentialMatcher;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link RequestMatcher} to trigger this filter's the credential removal
|
||||
* operation .
|
||||
* <p/>
|
||||
* By default, the {@link RequestMatcher} is {@code DELETE /webauthn/register/{id}}.
|
||||
* @param removeCredentialMatcher the {@link RequestMatcher} to use
|
||||
* @since 6.5
|
||||
*/
|
||||
public void setRemoveCredentialMatcher(RequestMatcher removeCredentialMatcher) {
|
||||
Assert.notNull(removeCredentialMatcher, "removeCredentialMatcher cannot be null");
|
||||
this.removeCredentialMatcher = removeCredentialMatcher;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
|
||||
throws ServletException, IOException {
|
||||
if (this.registerCredentialMatcher.matches(request)) {
|
||||
registerCredential(request, response);
|
||||
return;
|
||||
}
|
||||
RequestMatcher.MatchResult removeMatchResult = this.removeCredentialMatcher.matcher(request);
|
||||
if (removeMatchResult.isMatch()) {
|
||||
String id = removeMatchResult.getVariables().get("id");
|
||||
removeCredential(request, response, id);
|
||||
return;
|
||||
}
|
||||
filterChain.doFilter(request, response);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the {@link HttpMessageConverter} to read the
|
||||
* {@link WebAuthnRegistrationRequest} and write the response. The default is
|
||||
* {@link MappingJackson2HttpMessageConverter}.
|
||||
* @param converter the {@link HttpMessageConverter} to use. Cannot be null.
|
||||
*/
|
||||
public void setConverter(HttpMessageConverter<Object> converter) {
|
||||
Assert.notNull(converter, "converter cannot be null");
|
||||
this.converter = converter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link PublicKeyCredentialCreationOptionsRepository} to use. The default
|
||||
* is {@link HttpSessionPublicKeyCredentialCreationOptionsRepository}.
|
||||
* @param creationOptionsRepository the
|
||||
* {@link PublicKeyCredentialCreationOptionsRepository} to use. Cannot be null.
|
||||
*/
|
||||
public void setCreationOptionsRepository(PublicKeyCredentialCreationOptionsRepository creationOptionsRepository) {
|
||||
Assert.notNull(creationOptionsRepository, "creationOptionsRepository cannot be null");
|
||||
this.creationOptionsRepository = creationOptionsRepository;
|
||||
}
|
||||
|
||||
private void registerCredential(HttpServletRequest request, HttpServletResponse response) throws IOException {
|
||||
WebAuthnRegistrationRequest registrationRequest = readRegistrationRequest(request);
|
||||
if (registrationRequest == null) {
|
||||
response.setStatus(HttpStatus.BAD_REQUEST.value());
|
||||
return;
|
||||
}
|
||||
PublicKeyCredentialCreationOptions options = this.creationOptionsRepository.load(request);
|
||||
if (options == null) {
|
||||
response.setStatus(HttpStatus.BAD_REQUEST.value());
|
||||
return;
|
||||
}
|
||||
this.creationOptionsRepository.save(request, response, null);
|
||||
CredentialRecord credentialRecord = this.rpOptions.registerCredential(
|
||||
new ImmutableRelyingPartyRegistrationRequest(options, registrationRequest.getPublicKey()));
|
||||
SuccessfulUserRegistrationResponse registrationResponse = new SuccessfulUserRegistrationResponse(
|
||||
credentialRecord);
|
||||
ServletServerHttpResponse outputMessage = new ServletServerHttpResponse(response);
|
||||
this.converter.write(registrationResponse, MediaType.APPLICATION_JSON, outputMessage);
|
||||
}
|
||||
|
||||
private WebAuthnRegistrationRequest readRegistrationRequest(HttpServletRequest request) {
|
||||
HttpInputMessage inputMessage = new ServletServerHttpRequest(request);
|
||||
try {
|
||||
return (WebAuthnRegistrationRequest) this.converter.read(WebAuthnRegistrationRequest.class, inputMessage);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
logger.debug("Unable to parse WebAuthnRegistrationRequest", ex);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private void removeCredential(HttpServletRequest request, HttpServletResponse response, String id)
|
||||
throws IOException {
|
||||
this.userCredentials.delete(Bytes.fromBase64(id));
|
||||
response.setStatus(HttpStatus.NO_CONTENT.value());
|
||||
}
|
||||
|
||||
static class WebAuthnRegistrationRequest {
|
||||
|
||||
private RelyingPartyPublicKey publicKey;
|
||||
|
||||
RelyingPartyPublicKey getPublicKey() {
|
||||
return this.publicKey;
|
||||
}
|
||||
|
||||
void setPublicKey(RelyingPartyPublicKey publicKey) {
|
||||
this.publicKey = publicKey;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class SuccessfulUserRegistrationResponse {
|
||||
|
||||
private final CredentialRecord credentialRecord;
|
||||
|
||||
SuccessfulUserRegistrationResponse(CredentialRecord credentialRecord) {
|
||||
this.credentialRecord = credentialRecord;
|
||||
}
|
||||
|
||||
public boolean isSuccess() {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user