VillageSQL is a drop-in replacement for MySQL with extensions.
All examples in this guide work on VillageSQL. Install Now →
This guide uses a preview capability. Start the server with
--vsql_allow_preview_extensions=ON, or INSTALL EXTENSION is refused.vsql_oauth2 extension lets an account sign in with a JSON Web Token instead: the provider issues it, the server verifies the signature and the expiry, and a claim inside the token names the account.
Set up a signing key
The extension verifies signatures either against keys fetched from a JWKS endpoint, which is what you use with a real provider, or against one public key you paste in. The static key is the quickest way to see the whole path working, so this guide starts there.username_claim names the claim that carries the account name. It defaults to sub, which is what the example token below uses.
With a real provider you set jwks_url instead. The extension fetches the keys, refetches them on the interval in jwks_refresh_interval, and refetches at once when a token names a key id the cache does not hold. Set issuer and audience too, so a token minted for some other system is not accepted here.
Create the account
The account is an ordinary MySQL account, named at creation as one that authenticates with the extension. It carries its own grants:username_claim, sub by default. The statement above is enough while that claim says analyst. When your provider puts something else there, an email address for instance, create that account as well and let the connecting account proxy onto it:
alice@example.com directly, so it needs no password of its own. A server running validate_password refuses to create an account without one, answering ERROR 1819 (HY000): Your password does not satisfy the current policy requirements. Where that applies, give the account a password nobody holds, or an authentication plugin that permits no login at all.
Without the proxy grant the login is refused with ERROR 6126 (HY000): Access denied for user 'oidc_user'@'localhost', missing proxy privilege.
Log in
The token travels where the password normally goes, through the built-inmysql_clear_password plugin, which the client must opt into:
--password= puts it in the process list, which is why the token goes in the environment instead.
Inside the session, CURRENT_USER() is the database account and @@external_user keeps the identity the token carried, which is what you want in an audit trail:
Mapping roles from the token
An account can also take its privileges from the token. Point the extension at the claim that carries roles, and turn on the grant:"roles": ["shop_reader"] logs in with that role active:
roles_filter narrows which role names are considered, and roles_transform_pattern with roles_transform_replacement rewrites a provider’s name into a database role name, for providers whose group names do not look like role names.
With auto_grant off, the same token logs in, the role is skipped rather than granted, and CURRENT_ROLE() is NONE. Each skipped role is written to the error log: VEF auth: role 'shop_reader'@'%' requested for account 'dana'@'%' is not granted; skipping. auto_create behaves the same way for accounts, creating one at first login for a subject the server has never seen. Both are off by default, and both hand your provider authority over this server.
A rejected token looks like a wrong password
Every failure produces one error, and it is the ordinary one:using password: NO — which is misleading, since a token was sent. Nothing distinguishing appears in the server error log either, even at log_error_verbosity=3.
So debug from the token rather than from the error. Decode it and check four things in this order:
expis in the future. Clock skew between the provider and the server counts here.- The signing key matches. Check that the token’s
kidis one the JWKS document still publishes. A key the document no longer carries looks exactly like a forged token. issandaudmatch the values you configured, if you configured them.- The claim in
username_claimnames an account that exists, spelled the way the account is spelled.
Troubleshooting
See also
- MySQL User Management — the grants a token-authenticated account still needs
- MySQL Security Hardening — TLS and the rest of the connection surface
- Signing Data with HMAC — verifying a signature inside SQL rather than at login

