kopexa / mautic-oauth-bundle
OAuth 2.1 authorization server for Mautic (authorization code + PKCE S256, refresh tokens, dynamic client registration, RFC 8414 metadata, JWKS). Used by MCP clients such as Claude and ChatGPT.
Package info
github.com/kopexa-grc/mautic-oauth-bundle
Type:mautic-plugin
pkg:composer/kopexa/mautic-oauth-bundle
Requires
- php: ~8.4
- ext-openssl: *
- defuse/php-encryption: ^2.4
- guzzlehttp/psr7: ^2.7
- league/oauth2-server: ^9.4
- mautic/core-lib: ^7.2
Requires (Dev)
None
Suggests
None
Provides
None
Conflicts
None
Replaces
None
README
OAuth 2.1 authorization server for Mautic 7, so MCP clients (Claude, ChatGPT, …) can connect with the standard MCP authorization flow.
Installation
Requires Mautic 7.2+ (Composer-based installation) and PHP 8.4+:
composer require kopexa/mautic-oauth-bundle php bin/console mautic:plugins:reload
Configure the signing and encryption keys (kopexa_oauth_private_key, kopexa_oauth_encryption_key) in Mautic's config/local.php or, in the kopexa image, via environment variables.
Already included in the hardened Mautic image
ghcr.io/kopexa-grc/mautic.
It provides:
- authorization code grant with PKCE (S256 only) and rotating refresh tokens
- dynamic client registration (RFC 7591)
- authorization server metadata (RFC 8414)
- JWKS
- RFC 9068 JWT access tokens with an RFC 8707 audience
- revocation (RFC 7009)
Consent is shown under /s/, so Mautic's own login (password or SAML)
authenticates the user.
Built on league/oauth2-server.
Configuration, security model and endpoints: docs/oauth.md.
| Parameter | Env (kopexa image) | |
|---|---|---|
kopexa_oauth_private_key |
MAUTIC_OAUTH_PRIVATE_KEY |
RSA private key (PEM) |
kopexa_oauth_encryption_key |
MAUTIC_OAUTH_ENCRYPTION_KEY |
secret for codes / refresh tokens |
kopexa_oauth_redirect_hosts |
MAUTIC_OAUTH_REDIRECT_HOSTS |
allowed redirect hosts |
kopexa_oauth_resource |
MAUTIC_OAUTH_RESOURCE |
token audience (default <site_url>/mcp) |
kopexa_oauth_access_ttl / kopexa_oauth_refresh_ttl |
via MAUTIC_CONFIG_PARAMETERS |
seconds (3600 / 2592000) |
kopexa_oauth_scopes, kopexa_oauth_default_scopes, kopexa_oauth_optin_scopes |
via MAUTIC_CONFIG_PARAMETERS |
operator overrides for scopes, see below (default: none) |
Scopes
The server has no scopes of its own: the bundles that enforce scopes
declare them. Implement ScopeProviderInterface in an autoconfigured
service; the server collects all providers:
use MauticPlugin\KopexaOAuthBundle\Scope\ScopeDefinition; use MauticPlugin\KopexaOAuthBundle\Scope\ScopeProviderInterface; final class MyScopes implements ScopeProviderInterface { public function scopes(): iterable { yield new ScopeDefinition('reports:read', 'Read reports', default: true); yield new ScopeDefinition('reports:delete', 'Delete reports', optIn: true); } }
| Flag | Effect |
|---|---|
default |
granted when a client requests no scope; always kept on the consent page (cannot be unticked) |
optIn |
shown highlighted and unticked on the consent page: the user has to tick it, even if the client requests it |
The scopes appear in the metadata (scopes_supported), the client
registration response and on the consent page. A scope declared by two
providers is an error. Operators can add scopes or replace descriptions
(kopexa_oauth_scopes: scope => description) and make further scopes
default or opt-in (kopexa_oauth_default_scopes, kopexa_oauth_optin_scopes).
kopexa/mautic-mcp-bundle
declares the mcp:* scopes this way.
Development
bin/check.shchecks the package (PHP lint,composer validate, version consistency). CI runs it on every push and pull request.tests/functional/run.shinstalls the plugin into a real Mautic 7 and tests it over HTTP; CI runs it on PHP 8.4 and 8.5.- End-to-end tests also run in kopexa-grc/mautic-stack: its smoke test installs this package into the Mautic image and exercises it end to end.
- Commits follow Conventional Commits
(
feat:,fix:,docs:,chore:…); they drive versions and the changelog. - Releases are automated with
release-please: it keeps a
release PR open that bumps
VERSION, the plugin version andCHANGELOG.md. Merging it tagsv<version>and creates the GitHub release. Packagist and mautic-stack's Renovate pick the tag up. - Dependencies are kept current by Renovate, once a month
(security fixes immediately). Composer constraint updates
are committed as
fix(deps):and so lead to a patch release; CI-only updates (ci:) do not.
Apache-2.0 · Kopexa