In short
Problem: WordPress was the last service still asking for its own password.
Solution: An OAuth2/OpenID provider in Authentik plus the OpenID Connect Generic plugin in WordPress, with every value listed below.
Tested with: WordPress 7.1, Authentik 2026.8, OpenID Connect Generic 3.11
I use Authentik as the single login for my self-hosted services. This blog should work the same way: one click on the login button, passkey, done. The normal WordPress login stays as a fallback, in case Authentik is ever down.
In the examples, Authentik runs at https://auth.example.com, the blog at https://blog.example.com, and the Authentik application slug is blog. Replace these with your own values.
How the login works
- You click the login button on the WordPress login page.
- WordPress sends you to Authentik, where you log in, for example with a passkey.
- Authentik sends you back to WordPress with a one-time code.
- WordPress exchanges the code for tokens in the background, reads your username and e-mail, and logs you in as the matching WordPress user.
1. Authentik: create the provider
In the Authentik admin interface go to Applications → Providers → Create and choose OAuth2/OpenID Provider. These are the settings I use:
| Setting | Value |
|---|---|
| Name | Provider for Blog |
| Authorization flow | default-provider-authorization-implicit-consent (no extra “Allow access?” screen) |
| Invalidation flow | default-provider-invalidation-flow |
| Client type | Confidential |
| Client ID / Client Secret | generated by Authentik, copy both for WordPress |
| Redirect URI (strict) | https://blog.example.com/wp-admin/admin-ajax.php?action=openid-connect-authorize |
| Signing key | authentik Self-signed Certificate |
| Scopes | the default mappings for openid, email and profile |
| Subject mode | Based on the User’s hashed ID |
| Include claims in id_token | on |
| Issuer mode | Each provider has a different issuer |
| Grant types | Authorization code and refresh token |
| Access token validity | 5 minutes |
| Refresh token validity | 30 days |
Watch out: if you create the provider through the Authentik API instead of the UI, set grant_types yourself. Without it the list stays empty and every login ends with “The request is otherwise malformed”. The Authentik server log then shows “Invalid grant_type for provider”.
For reference, the same provider created through the API:
curl -X POST https://auth.example.com/api/v3/providers/oauth2/ \
-H "Authorization: Bearer $AUTHENTIK_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "Provider for Blog",
"client_type": "confidential",
"authorization_flow": "<uuid of default-provider-authorization-implicit-consent>",
"invalidation_flow": "<uuid of default-provider-invalidation-flow>",
"property_mappings": ["<openid uuid>", "<email uuid>", "<profile uuid>"],
"signing_key": "<uuid of the signing certificate>",
"sub_mode": "hashed_user_id",
"include_claims_in_id_token": true,
"grant_types": ["authorization_code", "refresh_token"],
"redirect_uris": [{
"matching_mode": "strict",
"url": "https://blog.example.com/wp-admin/admin-ajax.php?action=openid-connect-authorize"
}]
}'
2. Authentik: create the application
Go to Applications → Applications → Create:
| Setting | Value |
|---|---|
| Name | Blog |
| Slug | blog (it becomes part of the issuer and JWKS URLs) |
| Provider | Provider for Blog |
| Launch URL | https://blog.example.com/wp-admin/ |
Then open the application, go to Policy / Group / User Bindings and bind the users or the group who may log in to WordPress. Without a binding, every Authentik user can use the application.
All endpoints are listed in the discovery document of the application. Open it once to check that it works:
curl -s https://auth.example.com/application/o/blog/.well-known/openid-configuration
3. WordPress: install the plugin
Install and activate OpenID Connect Generic (by daggerhart) from the plugin directory. The settings are under Settings → OpenID Connect Client. As soon as the plugin is active, the login page shows a button. It only works once the settings below are saved.
4. WordPress: all plugin settings
| Field | Value |
|---|---|
| Login Type | OpenID Connect button on login form |
| Client ID | from the Authentik provider |
| Client Secret Key | from the Authentik provider |
| OpenID Scope | openid email profile |
| Login Endpoint URL | https://auth.example.com/application/o/authorize/ |
| Userinfo Endpoint URL | https://auth.example.com/application/o/userinfo/ |
| Token Validation Endpoint URL | https://auth.example.com/application/o/token/ |
| End Session Endpoint URL | https://auth.example.com/application/o/blog/end-session/ |
| JWKS URI | https://auth.example.com/application/o/blog/jwks/ |
| Issuer (if your version asks) | https://auth.example.com/application/o/blog/ |
| ACR values | leave empty |
| Identity Key | preferred_username |
| Disable SSL Verify | off |
| HTTP Request Timeout | 5 |
| Nickname Key | preferred_username |
| Email Formatting | {email} |
| Display Name Formatting | {name} |
| Identify with User Name | off |
| Enable Refresh Token | on |
| Link Existing Users | on |
| Create user if does not exist | off |
| Redirect Back to Origin Page | on |
| Redirect to the login screen when session is expired | off |
| Enable Logging | on while testing, off afterwards |
Only the JWKS URI, the End Session URL and the issuer contain the application slug. The authorize, token and userinfo endpoints are the same for every application in Authentik.
Tip: the bottom of the plugin settings page shows the exact Redirect URI WordPress will use. Copy it from there into Authentik, so the two match character for character.
How existing users are matched
With Link Existing Users on, the plugin looks for a WordPress user whose username matches the value of the identity key, here the Authentik username (preferred_username). The first login links the two accounts, and after that the link is fixed. Use the same username in WordPress and Authentik, or log in once with the matching account. With Create user if does not exist off, nobody gets a new WordPress account just because they have an Authentik login.
Optional: keep the secret out of the database
Instead of typing the values into the settings page, you can define them in wp-config.php. The plugin then uses these values and shows the fields as read-only:
define( 'OIDC_CLIENT_ID', 'your-client-id' );
define( 'OIDC_CLIENT_SECRET', 'your-client-secret' );
define( 'OIDC_CLIENT_SCOPE', 'openid email profile' );
define( 'OIDC_ENDPOINT_LOGIN_URL', 'https://auth.example.com/application/o/authorize/' );
define( 'OIDC_ENDPOINT_USERINFO_URL', 'https://auth.example.com/application/o/userinfo/' );
define( 'OIDC_ENDPOINT_TOKEN_URL', 'https://auth.example.com/application/o/token/' );
define( 'OIDC_ENDPOINT_LOGOUT_URL', 'https://auth.example.com/application/o/blog/end-session/' );
5. Test it
Before clicking the button, test the authorize request from the command line. A working provider redirects to the Authentik login flow:
curl -s -o /dev/null -w "%{http_code} -> %{redirect_url}\n" \
"https://auth.example.com/application/o/authorize/?response_type=code&scope=openid%20email%20profile&client_id=YOUR_CLIENT_ID&state=test&redirect_uri=https%3A%2F%2Fblog.example.com%2Fwp-admin%2Fadmin-ajax.php%3Faction%3Dopenid-connect-authorize"
# good: 302 -> https://auth.example.com/if/flow/default-authentication-flow/?...
# wrong: 302 -> https://blog.example.com/...?error=invalid_request&...
If that looks good, log out of WordPress, click the login button and log in with Authentik. You should land in the dashboard as your existing user.
Troubleshooting
| What you see | Likely cause |
|---|---|
| “The request is otherwise malformed” | No grant types on the provider (created through the API) |
| “Redirect URI Error” | The redirect URI in Authentik differs from the one WordPress sends, even by a slash |
| Back on WordPress, but not logged in | No WordPress user matches preferred_username, and user creation is off |
| “Permission denied” in Authentik | Your user is not bound to the application |
| A blank page with “0” | Authentik returned an error to the redirect URI. Check the Authentik server log. |
What I learned
- The Authentik UI fills in defaults that the API does not.
- The server log tells you the real reason when the browser only shows a generic error.
- Keep the local WordPress login as a fallback. If the identity provider is down, you still want to reach your own site.