WL#17355: MFA password support for connection APIs
Background
MySQL Shell connection APIs accept connection data as a URI or dictionary. A
dictionary previously represented only one password, although a classic MySQL
account can require up to three authentication factors. As a result,
applications using shell.connect() or mysql.getSession() could not provide,
prompt for, or store passwords for additional factors through the connection
API.
Motivation
Applications must be able to establish a classic MySQL session for an MFA account without relying on command-line password options. They must also be able to use the configured credential helper for each requested factor while retaining the existing first-factor password workflow.
User stories
- As an application developer, I want to provide passwords for all required authentication factors in connection data passed to a Shell connection API.
- As an application developer, I want to request a password interactively for a specific MFA factor when it is not supplied in connection data.
- As a user, I want prompted MFA passwords to be available through the configured credential helper on later connections.
Summary and scope
This work extends connection dictionaries accepted by Shell connection APIs
with MFA password fields and extends shell.storeCredential() to store the
passwords represented by connection data. It covers classic MySQL protocol
connections.
It also exposes WebAuthn connection options and a registration-factor connection option.
It does not add MFA password fields to connection URIs.
FR1 - Connection dictionaries accepted by shell.connect(),
shell.openSession(), and mysql.getSession() must accept password1,
password2, and password3 as string or null values.
FR1.1 - password and password1 both identify the first authentication
factor. A supplied string is used as that factor's password. If both are given,
password1 takes precedence.
FR1.2 - For password, omission or null must retain the existing first-factor
behavior: use a stored credential when available and otherwise prompt for a
password. no-password: true must provide an empty first-factor password and
must suppress a first-factor prompt.
FR1.3 - For password2 and password3, omission must not request the
respective factor. Null must request the factor, using a stored credential when
available and otherwise prompting for its password.
FR1.4 - A requested MFA password must be supplied to the MySQL client library for its corresponding authentication factor.
FR2 - In shell.storeCredential(url[, password]), the optional password
parameter must accept the existing string value or a dictionary containing
password, password1, password2, and password3. It must store every
password supplied or prompted for under the corresponding authentication factor.
FR2.1 - When shell.storeCredential() is called without a second argument,
Shell must prompt for the first-factor password before storing it. In password
dictionary form, a null password, password1, password2, or password3
field must prompt for its corresponding factor before storing it. An omitted
password2 or password3 field must not prompt for that factor.
FR2.2 - A string password parameter must supply the first-factor password,
as in the existing API. In dictionary form, password and password1
identify the first factor and password2 and password3 identify their
respective factors. If both password and password1 are supplied,
password1 takes precedence.
FR3 - Connection data must accept
plugin-authentication-webauthn-client-preserve-privacy as a boolean. When
enabled, Shell must request the WebAuthn client to use discoverable
credentials.
FR3.1 - Connection data must accept
plugin-authentication-webauthn-device as a non-negative integer selecting a
WebAuthn client device.
FR3.2 - Connection data must accept register-factor as either a string or
integer value identifying the authentication factor for device registration.
Connection data
The following dictionary form is accepted by the affected Shell APIs:
{
"user": "app",
"host": "db.example.com",
"password": "first-factor-password",
"password2": "second-factor-password",
"password3": "third-factor-password"
}
password1, password2, and password3 are factor-specific fields.
password1 is an alias for the existing first-factor password field. A null
factor field requests Shell's credential lookup and prompt workflow for that
factor; omitting password2 or password3 does not request it.
Credential API
shell.storeCredential() retains its URL input and changes its optional
password argument to:
shell.storeCredential(url[, password])
url retains the existing user@(host[:port]|socket) format. password may
be a string, supplying the first-factor password, or a dictionary with
password, password1, password2, and password3 fields. The dictionary
fields specify factor passwords.
WebAuthn and registration options
The following connection-data fields are accepted in addition to the MFA password fields:
{
"plugin-authentication-webauthn-client-preserve-privacy": true,
"plugin-authentication-webauthn-device": 0,
"register-factor": 2
}
plugin-authentication-webauthn-client-preserve-privacy is boolean.
plugin-authentication-webauthn-device is an integer greater than or equal to
zero. register-factor accepts either an integer or its string form.
Connection options retain factor-specific password state for the three MySQL authentication factors. The connection path resolves credentials and prompts per requested factor before calling the MySQL client library with each factor's password.
Credential helper entries for factors two and three use factor-specific identifiers derived from the user and connection transport endpoint. These entries are managed with the associated connection credential and are not exposed as generic Shell secrets.
The password-save policy evaluates the set of prompted passwords as one decision. If saved, every password prompted during that connection is stored; passwords that were supplied directly or retrieved from the credential helper are not newly stored by that decision.
Because the authentication protocol does not report in advance which MFA factors the server will require, Shell treats an omitted second- or third-factor field as an instruction not to request that factor. Applications that need Shell to obtain such a password must include the field with a null value.