Symfony bundle for openid-connect

Installs: 9 552

Dependents: 0

Suggesters: 0

Security: 0

Stars: 1

Watchers: 4

Forks: 0

Open Issues: 1


3.0.3 2023-03-01 10:37 UTC

This package is auto-updated.

Last update: 2023-05-15 13:57:14 UTC


Github Release PHP Version Build Status Codecov Code Coverage Read License Package downloads on Packagist

Symfony bundle for authorization via OpenID Connect.


To install run

composer require itk-dev/openid-connect-bundle


Before being able to use the bundle, you must have your own User entity and database setup.

Once you have this, you need to

  • Configure variables for OpenId Connect
  • Create an Authenticator class that extends the bundle authenticator, OpenIdLoginAuthenticator
  • Configure LoginTokenAuthenticator in order to use CLI login.

Variable configuration

In /config/packages/ you need the following itkdev_openid_connect.yaml file for configuring OpenId Connect variables

    cache_pool: '' # Cache item pool for caching discovery document and CLI login tokens
    route: '%env(string:OIDC_CLI_LOGIN_ROUTE)%' # Redirect route for CLI login
  user_provider: ~ #
    # Define one or more providers
    # [providerKey]:
    #   options:
    #     metadata_url: …
        metadata_url: '%env(string:ADMIN_OIDC_METADATA_URL)%'
        client_id: '%env(string:ADMIN_OIDC_CLIENT_ID)%'
        client_secret: '%env(string:ADMIN_OIDC_CLIENT_SECRET)%'
        # Specify redirect URI
        redirect_uri: '%env(string:ADMIN_OIDC_REDIRECT_URI)%'
        # Optional: Specify leeway (seconds) to account for clock skew between provider and hosting
        #           Defaults to 10
        leeway: '%env(int:ADMIN_OIDC_LEEWAY)%'
        metadata_url: '%env(string:USER_OIDC_METADATA_URL)%'
        client_id: '%env(string:USER_OIDC_CLIENT_ID)%'
        client_secret: '%env(string:USER_OIDC_CLIENT_SECRET)%'
        # As an alternative to using (a more or less) hardcoded redirect uri,
        # a Symfony route can be used as redirect URI
        redirect_route: 'default'
        # Define any params for the redirect_route
        # redirect_route_parameters: { type: user }

With the following .env environment variables

###> itk-dev/openid-connect-bundle ###
# "admin" open id connect configuration variables (values provided by the OIDC IdP)

# "user" open id connect configuration variables

# cli redirect url 
###< itk-dev/openid-connect-bundle ###

Set the actual values your env.local file to ensure they are not committed to Git.

In /config/routes/ you need a similar itkdev_openid_connect.yaml file for configuring the routing

  resource: "@ItkDevOpenIdConnectBundle/src/Resources/config/routes.yaml"
  prefix: "/openidconnect" # Prefix for bundle routes

It is not necessary to add a prefix to the bundle routes, but in case you want i.e. another /login route, it makes distinguishing between them easier.

When invoking the login controller action (route itkdev_openid_connect_login) the key of a provider must be set in the provider parameter, e.g.

  <a href="{{ path('itkdev_openid_connect_login', {provider: 'user'}) }}">{{ 'Sign in'|trans }}</a>
  $router->generate('itkdev_openid_connect_login', ['provider => 'user']);

Make sure to allow anonymous access to the login controller route, i.e. something along the lines of

# config/packages/security.yaml
    - { path: ^/openidconnect/login(/.+)?$, role: IS_AUTHENTICATED_ANONYMOUSLY }

CLI login

In order to use the CLI login feature the following environment variable must be set in order for Symfony to be able to generate URLs in commands:


See Symfony documentation: Generating URLs in Commands for more information.

You must also add the bundles CliLoginTokenAuthenticator to the security.yaml file:

        - ItkDev\OpenIdConnectBundle\Security\CliLoginTokenAuthenticator

Finally, configure the Symfony route to use for login links: cli_login_options: route. If yoy have multiple firewalls that are active for different url patterns you need to make sure you add LoginTokenAuthenticator to the firewall active for the route specified here.

Creating the Authenticator

The bundle can help you get the claims received from the authorizer – the only functions that need to be implemented are authenticate(), onAuthenticationSuccess() and start().


namespace App\Security;

use ItkDev\OpenIdConnect\Exception\ItkOpenIdConnectException;
use ItkDev\OpenIdConnectBundle\Security\OpenIdLoginAuthenticator;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Core\User\UserProviderInterface;
use Symfony\Component\Security\Http\Authenticator\Passport\Passport;

class SomeAuthenticator extends OpenIdLoginAuthenticator

    public function authenticate(Request $request): Passport
        // Get the OIDC claims.
        try {
            $claims = $this->validateClaims($request);
            // Authentication success
            // TODO: Implement authenticate() method.
        } catch (ItkOpenIdConnectException $exception) {
            // Authentication failed
            throw new CustomUserMessageAuthenticationException($exception->getMessage());

    public function onAuthenticationSuccess(Request $request, TokenInterface $token, string $firewallName): ?Response
        // TODO: Implement onAuthenticationSuccess() method.

    public function start(Request $request, AuthenticationException $authException = null)
        // TODO: Implement start() method.

See below for a full authenticator example.

Make sure to add your authenticator to the security.yaml file - and if you have more than one to add an entry point.

          - App\Security\ExampleAuthenticator
          - ItkDev\OpenIdConnectBundle\Security\LoginTokenAuthenticator
        entry_point: App\Security\ExampleAuthenticator

Example authenticator functions

Here is an example using a User with a name and email property. First we extract data from the claims, then check if this user already exists and finally update/create it based on whether it existed or not.


namespace App\Security;

use App\Entity\User;
use Doctrine\ORM\EntityManagerInterface;
use ItkDev\OpenIdConnect\Exception\ItkOpenIdConnectException;
use ItkDev\OpenIdConnectBundle\Exception\InvalidProviderException;
use ItkDev\OpenIdConnectBundle\Security\OpenIdConfigurationProviderManager;
use ItkDev\OpenIdConnectBundle\Security\OpenIdLoginAuthenticator;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Core\Exception\CustomUserMessageAuthenticationException;
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge;
use Symfony\Component\Security\Http\Authenticator\Passport\Passport;
use Symfony\Component\Security\Http\Authenticator\Passport\SelfValidatingPassport;

class AzureOIDCAuthenticator extends OpenIdLoginAuthenticator
     * AzureOIDCAuthenticator constructor
     * @param EntityManagerInterface $entityManager
     * @param RequestStack $requestStack
     * @param UrlGeneratorInterface $router
     * @param OpenIdConfigurationProviderManager $providerManager
    public function __construct(
        private readonly EntityManagerInterface $entityManager,
        private readonly RequestStack $requestStack,
        private readonly UrlGeneratorInterface $router,
        private readonly OpenIdConfigurationProviderManager $providerManager
    ) {
        parent::__construct($providerManager, $requestStack);

    /** @inheritDoc */
    public function authenticate(Request $request): Passport
        try {
            // Validate claims
            $claims = $this->validateClaims($request);

            // Extract properties from claims
            $name = $claims['name'];
            $email = $claims['upn'];

            // Check if user exists already - if not create a user
            $user = $this->entityManager->getRepository(User::class)
                ->findOneBy(['email'=> $email]);
            if (null === $user) {
                // Create the new user and persist it
                $user = new User();
            // Update/set user properties


            return new SelfValidatingPassport(new UserBadge($user->getUserIdentifier()));
        } catch (ItkOpenIdConnectException|InvalidProviderException $exception) {
            throw new CustomUserMessageAuthenticationException($exception->getMessage());

    /** @inheritDoc */
    public function onAuthenticationSuccess(Request $request, TokenInterface $token, string $firewallName): ?Response
        return new RedirectResponse($this->router->generate('homepage_authenticated'));

    /** @inheritDoc */
    public function start(Request $request, AuthenticationException $authException = null): Response
        return new RedirectResponse($this->router->generate('itkdev_openid_connect_login', [
            'provider' => 'user',

Sign in from command line

Rather than signing in via OpenId Connect, you can get a sign in url from the command line by providing a username. Make sure to configure OIDC_CLI_REDIRECT_URL. Run

bin/console itk-dev:openid-connect:login <username>


bin/console itk-dev:openid-connect:login --help

for details.

Be aware that a login token only can be used once before it is removed, and if you used email as your user provider property the email goes into the username argument.

Development Setup

A docker-compose.yml file with a PHP 8.1 image is included in this project. To install the dependencies you can run

docker compose up -d
docker compose exec phpfpm composer install

Unit Testing

A PhpUnit setup is included in this library. To run the unit tests:

docker compose exec phpfpm composer install
docker compose exec phpfpm ./vendor/bin/phpunit

Psalm static analysis

We’re using Psalm for static analysis. To run psalm do

docker compose exec phpfpm composer install
docker compose exec phpfpm ./vendor/bin/psalm

Check Coding Standard

The following command let you test that the code follows the coding standard for the project.

  • PHP files (PHP-CS-Fixer)

    docker compose exec phpfpm composer coding-standards-check
  • Markdown files (markdownlint standard rules)

    docker run --rm -v "$PWD":/usr/src/app -w /usr/src/app node:18 yarn install
    docker run --rm -v "$PWD":/usr/src/app -w /usr/src/app node:18 yarn coding-standards-check

Apply Coding Standards

To attempt to automatically fix coding style

  • PHP files (PHP-CS-Fixer)

    docker compose exec phpfpm composer coding-standards-apply
  • Markdown files (markdownlint standard rules)

    docker run --rm -v "$PWD":/usr/src/app -w /usr/src/app node:18 yarn install
    docker run --rm -v "$PWD":/usr/src/app -w /usr/src/app node:18 yarn coding-standards-apply


GitHub Actions are used to run the test suite and code style checks on all PRs.

If you wish to test against the jobs locally you can install act. Then do:

act -P ubuntu-latest=shivammathur/node:latest pull_request


We use SemVer for versioning. For the versions available, see the tags on this repository.


This project is licensed under the MIT License - see the file for details