Skip to content

Rate this page
Thanks for your feedback
Thank you! The feedback has been submitted.

For help, click the link below to get free database assistance or contact our experts for personalized support.

OpenID Connect authentication

Tip

Want to try OIDC end-to-end on a single server? Follow the Get started with OpenID Connect authentication walkthrough. This page is the full reference.

What is OpenID Connect authentication?

OpenID Connect (OIDC) is an identity layer on top of the OAuth 2.0 framework. The user authenticates against an external Identity Provider (IDP) before connecting to MySQL. Supported providers include Keycloak, Okta, and Microsoft Entra ID.

The IDP issues a signed JSON Web Token (JWT), called the Identity token. The client transmits the token to Percona Server for MySQL during the authentication handshake. The server verifies the token and grants the connection without exchanging a password.

For an overview of the supported authentication methods, see Authentication methods. To compare OIDC with related plugins, see LDAP authentication, PAM authentication, and FIDO authentication. To configure the encrypted transport that OIDC requires, see SSL improvements.

Plugin capabilities

The plugin provides the following capabilities:

  • Verify signed Identity tokens issued by one or more configured Identity Providers.

  • Refresh signing keys from a JSON Web Key Set (JWKS) endpoint at runtime.

  • Enforce an optional aud audience check per IDP.

  • Map IDP group claims to MySQL roles for the duration of the session.

  • Support the RS256, RS384, RS512, ES256, and HS256 signature algorithms.

The server-side plugin pairs with the authentication_openid_connect_client client-side plugin distributed with Percona Server for MySQL.

Plugin and library file names

The library file must reside in the directory named by the plugin_dir system variable. The file name suffix may differ on your platform.

The following table lists the plugin components:

Plugin or file Name
Server-side plugin auth_openid_connect
Client-side plugin authentication_openid_connect_client
Server library file auth_openid_connect.so
Client library file authentication_openid_connect_client.so

Note

The client-side plugin and its option flag may be renamed in a future release. The change accompanies a planned rewrite of the client-side code.

How does OpenID Connect authentication work?

The plugin processes a connection in the following sequence:

  1. The user authenticates against the IDP through an out-of-band flow. The IDP returns a JWT Identity token.

  2. The user writes the token to a file readable by the operating system account that runs the client. The token must not exceed 10 KB.

  3. The client reads the token file. The client validates that the file contains a well-formed JWT.

  4. The client confirms the connection uses TLS, a Unix domain socket, or shared memory. The client refuses to send the token over plaintext TCP.

  5. The client transmits the token to the server during the authentication handshake.

  6. The server confirms the connection is secure. The server then receives and decodes the token.

  7. The server validates the token against the configuration of the referenced IDP.

The plugin accepts the token only when every check in the following table passes:

Check Requirement
JWT structure Parses as a valid JWT
Signature algorithm One of RS256, RS384, RS512, ES256, or HS256
Signature Verified by a configured public key, selected by the kid header
Expiration (exp claim) Timestamp in the future
Issuer (iss claim) Equals the configured issuer-name for the IDP
Subject (sub claim) Equals the user value in the account IDENTIFIED ... AS clause
Audience (aud claim) Matches an allowed audience when audiences is configured

When group-claim and group-role are both configured, the plugin reads the group claim from the token. The plugin grants the connection any matching MySQL roles for the session.

The plugin denies the connection on any failed check. The plugin writes a diagnostic message to the server error log.

The plugin validates the token only at connection time. A connection remains active when the token expires later in the session.

Prerequisites

Before you configure OpenID Connect authentication, gather the resources in the following table:

Resource Description
Identity Provider OIDC-compliant IDP that issues signed Identity tokens
IDP issuer URL URL plus either a JWKS endpoint or a static set of public keys exported as JWKs
Secure transport TLS or a Unix domain socket between the client and the server
Token delivery method Wrapper script or other tool that calls the IDP token endpoint and writes tokens to a file
User identifier sub claim value supplied by the IDP for each user account

The plugin has been tested with Keycloak. Any IDP that exposes a standard JWKS endpoint is compatible.

Install the plugin

INSTALL PLUGIN requires the INSERT privilege on mysql.plugin. Editing my.cnf requires file-system access on the server host.

Choose one of the following installation methods:

Run this statement to load the plugin during a session:

INSTALL PLUGIN auth_openid_connect SONAME 'auth_openid_connect.so';
Expected output
Query OK, 0 rows affected (0.01 sec)

Add the following lines to the [mysqld] section of my.cnf:

[mysqld]
plugin-load-add=auth_openid_connect.so

Restart the server for the change to take effect.

Verify the installation

Run the SHOW PLUGINS statement, or query INFORMATION_SCHEMA.PLUGINS. Confirm that the plugin loaded successfully:

SELECT PLUGIN_NAME, PLUGIN_STATUS
FROM INFORMATION_SCHEMA.PLUGINS
WHERE PLUGIN_NAME = 'auth_openid_connect';
Expected output
+---------------------+---------------+
| PLUGIN_NAME         | PLUGIN_STATUS |
+---------------------+---------------+
| auth_openid_connect | ACTIVE        |
+---------------------+---------------+
1 row in set (0.00 sec)

A PLUGIN_STATUS value other than ACTIVE, or an empty result set, indicates that the plugin failed to load. Check the server error log for the cause.

Configure the plugin

The plugin reads the list of trusted Identity Providers from a single JSON document. The administrator supplies the document through the auth_openid_connect_configuration system variable. The variable accepts the document inline or as a path to a file.

SET GLOBAL requires the SYSTEM_VARIABLES_ADMIN privilege. SET PERSIST_ONLY additionally requires PERSIST_RO_VARIABLES_ADMIN.

Configuration schema

Each top-level key in the JSON document is the IDP name. User accounts reference this name in the IDENTIFIED ... AS clause. The value is an object with the following members:

Key Required Description
audiences No An array of allowed aud claim values. The plugin rejects the token when the aud claim does not match. The plugin omits the audience check when this key is absent.
group-claim No The name of the JWT claim that lists the user group memberships, such as groups. The claim value must be a string or an array of strings.
group-role No An array of single-key objects that map IDP group names to MySQL role names. The mapping pairs with group-claim.
issuer-name Yes The exact value of the iss claim that the IDP issues. The plugin matches this value against the token’s iss claim.
jwks-url When keys is absent The HTTPS URL of the IDP JWKS endpoint. The plugin fetches and caches keys from the URL. The plugin can refresh keys at runtime through the update_jwks() UDF. HTTP URLs are accepted for testing only and emit a warning.
keys When jwks-url is absent An array of JSON Web Key (JWK) objects that verify token signatures. Each entry must include kty (RSA or EC), kid, and the algorithm-specific parameters. RSA keys require n and e. EC keys require crv, x, and y.

Set the configuration variable

The system variable auth_openid_connect_configuration accepts a string with one of the following prefixes:

  • JSON:// followed by the configuration JSON inline.

  • FILE:// followed by an absolute path to a file that contains the configuration JSON.

The prefix check is case-insensitive. Setting the variable to a value without a recognized prefix fails with ERROR 1231 (42000). The same error applies when the file cannot be read or parsed. Detailed messages appear in the server error log.

Example configuration

The following JSON document configures two trusted IDPs. The first uses static keys to verify token signatures. The second resolves keys at runtime through a jwks-url.

The n value is truncated for brevity. Replace ptR4...QEASRw with the full base64url-encoded RSA modulus from the IDP signing key.

{
  "oidc-idp": {
    "issuer-name": "https://idp.example.com/realms/dummy",
    "keys": [
      {
        "kid": "rsa-key-1",
        "kty": "RSA",
        "n": "ptR4YxjdrF2RrYiY9XYH3KcXKzlS6b2foGAeHN9dViAs5y...QEASRw",
        "e": "AQAB",
        "use": "sig",
        "alg": "RS256"
      }
    ],
    "audiences": [
      "ee2811b9-10b8",
      "https://api.example.com"
    ],
    "group-claim": "groups",
    "group-role": [
      { "acc": "accounting" },
      { "eng": "engineering" }
    ]
  },
  "my-keycloak": {
    "issuer-name": "https://keycloak.example.com/realms/master",
    "jwks-url": "https://keycloak.example.com/realms/master/protocol/openid-connect/certs",
    "audiences": [ "account" ],
    "group-claim": "groups",
    "group-role": [
      { "/accounting": "accounting" },
      { "/marketing": "marketing" }
    ]
  }
}

The two subsections that follow load this document into the auth_openid_connect_configuration system variable.

Configure from a file

Save the example as /etc/mysql/oidc/idps.json. Set the variable to reference the file path:

SET GLOBAL auth_openid_connect_configuration =
  'FILE:///etc/mysql/oidc/idps.json';
Expected output
Query OK, 0 rows affected (0.00 sec)

Configure inline

Set the variable to an inline JSON document:

SET GLOBAL auth_openid_connect_configuration =
  'JSON://{"my-keycloak":{"issuer-name":"https://keycloak.example.com/realms/master","jwks-url":"https://keycloak.example.com/realms/master/protocol/openid-connect/certs","audiences":["account"]}}';
Expected output
Query OK, 0 rows affected (0.00 sec)

Persist the configuration

Warning

A SET GLOBAL assignment does not survive a server restart. Use SET PERSIST_ONLY to persist the configuration to disk and avoid an authentication outage on the next restart.

Persist the configuration with the following statement:

SET PERSIST_ONLY auth_openid_connect_configuration =
  'FILE:///etc/mysql/oidc/idps.json';
Expected output
Query OK, 0 rows affected (0.00 sec)

Create a user

Each MySQL account maps to one user identity in one Identity Provider. The mapping is encoded as a JSON object in the IDENTIFIED ... AS clause:

CREATE USER 'mysql_oidc_user'@'%'
  IDENTIFIED WITH 'auth_openid_connect'
  AS '{"identity_provider": "my-keycloak", "user": "4c28d537-a635-4b6d-957f-58e3c8860bcc"}';
Expected output
Query OK, 0 rows affected (0.01 sec)

The clause requires two fields:

  • identity_provider must match a top-level key in auth_openid_connect_configuration.

  • user must match the sub claim in the Identity tokens that the IDP issues for this user.

For Keycloak, the sub claim contains the user UUID. For other providers, the claim may contain an email address or another stable identifier.

The server validates the JSON at connection time, not at user creation. The connection fails when either field is missing. The connection also fails when the configuration does not contain the referenced IDP.

Grant privileges to the account with GRANT, the same as any other MySQL account.

Obtain an Identity token

The plugin requires the ID token issued by the IDP. The ID token is distinct from the access token. Production environments use the OAuth 2.0 flow that the IDP recommends. Common choices include authorization code with PKCE and device authorization.

The following example uses Keycloak and the password grant. The flow suits scripted tests and development environments. Replace placeholders with values from your IDP:

curl -s -X POST \
  https://keycloak.example.com/realms/master/protocol/openid-connect/token \
  -d 'grant_type=password' \
  -d 'client_id=mysql-oidc' \
  -d 'scope=openid' \
  -d 'username=alice' \
  -d 'password=<password>' \
  | jq -r .id_token > /run/user/1000/id_token.jwt

chmod 600 /run/user/1000/id_token.jwt

The scope=openid parameter is required. Without it, the IDP returns an access token but no ID token. The client_id must reference a client registered in the IDP. The client must permit the relevant grant type.

The file must contain only the raw compact-serialized JWT. The JWT is three base64url segments joined by dots, with no surrounding whitespace, JSON wrapper, or Bearer prefix.

For other IDPs, see the vendor documentation. Examples: Okta token endpoint and Microsoft Entra ID OAuth 2.0 token endpoint .

Connect with a client

A client passes the path to the Identity token file with the --authentication-openid-connect-client-id-token-file option:

mysql --host=mysql.example.com \
      --ssl-mode=REQUIRED \
      --user=mysql_oidc_user \
      --authentication-openid-connect-client-id-token-file=/run/user/1000/id_token.jwt

The client refuses to authenticate when any of the following conditions occur:

  • The --authentication-openid-connect-client-id-token-file option is missing.

  • The token file is missing, empty, or larger than 10 KB.

  • The token file does not contain a syntactically valid JWT.

  • The connection between the client and server is not secured by TLS, a Unix socket, or shared memory.

Map groups to MySQL roles

When group-claim and group-role are both configured for an IDP, the plugin inspects the named claim at connection time. The plugin associates each matching MySQL role with the connection. The user activates the role with SET ROLE .

Consider the following configuration fragment:

"group-claim": "groups",
"group-role": [
  { "acc": "accounting" },
  { "eng": "engineering" }
]

Suppose a token has the groups claim with the value ["acc", "hr"]. The connection then exhibits the following behavior:

  • SET ROLE accounting succeeds because acc maps to the accounting role.

  • SET ROLE engineering fails because eng is not present in the token.

  • SET ROLE hr fails because hr has no role mapping.

The roles must already exist on the server. The plugin does not create roles automatically.

The group claim must be a JSON array of strings or a single string. Any other type causes authentication to fail with the message cannot parse groups claim in the token.

Refresh JWKS keys

When you configure an IDP with jwks-url, the plugin loads keys at startup. The plugin also reloads keys on every assignment to auth_openid_connect_configuration. Keys may rotate at the IDP between configuration changes. The plugin provides the update_jwks() user-defined function (UDF) to refresh cached keys without changing the configuration.

Register the UDF after you install the plugin:

CREATE FUNCTION update_jwks RETURNS INTEGER SONAME 'auth_openid_connect.so';
Expected output
Query OK, 0 rows affected (0.01 sec)

Call the UDF without arguments to refresh keys for every configured IDP that has a jwks-url:

SELECT update_jwks();
Expected output
+---------------+
| update_jwks() |
+---------------+
|             1 |
+---------------+
1 row in set (0.05 sec)

Call the UDF with one string argument to refresh a single IDP:

SELECT update_jwks('my-keycloak');
Expected output
+-----------------------------+
| update_jwks('my-keycloak')  |
+-----------------------------+
|                           1 |
+-----------------------------+
1 row in set (0.04 sec)

The following table lists the return values:

Return value Meaning
>= 0 The number of IDPs whose keys were successfully refreshed.
-1 The named IDP is not in the configuration.
-2 An unexpected error occurred during the refresh. Details are written to the server error log.

Schedule the UDF from mysql.event or external tooling to align the cache with the key rotation policy of the IDP.

Uninstall the plugin

Warning

Drop every UDF that depends on the plugin before you uninstall the plugin. A stale function definition remains when UNINSTALL PLUGIN runs while a UDF still references the library.

First, drop the UDF:

DROP FUNCTION IF EXISTS update_jwks;
Expected output
Query OK, 0 rows affected (0.00 sec)

Then, uninstall the plugin:

UNINSTALL PLUGIN auth_openid_connect;
Expected output
Query OK, 0 rows affected (0.00 sec)

System variable reference

auth_openid_connect_configuration

The following table summarizes the system variable. For the full list of system variables, see Percona Server system variables.

Variable name Default value Scope Dynamic Valid values
auth_openid_connect_configuration {} Global Yes A string prefixed with JSON:// or FILE://

The variable holds the configuration of trusted OpenID Connect Identity Providers. The value must start with JSON:// for an inline JSON document. The value must start with FILE:// for an absolute path to a JSON file. The configuration schema appears in Configure the plugin.

The plugin validates the variable at assignment. The server rejects an unknown prefix, malformed JSON, or an unreadable file with ERROR 1231 (42000).

A configuration that references an unreachable jwks-url is accepted at assignment. The plugin writes a warning to the server error log. Authentication against that IDP fails until the keys load successfully. Run update_jwks() after the IDP becomes reachable.

User-defined function reference

update_jwks()

The following table summarizes the function:

Property Value
Return type INTEGER
Library auth_openid_connect.so
Arguments Zero or one string

The function refreshes cached JWKS public keys for one IDP or for all configured IDPs. For details on return values, see Refresh JWKS keys.

Troubleshoot connection failures

The plugin writes a single diagnostic line to the server error log on each rejected connection. The client receives a generic authentication failure to avoid leaking configuration details.

The following table follows a Symptoms, Diagnosis, and Solution model for the most frequent failures:

Symptom Diagnosis Solution
unsecure connection, use TLS, socket or memory in the server log The client connected over plaintext TCP. Reconnect with TLS, a Unix socket, or shared memory.
IDP not found: <name> in the server log The identity_provider value in the user IDENTIFIED ... AS clause does not match a key in the configuration. Update the user definition or the configuration so the IDP names align.
invalid sysvar prefix, expected FILE:// or JSON:// in the server log The auth_openid_connect_configuration value lacks a valid prefix. Reset the variable with the correct JSON:// or FILE:// prefix.
JWKS configuration is insecure, use HTTPS warning in the server log The jwks-url uses http://. Replace the URL with an https:// endpoint before production use.
JWKS: HTTP GET from <url> failed in the server log The IDP is unreachable or returned a non-2xx status. Verify network reachability. Run update_jwks() after the IDP recovers.
incorrect number of keys in the server log The token has no kid header but the IDP has multiple keys. Include the kid header in the token, or reduce the IDP to a single key.