From bb0f86d9fcee72694636ec2507fbf61262d00b3e Mon Sep 17 00:00:00 2001 From: Filipe Silva Date: Sat, 5 Aug 2023 01:15:21 +0100 Subject: [PATCH] WL#15197, Support WebauthN in fido authentication plugin. Change-Id: Ia83d2c1f7619e7c0febbbc426a7cfef4d0feae43 --- CHANGES | 2 + .../WebAuthnAuthenticationCallback.java | 186 ++++++++++++++ .../mysql/cj/conf/PropertyDefinitions.java | 9 +- .../java/com/mysql/cj/conf/PropertyKey.java | 1 + .../a/NativeAuthenticationProvider.java | 2 + .../AuthenticationFidoClient.java | 2 +- .../AuthenticationWebAuthnClient.java | 228 ++++++++++++++++++ .../CachingSha2PasswordPlugin.java | 2 +- .../cj/LocalizedErrorMessages.properties | 10 +- 9 files changed, 436 insertions(+), 6 deletions(-) create mode 100644 src/main/core-api/java/com/mysql/cj/callback/WebAuthnAuthenticationCallback.java create mode 100644 src/main/protocol-impl/java/com/mysql/cj/protocol/a/authentication/AuthenticationWebAuthnClient.java diff --git a/CHANGES b/CHANGES index f916bdd2d..64fe4257f 100644 --- a/CHANGES +++ b/CHANGES @@ -3,6 +3,8 @@ Version 8.2.0 + - WL#15197, Support WebauthN in fido authentication plugin. + - Fix for Bug#107215 (Bug#34139593), ClassCastException: java.time.LocalDateTime cannot be cast to java.sql.Timestamp. - WL#15747, Remove autoDeserialize feature. diff --git a/src/main/core-api/java/com/mysql/cj/callback/WebAuthnAuthenticationCallback.java b/src/main/core-api/java/com/mysql/cj/callback/WebAuthnAuthenticationCallback.java new file mode 100644 index 000000000..006165d30 --- /dev/null +++ b/src/main/core-api/java/com/mysql/cj/callback/WebAuthnAuthenticationCallback.java @@ -0,0 +1,186 @@ +/* + * Copyright (c) 2023, Oracle and/or its affiliates. + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, version 2.0, as published by the + * Free Software Foundation. + * + * This program is also distributed with certain software (including but not + * limited to OpenSSL) that is licensed under separate terms, as designated in a + * particular file or component or in included license documentation. The + * authors of MySQL hereby grant you an additional permission to link the + * program and your derivative works with the separately licensed software that + * they have included with MySQL. + * + * Without limiting anything contained in the foregoing, this file, which is + * part of MySQL Connector/J, is also subject to the Universal FOSS Exception, + * version 1.0, a copy of which can be found at + * http://oss.oracle.com/licenses/universal-foss-exception. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License, version 2.0, + * for more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package com.mysql.cj.callback; + +import java.util.ArrayList; +import java.util.List; + +/** + * The callback object used by the authentication plugin AuthenticationWebAuthnClient to exchange authenticator data between the driver and the client + * application. + * + * Applications must implement a MysqlCallbackHandler to manage the interaction with authenticator devices and provide the data required to proceed with + * FIDO2/WebAuthn authentication. Such handler receives an instance of this class, which must then be used to obtain the data to send to the device and to + * submit the produced authenticator data and signature into the driver. + */ +public class WebAuthnAuthenticationCallback implements MysqlCallback { + + // FIDO inputs. + private byte[] clientDataHash; + private String relyingPartyId; + private byte[] credentialId; + + // FIDO outputs. + private boolean supportsCredentialManagement; + private List authenticatorDataEntries; + private List signatureEntries; + + /** + * Instances of this object are used to exchange FIDO data between the client application and the driver and are responsible for managing all the + * interactions with the FIDO authenticator devices. + * + * @param clientDataHash + * the client data hash + * @param relyingPartyId + * the relying party id + * @param credentialId + * the credential id + */ + public WebAuthnAuthenticationCallback(byte[] clientDataHash, String relyingPartyId, byte[] credentialId) { + this.clientDataHash = clientDataHash; + this.relyingPartyId = relyingPartyId; + this.credentialId = credentialId; + this.authenticatorDataEntries = new ArrayList<>(); + this.signatureEntries = new ArrayList<>(); + } + + /** + * Returns the FIDO Client Data Hash (an SHA-256 hash computed from the Client Data JSON) for the upcoming authenticator interaction. + * + * @return + * the client data hash + */ + public byte[] getClientDataHash() { + return this.clientDataHash; + } + + /** + * Returns the FIDO Relying Party Id for the upcoming authenticator interaction. + * + * @return + * the relying party id + */ + public String getRelyingPartyId() { + return this.relyingPartyId; + } + + /** + * Returns the FIDO Credential Id for the upcoming authenticator interaction. + * + * @return + * the credential id + */ + public byte[] getCredentialId() { + return this.credentialId; + } + + /** + * Sets whether this FIDO Authenticator device supports Credential Management. + * + * @param supportsCredMan + * is credential management supported? + */ + public void setSupportsCredentialManagement(boolean supportsCredMan) { + this.supportsCredentialManagement = supportsCredMan; + } + + /** + * Returns whether this FIDO Authenticator device supports Credential Management. + * + * @return + * is credential management supported? + */ + public boolean getSupportsCredentialManagement() { + return this.supportsCredentialManagement; + } + + /** + * Adds a FIDO Authenticator Data produced by the authenticator interaction. + * + * @param authenticatorData + * the authenticator data + */ + public void addAuthenticatorData(byte[] authenticatorData) { + this.authenticatorDataEntries.add(authenticatorData); + } + + /** + * Returns one of FIDO Authenticator Data produced by the authenticator interaction. + * + * @param idx + * the index of the Authenticator Data to return + * + * @return + * the authenticator data + */ + public byte[] getAuthenticatorData(int idx) { + if (idx >= this.authenticatorDataEntries.size()) { + return null; + } + return this.authenticatorDataEntries.get(idx); + } + + /** + * Adds a FIDO Signature produced by the authenticator interaction. + * + * @param signature + * the signature + */ + public void addSignature(byte[] signature) { + this.signatureEntries.add(signature); + } + + /** + * Returns one of the FIDO Signatures produced by the authenticator interaction + * + * @param idx + * the index of the Signature to return + * + * @return + * the signature + */ + public byte[] getSignature(int idx) { + if (idx >= this.signatureEntries.size()) { + return null; + } + return this.signatureEntries.get(idx); + } + + /** + * Returns the number of assertions produced by the authenticator interaction + * + * @return + * the number of assertions + */ + public int getAssertCount() { + return Math.min(this.authenticatorDataEntries.size(), this.signatureEntries.size()); + } + +} diff --git a/src/main/core-api/java/com/mysql/cj/conf/PropertyDefinitions.java b/src/main/core-api/java/com/mysql/cj/conf/PropertyDefinitions.java index 91dc33b4a..324d0150d 100644 --- a/src/main/core-api/java/com/mysql/cj/conf/PropertyDefinitions.java +++ b/src/main/core-api/java/com/mysql/cj/conf/PropertyDefinitions.java @@ -209,13 +209,16 @@ public enum DatabaseTerm { Messages.getString("ConnectionProperties.ldapServerHostname"), "8.0.23", CATEGORY_AUTH, Integer.MIN_VALUE + 9), new StringPropertyDefinition(PropertyKey.ociConfigFile, DEFAULT_VALUE_NULL_STRING, RUNTIME_MODIFIABLE, - Messages.getString("ConnectionProperties.ociConfigFile"), "8.0.27", CATEGORY_AUTH, Integer.MIN_VALUE + 7), + Messages.getString("ConnectionProperties.ociConfigFile"), "8.0.27", CATEGORY_AUTH, Integer.MIN_VALUE + 10), new StringPropertyDefinition(PropertyKey.ociConfigProfile, "DEFAULT", RUNTIME_MODIFIABLE, - Messages.getString("ConnectionProperties.ociConfigProfile"), "8.0.33", CATEGORY_AUTH, Integer.MIN_VALUE + 8), + Messages.getString("ConnectionProperties.ociConfigProfile"), "8.0.33", CATEGORY_AUTH, Integer.MIN_VALUE + 11), new StringPropertyDefinition(PropertyKey.authenticationFidoCallbackHandler, DEFAULT_VALUE_NULL_STRING, RUNTIME_MODIFIABLE, - Messages.getString("ConnectionProperties.authenticationFidoCallbackHandler"), "8.0.29", CATEGORY_AUTH, Integer.MIN_VALUE + 9), + Messages.getString("ConnectionProperties.authenticationFidoCallbackHandler"), "8.0.29", CATEGORY_AUTH, Integer.MIN_VALUE + 12), + + new StringPropertyDefinition(PropertyKey.authenticationWebAuthnCallbackHandler, DEFAULT_VALUE_NULL_STRING, RUNTIME_MODIFIABLE, + Messages.getString("ConnectionProperties.authenticationWebAuthnCallbackHandler"), "8.2.0", CATEGORY_AUTH, Integer.MIN_VALUE + 13), // // CATEGORY_CONNECTION diff --git a/src/main/core-api/java/com/mysql/cj/conf/PropertyKey.java b/src/main/core-api/java/com/mysql/cj/conf/PropertyKey.java index 561ff129d..cea7fd5a8 100644 --- a/src/main/core-api/java/com/mysql/cj/conf/PropertyKey.java +++ b/src/main/core-api/java/com/mysql/cj/conf/PropertyKey.java @@ -72,6 +72,7 @@ public enum PropertyKey { alwaysSendSetIsolation("alwaysSendSetIsolation", true), // authenticationFidoCallbackHandler("authenticationFidoCallbackHandler", true), // authenticationPlugins("authenticationPlugins", true), // + authenticationWebAuthnCallbackHandler("authenticationWebAuthnCallbackHandler", true), // autoClosePStmtStreams("autoClosePStmtStreams", true), // autoGenerateTestcaseScript("autoGenerateTestcaseScript", true), // autoReconnect("autoReconnect", true), // diff --git a/src/main/protocol-impl/java/com/mysql/cj/protocol/a/NativeAuthenticationProvider.java b/src/main/protocol-impl/java/com/mysql/cj/protocol/a/NativeAuthenticationProvider.java index 71ac1f21d..f62c66545 100644 --- a/src/main/protocol-impl/java/com/mysql/cj/protocol/a/NativeAuthenticationProvider.java +++ b/src/main/protocol-impl/java/com/mysql/cj/protocol/a/NativeAuthenticationProvider.java @@ -59,6 +59,7 @@ import com.mysql.cj.protocol.a.authentication.AuthenticationKerberosClient; import com.mysql.cj.protocol.a.authentication.AuthenticationLdapSaslClientPlugin; import com.mysql.cj.protocol.a.authentication.AuthenticationOciClient; +import com.mysql.cj.protocol.a.authentication.AuthenticationWebAuthnClient; import com.mysql.cj.protocol.a.authentication.CachingSha2PasswordPlugin; import com.mysql.cj.protocol.a.authentication.MysqlClearPasswordPlugin; import com.mysql.cj.protocol.a.authentication.MysqlNativePasswordPlugin; @@ -260,6 +261,7 @@ private void loadAuthenticationPlugins() { pluginsToInit.add(new AuthenticationKerberosClient()); pluginsToInit.add(new AuthenticationOciClient()); pluginsToInit.add(new AuthenticationFidoClient()); + pluginsToInit.add(new AuthenticationWebAuthnClient()); // plugins from authenticationPluginClasses connection parameter String authenticationPluginClasses = this.propertySet.getStringProperty(PropertyKey.authenticationPlugins).getValue(); diff --git a/src/main/protocol-impl/java/com/mysql/cj/protocol/a/authentication/AuthenticationFidoClient.java b/src/main/protocol-impl/java/com/mysql/cj/protocol/a/authentication/AuthenticationFidoClient.java index c1a96f5da..263472258 100644 --- a/src/main/protocol-impl/java/com/mysql/cj/protocol/a/authentication/AuthenticationFidoClient.java +++ b/src/main/protocol-impl/java/com/mysql/cj/protocol/a/authentication/AuthenticationFidoClient.java @@ -137,7 +137,7 @@ public boolean nextAuthenticationStep(NativePacketPayload fromServer, List { + + public static String PLUGIN_NAME = "authentication_webauthn_client"; + private static final String CLIENT_DATA_JSON = "{\"type\":\"webauthn.get\",\"challenge\":\"%s\",\"origin\":\"https://%s\",\"crossOrigin\":false }"; + + private enum AuthStage { + INITIAL_DATA, CREDENTIAL_ID, FINISHED; + } + + private String sourceOfAuthData = PLUGIN_NAME; + private AuthStage stage = AuthStage.INITIAL_DATA; + private byte[] challenge = null; + private String relyingPartyId = null; + private String clientDataJson = null; + private byte[] clientDataHash = null; + private byte[] credentialId = null; + + private MysqlCallbackHandler usernameCallbackHandler = null; + private MysqlCallbackHandler webAuthnAuthenticationCallbackHandler = null; + private WebAuthnAuthenticationCallback webAuthnAuthCallback = null; + + @Override + public void init(Protocol protocol, MysqlCallbackHandler callbackHandler) { + this.usernameCallbackHandler = callbackHandler; + + String webAuthnCallbackHandlerClassName = protocol.getPropertySet().getStringProperty(PropertyKey.authenticationWebAuthnCallbackHandler).getValue(); + if (webAuthnCallbackHandlerClassName == null) { + throw ExceptionFactory.createException(Messages.getString("AuthenticationWebAuthnClientPlugin.MissingCallbackHandler")); + } + + this.webAuthnAuthenticationCallbackHandler = Util.getInstance(MysqlCallbackHandler.class, webAuthnCallbackHandlerClassName, null, null, + protocol.getExceptionInterceptor()); + } + + @Override + public void reset() { + this.stage = AuthStage.INITIAL_DATA; + this.challenge = null; + this.relyingPartyId = null; + this.clientDataJson = null; + this.clientDataHash = null; + this.credentialId = null; + } + + @Override + public void destroy() { + reset(); + this.usernameCallbackHandler = null; + this.webAuthnAuthenticationCallbackHandler = null; + this.webAuthnAuthCallback = null; + } + + @Override + public String getProtocolPluginName() { + return PLUGIN_NAME; + } + + @Override + public boolean requiresConfidentiality() { + return false; + } + + @Override + public boolean isReusable() { + return false; + } + + @Override + public void setAuthenticationParameters(String user, String password) { + if (user == null && this.usernameCallbackHandler != null) { + // Fall back to system login user. + this.usernameCallbackHandler.handle(new UsernameCallback(System.getProperty("user.name"))); + } + } + + @Override + public void setSourceOfAuthData(String sourceOfAuthData) { + this.sourceOfAuthData = sourceOfAuthData; + } + + @Override + public boolean nextAuthenticationStep(NativePacketPayload fromServer, List toServer) { + toServer.clear(); + + if (!this.sourceOfAuthData.equals(PLUGIN_NAME)) { + // Cannot do anything with whatever payload comes from the server, so just skip this iteration and wait for a Protocol::AuthSwitchRequest or a + // Protocol::AuthNextFactor. + return true; + } + + NativePacketPayload packet; + + switch (this.stage) { + case INITIAL_DATA: + if (fromServer.getPayloadLength() == 0) { + // FIDO device registration process was not completed. + throw ExceptionFactory.createException(Messages.getString("AuthenticationWebAuthnClientPlugin.IncompleteRegistration")); + } + + fromServer.readInteger(IntegerDataType.INT1); // Reserved for future extensibility. + this.challenge = fromServer.readBytes(StringSelfDataType.STRING_LENENC); + this.relyingPartyId = fromServer.readString(StringSelfDataType.STRING_LENENC, "UTF-8"); + + // Compute the Client Data Hash. + Encoder b64Encoder = Base64.getUrlEncoder().withoutPadding(); + String b64EncodedChallenge = b64Encoder.encodeToString(this.challenge); + this.clientDataJson = String.format(CLIENT_DATA_JSON, b64EncodedChallenge, this.relyingPartyId); + MessageDigest digest; + try { + digest = MessageDigest.getInstance("SHA-256"); + } catch (NoSuchAlgorithmException e) { + throw ExceptionFactory.createException(Messages.getString("AuthenticationWebAuthnClientPlugin.FaileMessageDigestSha256"), e); + } + this.clientDataHash = digest.digest(this.clientDataJson.getBytes(StandardCharsets.UTF_8)); + + // Request the FIDO Credential Id from the server even though it may not have it, in which case the server returns an empty packet. This request + // could be avoided if the Authenticator device supports Credentials Management. That information could be passed through + // WebAuthnAuthenticationCallback.getSupportsCredentialManagement() and then used to decide whether the FIDO Credential Id request should be + // sent or not, however that would add complexity to the MysqlCallbackHandler implementations. + packet = new NativePacketPayload(new byte[] { 1 }); + toServer.add(packet); + + this.stage = AuthStage.CREDENTIAL_ID; + break; + case CREDENTIAL_ID: + this.credentialId = fromServer.getPayloadLength() > 0 ? fromServer.readBytes(StringSelfDataType.STRING_LENENC) : new byte[0]; + + // All data collected. Get assertions from the authenticator device.. + this.webAuthnAuthCallback = new WebAuthnAuthenticationCallback(this.clientDataHash, this.relyingPartyId, this.credentialId); + this.webAuthnAuthenticationCallbackHandler.handle(this.webAuthnAuthCallback); + + int assertionsCount = this.webAuthnAuthCallback.getAssertCount(); + int authenticatorDataLength = 0; + int signaturesLength = 0; + for (int i = 0; i < assertionsCount; i++) { + authenticatorDataLength += this.webAuthnAuthCallback.getAuthenticatorData(i).length; + signaturesLength += this.webAuthnAuthCallback.getSignature(i).length; + } + + int packetLen = 1; // 1 for status tag + packetLen += 1; // + 1 for number of assertions + packetLen += authenticatorDataLength + signaturesLength + 2 * assertionsCount; // + ((1 + 1) * assertionsCount) for length encoding + packetLen += this.clientDataJson.length() + 1; // + 1 for length encoding + packet = new NativePacketPayload(packetLen); + packet.writeInteger(IntegerDataType.INT1, 2); + packet.writeInteger(IntegerDataType.INT_LENENC, assertionsCount); + for (int i = 0; i < assertionsCount; i++) { + byte[] authenticatorData = this.webAuthnAuthCallback.getAuthenticatorData(i); + if (authenticatorData == null || authenticatorData.length == 0) { + throw ExceptionFactory.createException(Messages.getString("AuthenticationWebAuthnClientPlugin.InvalidAuthenticatorData")); + } + packet.writeBytes(StringSelfDataType.STRING_LENENC, authenticatorData); + byte[] signature = this.webAuthnAuthCallback.getSignature(i); + if (signature == null || signature.length == 0) { + throw ExceptionFactory.createException(Messages.getString("AuthenticationWebAuthnClientPlugin.InvalidSignature")); + } + packet.writeBytes(StringSelfDataType.STRING_LENENC, signature); + } + packet.writeBytes(StringSelfDataType.STRING_LENENC, this.clientDataJson.getBytes(StandardCharsets.UTF_8)); + toServer.add(packet); + + this.stage = AuthStage.FINISHED; + break; + case FINISHED: + throw ExceptionFactory.createException(Messages.getString("AuthenticationWebAuthnClientPlugin.AuthenticationFactorComplete")); + } + + return true; + } + +} diff --git a/src/main/protocol-impl/java/com/mysql/cj/protocol/a/authentication/CachingSha2PasswordPlugin.java b/src/main/protocol-impl/java/com/mysql/cj/protocol/a/authentication/CachingSha2PasswordPlugin.java index 04368f42e..25bc510b0 100644 --- a/src/main/protocol-impl/java/com/mysql/cj/protocol/a/authentication/CachingSha2PasswordPlugin.java +++ b/src/main/protocol-impl/java/com/mysql/cj/protocol/a/authentication/CachingSha2PasswordPlugin.java @@ -50,7 +50,7 @@ public class CachingSha2PasswordPlugin extends Sha256PasswordPlugin { public static String PLUGIN_NAME = "caching_sha2_password"; - public enum AuthStage { + private enum AuthStage { FAST_AUTH_SEND_SCRAMBLE, FAST_AUTH_READ_RESULT, FAST_AUTH_COMPLETE, FULL_AUTH; } diff --git a/src/main/resources/com/mysql/cj/LocalizedErrorMessages.properties b/src/main/resources/com/mysql/cj/LocalizedErrorMessages.properties index 175dd34d0..a3b84e0c2 100644 --- a/src/main/resources/com/mysql/cj/LocalizedErrorMessages.properties +++ b/src/main/resources/com/mysql/cj/LocalizedErrorMessages.properties @@ -67,6 +67,13 @@ AuthenticationProvider.BadDisabledAuthenticationPlugin=Can''t disable the defaul AuthenticationProvider.AuthenticationPluginRequiresSSL=SSL connection required for plugin "{0}". Check if ''sslMode'' is enabled. AuthenticationProvider.UnexpectedAuthenticationApproval=Unexpected authentication approval. Authentication plugin "{0}" did not report "done" state but server has approved the connection. +AuthenticationWebAuthnClientPlugin.MissingCallbackHandler=A callback handler was not specified. Consult the description of the connection property ''authenticationWebAuthnCallbackHandler''. +AuthenticationWebAuthnClientPlugin.IncompleteRegistration=FIDO device registration process not completed. +AuthenticationWebAuthnClientPlugin.FaileMessageDigestSha256=Failed getting an instance of a MessageDigest for the SHA-256 digest algorithm. +AuthenticationWebAuthnClientPlugin.InvalidAuthenticatorData=Missing or invalid FIDO authenticator data. +AuthenticationWebAuthnClientPlugin.InvalidSignature=Missing or invalid FIDO signature. +AuthenticationWebAuthnClientPlugin.AuthenticationFactorComplete=No more data expected. FIDO authentication factor is complete. + Blob.0=indexToWriteAt must be >= 1 Blob.1=IO Error while writing bytes to blob Blob.2="pos" argument can not be < 1. @@ -802,8 +809,9 @@ ConnectionProperties.allowReplicaDownConnections=By default, a replication-aware ConnectionProperties.allowSourceDownConnections=By default, a replication-aware connection will fail to connect when configured source hosts are all unavailable at initial connection. Setting this property to "true" allows to establish the initial connection, by failing over to the replica servers, in read-only state. It won''t prevent subsequent failures when switching back to the source hosts i.e. by setting the replication connection to read/write state. ConnectionProperties.allowUrlInLoadLocal=Should the driver allow URLs in "LOAD DATA LOCAL INFILE ..." statements? ConnectionProperties.alwaysSendSetIsolation=Should the driver always communicate with the database when ''Connection.setTransactionIsolation()'' is called? If set to "false", the driver will only communicate with the database when the requested transaction isolation is different than the whichever is newer, the last value that was set via ''Connection.setTransactionIsolation()'', or the value that was read from the server when the connection was established. Note that "useLocalSessionState=true" will force the same behavior as "alwaysSendSetIsolation=false", regardless of how ''alwaysSendSetIsolation'' is set. -ConnectionProperties.authenticationPlugins=Comma-delimited list of classes that implement the interface ''com.mysql.cj.protocol.AuthenticationPlugin''. These plugins will be loaded at connection initialization and can be used together with their sever-side counterparts for authenticating users, unless they are also disabled in the connection property ''disabledAuthenticationPlugins''. ConnectionProperties.authenticationFidoCallbackHandler=Fully-qualified class name of a class implementing the interface ''com.mysql.cj.callback.MysqlCallbackHandler''. This class will be used by the FIDO authentication plugin to obtain the authenticator data and signature required for the FIDO authentication process. See the documentation of ''com.mysql.cj.callback.FidoAuthenticationCallback'' for more details. +ConnectionProperties.authenticationPlugins=Comma-delimited list of classes that implement the interface ''com.mysql.cj.protocol.AuthenticationPlugin''. These plugins will be loaded at connection initialization and can be used together with their sever-side counterparts for authenticating users, unless they are also disabled in the connection property ''disabledAuthenticationPlugins''. +ConnectionProperties.authenticationWebAuthnCallbackHandler=Fully-qualified class name of a class implementing the interface ''com.mysql.cj.callback.MysqlCallbackHandler''. This class will be used by the WebAuthn authentication plugin to obtain the authenticator data and signature required for the FIDO authentication process. See the documentation of com.mysql.cj.callback.WebAuthnAuthenticationCallback for more details. ConnectionProperties.autoClosePstmtStreams=Should the driver automatically call the method ''close()'' on streams/readers passed as arguments via ''set*()'' methods? ConnectionProperties.autoGenerateTestcaseScript=Should the driver dump the SQL it is executing, including server-side prepared statements to STDERR? ConnectionProperties.autoReconnect=Should the driver try to re-establish stale and/or dead connections? If enabled the driver will throw an exception for queries issued on a stale or dead connection, which belong to the current transaction, but will attempt reconnect before the next query issued on the connection in a new transaction. The use of this feature is not recommended, because it has side effects related to session state and data consistency when applications don''t handle SQLExceptions properly, and is only designed to be used when you are unable to configure your application to handle SQLExceptions resulting from dead and stale connections properly. Alternatively, as a last option, investigate setting the MySQL server variable ''wait_timeout'' to a high value, rather than the default of 8 hours.