Skip to content
4 changes: 2 additions & 2 deletions mysql-schema/mysql-schema-user.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ The `mysql.user` table contains several fields that can be categorized into thre
* `authentication_string` and `plugin`: `authentication_string` stores the credentials for the user account. The credentials are interpreted based on the authentication plugin specified in the `plugin` field.
* `Account_locked`: indicates whether the user account is locked.
* `Password_reuse_history` and `Password_reuse_time`: used for [Password reuse policy](/password-management.md#password-reuse-policy).
* `User_attributes`: provides information about user comments and user attributes.
* `User_attributes`: provides information about user comments and user attributes. It also stores the secondary password under `$.additional_password` when [dual passwords](/password-management.md#dual-password-policy) are used.
* `Token_issuer`: used for the [`tidb_auth_token`](/security-compatibility-with-mysql.md#tidb_auth_token) authentication plugin.
* `Password_expired`, `Password_last_changed`, and `Password_lifetime`: used for [Password expiration policy](/password-management.md#password-expiration-policy).

Expand All @@ -102,7 +102,7 @@ The `mysql.user` table contains several fields that can be categorized into thre
* `authentication_string` and `plugin`: `authentication_string` stores the credentials for the user account. The credentials are interpreted based on the authentication plugin specified in the `plugin` field.
* `Account_locked`: indicates whether the user account is locked.
* `Password_reuse_history` and `Password_reuse_time`: used for [Password reuse policy](https://docs.pingcap.com/tidb/stable/password-management#password-reuse-policy).
* `User_attributes`: provides information about user comments and user attributes.
* `User_attributes`: provides information about user comments and user attributes. It also stores the secondary password under `$.additional_password` when [dual passwords](https://docs.pingcap.com/tidb/stable/password-management#dual-password-policy) are used.
* `Token_issuer`: used for the [`tidb_auth_token`](https://docs.pingcap.com/tidb/stable/security-compatibility-with-mysql#tidb_auth_token) authentication plugin.
* `Password_expired`, `Password_last_changed`, and `Password_lifetime`: used for [Password expiration policy](https://docs.pingcap.com/tidb/stable/password-management#password-expiration-policy).

Expand Down
51 changes: 51 additions & 0 deletions password-management.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
- Password reuse policy: prevent users from reusing old passwords.
- Failed-login tracking and temporary account locking policy: temporarily lock a user account to prevent the same user from trying to log in after multiple login failures caused by wrong passwords.

Starting from v8.5.7, TiDB also supports the dual password policy, which lets you rotate passwords for applications without downtime. For details, see [Dual password policy](#dual-password-policy).

## TiDB authentication credential storage

To ensure the authenticity of user identity, TiDB uses passwords as credentials to authenticate users when they log in to the TiDB server.
Expand Down Expand Up @@ -358,6 +360,55 @@
> - The default value of the `PASSWORD HISTORY` and `PASSWORD REUSE INTERVAL` options is 0, which means that the reuse policy is disabled.
> - When you modify a username, TiDB migrates the corresponding password history in the `mysql.password_history` system table from the original username to the new username.

## Dual password policy

Starting from v8.5.7, TiDB supports the MySQL-compatible [dual password policy](https://dev.mysql.com/doc/refman/8.0/en/password-management.html#dual-passwords), which allows an account to hold two valid passwords at the same time: a primary password and a secondary password. When many applications share one account, dual passwords let you rotate the password in phases without any downtime: establish a new primary password while the old password keeps working, migrate the applications one by one, and then discard the old password.

Check warning on line 365 in password-management.md

View workflow job for this annotation

GitHub Actions / vale

[vale] reported by reviewdog 🐶 [PingCAP.Ambiguous] Consider using a clearer word than 'many' because it may cause confusion. Raw Output: {"message": "[PingCAP.Ambiguous] Consider using a clearer word than 'many' because it may cause confusion.", "location": {"path": "password-management.md", "range": {"start": {"line": 365, "column": 283}}}, "severity": "INFO"}

A typical rotation works as follows:

1. Set a new primary password and retain the current password as the secondary password:

```sql
ALTER USER 'app_user'@'%' IDENTIFIED BY 'new_password' RETAIN CURRENT PASSWORD;
```

At this point, `app_user` can log in with both `new_password` and the previous password.

2. Update every application to use the new password.

3. After all applications are migrated, discard the secondary password:

```sql
ALTER USER 'app_user'@'%' DISCARD OLD PASSWORD;
```

At this point, only `new_password` is valid.

`SET PASSWORD` supports the same clause:

```sql
SET PASSWORD FOR 'app_user'@'%' = 'new_password' RETAIN CURRENT PASSWORD;
```

TiDB stores the secondary password in the `User_attributes` column of the `mysql.user` system table, and [`SHOW CREATE USER`](/sql-statements/sql-statement-show-create-user.md) does not display it in its output.

### Required privileges

- Operating on your own account (including the `ALTER USER USER() ...` and `SET PASSWORD ... RETAIN CURRENT PASSWORD` forms) requires the `APPLICATION_PASSWORD_ADMIN` [dynamic privilege](/privilege-management.md#dynamic-privileges). The `CREATE USER` privilege or the `UPDATE` privilege on the `mysql` schema also suffices.
- Operating on another account requires the normal cross-account authority: the `CREATE USER` privilege (or the `UPDATE` privilege on the `mysql` schema) for `ALTER USER`, and the `SUPER` privilege for `SET PASSWORD`. `APPLICATION_PASSWORD_ADMIN` does not grant authority over other accounts.

### Restrictions

- TiDB only supports dual passwords for accounts that authenticate with the `mysql_native_password`, `caching_sha2_password`, or `tidb_sm3_password` plugin.
- `RETAIN CURRENT PASSWORD` requires a non-empty new password. Otherwise, TiDB reports the `ER_CURRENT_PASSWORD_CANNOT_BE_RETAINED` error.
- `RETAIN CURRENT PASSWORD` requires a non-empty current password. Otherwise, TiDB reports the `ER_SECOND_PASSWORD_CANNOT_BE_EMPTY` error.
- You cannot combine `RETAIN CURRENT PASSWORD` with a change of the authentication plugin. Otherwise, TiDB reports the `ER_PASSWORD_CANNOT_BE_RETAINED_ON_PLUGIN_CHANGE` error. Changing the authentication plugin without `RETAIN CURRENT PASSWORD` silently removes any existing secondary password.
- Changing the primary password without `RETAIN CURRENT PASSWORD` leaves an existing secondary password unchanged.

> **Note:**
>
> During a rolling upgrade, a retained secondary password authenticates only on TiDB nodes that are already upgraded to a version that supports dual passwords. The new primary password works on all nodes. Complete the cluster upgrade before relying on dual password rotation.

## Failed-login tracking and temporary account locking policy

TiDB can track the number of failed login attempts for an account. To prevent the password from being cracked by brute force, TiDB can lock the account after a specified number of failed login attempts.
Expand Down
1 change: 1 addition & 0 deletions privilege-management.md
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,7 @@ Dynamic privileges include:
* `SYSTEM_VARIABLES_ADMIN`
* `ROLE_ADMIN`
* `CONNECTION_ADMIN`
* `APPLICATION_PASSWORD_ADMIN` allows privilege owners to use the `RETAIN CURRENT PASSWORD` and `DISCARD OLD PASSWORD` clauses on their own account. For details, see [Dual password policy](/password-management.md#dual-password-policy).
* `PLACEMENT_ADMIN` allows privilege owners to create, modify, and remove placement policies.
* `DASHBOARD_CLIENT` allows privilege owners to log in to TiDB Dashboard.
* `RESTRICTED_TABLES_ADMIN` allows privilege owners to view system tables when SEM is enabled.
Expand Down
1 change: 0 additions & 1 deletion security-compatibility-with-mysql.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ TiDB supports security features similar to MySQL 5.7, and also supports some sec
- Column level permissions.
- These permission attributes: `max_questions` and `max_updated`.
- Password verification policy, which requires you to verify the current password when you change it.
- Dual password policy.
- Random password generation.
- Multi-factor authentication.

Expand Down
43 changes: 41 additions & 2 deletions sql-statements/sql-statement-alter-user.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,16 @@ This statement changes an existing user inside the TiDB privilege system. In the

```ebnf+diagram
AlterUserStmt ::=
'ALTER' 'USER' IfExists (UserSpecList RequireClauseOpt ConnectionOptions PasswordOption LockOption AttributeOption | 'USER' '(' ')' 'IDENTIFIED' 'BY' AuthString) ResourceGroupNameOption
'ALTER' 'USER' IfExists (UserSpecList RequireClauseOpt ConnectionOptions PasswordOption LockOption AttributeOption | 'USER' '(' ')' ('IDENTIFIED' 'BY' AuthString DualPasswordOption | 'DISCARD' 'OLD' 'PASSWORD')) ResourceGroupNameOption

UserSpecList ::=
UserSpec ( ',' UserSpec )*

UserSpec ::=
Username AuthOption
Username AuthOption DualPasswordOption

DualPasswordOption ::=
( 'RETAIN' 'CURRENT' 'PASSWORD' | 'DISCARD' 'OLD' 'PASSWORD' )?

Username ::=
StringName ('@' StringName | singleAtIdentifier)? | 'CURRENT_USER' OptionalBraces
Expand Down Expand Up @@ -74,6 +77,41 @@ mysql> SHOW CREATE USER 'newuser';
1 row in set (0.00 sec)
```

Rotate the password for `newuser` without downtime using [dual passwords](/password-management.md#dual-password-policy): retain the current password as the secondary password while setting a new primary password, and discard it after all applications have switched to the new password. Both statements require the `APPLICATION_PASSWORD_ADMIN` dynamic privilege when operating on your own account.

```sql
ALTER USER 'newuser' IDENTIFIED BY 'newpassword' RETAIN CURRENT PASSWORD;
```

```
Query OK, 0 rows affected (0.02 sec)
```

At this point, `newuser` can log in with both the new and the previous password. TiDB stores the secondary password under `$.additional_password` in the `User_attributes` column of the `mysql.user` system table:

```sql
SELECT JSON_EXTRACT(User_attributes, '$.additional_password') IS NOT NULL AS has_secondary FROM mysql.user WHERE User = 'newuser';
```

```
+---------------+
| has_secondary |
+---------------+
| 1 |
+---------------+
1 row in set (0.00 sec)
```

```sql
ALTER USER 'newuser' DISCARD OLD PASSWORD;
```

```
Query OK, 0 rows affected (0.02 sec)
```

After `DISCARD OLD PASSWORD`, only the new password is valid.

Lock the user `newuser`:

```sql
Expand Down Expand Up @@ -216,6 +254,7 @@ SELECT USER, JSON_EXTRACT(User_attributes, "$.resource_group") FROM mysql.user W
<CustomContent platform="tidb">

* [TiDB User Account Management](/user-account-management.md)
* [TiDB Password Management](/password-management.md)
* [Security Compatibility with MySQL](/security-compatibility-with-mysql.md)

</CustomContent>
Expand Down
16 changes: 14 additions & 2 deletions sql-statements/sql-statement-set-password.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ This statement changes the user password for a user account in the TiDB system d

```ebnf+diagram
SetPasswordStmt ::=
"SET" "PASSWORD" ( "FOR" Username )? "=" ( stringLit | "PASSWORD" "(" stringLit ")" )
"SET" "PASSWORD" ( "FOR" Username )? "=" ( stringLit | "PASSWORD" "(" stringLit ")" ) ( "RETAIN" "CURRENT" "PASSWORD" )?
```

## Examples
Expand Down Expand Up @@ -55,9 +55,21 @@ mysql> SHOW CREATE USER 'newuser';
1 row in set (0.00 sec)
```

Starting from v8.5.7, `SET PASSWORD ... RETAIN CURRENT PASSWORD` retains the current password as the secondary password while setting the new primary password, so both passwords remain valid during a password rotation. For details, see [Dual password policy](/password-management.md#dual-password-policy).

```sql
SET PASSWORD FOR 'newuser' = 'newpassword' RETAIN CURRENT PASSWORD;
```

```
Query OK, 0 rows affected (0.01 sec)
```

Setting your own password with `RETAIN CURRENT PASSWORD` requires the `APPLICATION_PASSWORD_ADMIN` dynamic privilege. Setting the password of another account requires the `SUPER` privilege.

## MySQL compatibility

The `SET PASSWORD` statement in TiDB is fully compatible with MySQL. If you find any compatibility differences, [report a bug](https://docs.pingcap.com/tidb/stable/support).
The `SET PASSWORD` statement in TiDB is fully compatible with MySQL, except that TiDB does not support the `REPLACE 'current_auth_string'` clause for verifying the current password. If you find any compatibility differences, [report a bug](https://docs.pingcap.com/tidb/stable/support).

## See also

Expand Down
Loading