nbgrp / onelogin-saml-bundle
OneLogin SAML Symfony Bundle
Installs: 491 461
Dependents: 0
Suggesters: 0
Security: 0
Stars: 44
Watchers: 5
Forks: 16
Open Issues: 14
Type:symfony-bundle
Requires
- php: ^8.2
- onelogin/php-saml: ^4
- psr/log: ^1 || ^2 || ^3
- symfony/config: ^7
- symfony/dependency-injection: ^7
- symfony/deprecation-contracts: ^3
- symfony/event-dispatcher-contracts: ^3
- symfony/http-foundation: ^7
- symfony/http-kernel: ^7
- symfony/routing: ^7
- symfony/security-bundle: ^7
- symfony/security-core: ^7
- symfony/security-http: ^7
Requires (Dev)
- doctrine/orm: ^2.3 || ^3
- phpunit/phpunit: ^11.2
- symfony/event-dispatcher: ^7
- 2.0.x-dev
- v2.0.2
- v2.0.1
- v2.0.0
- 1.4.x-dev
- v1.4.0
- 1.3.x-dev
- v1.3.2
- v1.3.1
- v1.3.0
- 1.2.x-dev
- v1.2.4
- v1.2.3
- v1.2.2
- v1.2.1
- v1.2.0
- 1.1.x-dev
- v1.1.6
- v1.1.5
- v1.1.4
- v1.1.3
- v1.1.2
- v1.1.1
- v1.1.0
- 1.0.x-dev
- v1.0.9
- v1.0.8
- v1.0.7
- v1.0.6
- v1.0.5
- v1.0.4
- v1.0.3
- v1.0.2
- v1.0.1
- v1.0.0
- dev-dependabot/github_actions/codecov/codecov-action-4
- dev-dependabot/github_actions/actions/cache-4
This package is auto-updated.
Last update: 2024-10-31 22:51:01 UTC
README
Overview
OneLogin SAML Symfony Bundle.
This bundle depends on Symfony 6 and newer.
For older Symfony versions you can use hslavich/oneloginsaml-bundle which this bundle based on.
Compatibility
Installation
composer require nbgrp/onelogin-saml-bundle
If you use Symfony Flex it enables the bundle automatically. Otherwise, to enable the bundle add the
following code in config/bundles.php
:
return [ // ... Nbgrp\OneloginSamlBundle\NbgrpOneloginSamlBundle::class => ['all' => true], ];
Configuration
To configure the bundle you need to add configuration in config/packages/nbgrp_onelogin_saml.yaml
.
You can use any configuration format (yaml, xml, or php), but for convenience in this document will
be used yaml.
Check https://github.com/onelogin/php-saml#settings for more info about OneLogin PHP SAML settings.
You can use
<request_scheme_and_host>
placeholder in the following configuration values which will be replaced by the appropriate values from theRequest
object:
- onelogin_settings.sp.entityId
- onelogin_settings.sp.assertionConsumerService.url
- onelogin_settings.sp.singleLogoutService.url
- onelogin_settings.baseurl
Pay attention to trusted proxies settings if you're running your application behind a load balancer or a reverse proxy.
nbgrp_onelogin_saml: onelogin_settings: default: # Mandatory SAML settings idp: entityId: 'https://id.example.com/saml2/idp/metadata.php' singleSignOnService: url: 'https://id.example.com/saml2/idp/SSOService.php' binding: 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect' singleLogoutService: url: 'https://id.example.com/saml2/idp/SingleLogoutService.php' binding: 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect' x509cert: 'MIIC...' sp: entityId: 'https://myapp.com/saml/metadata' # Default: '<request_scheme_and_host>/saml/metadata' assertionConsumerService: url: 'https://myapp.com/saml/acs' # Default: '<request_scheme_and_host>/saml/acs' binding: 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST' singleLogoutService: url: 'https://myapp.com/saml/logout' # Default: '<request_scheme_and_host>/saml/logout' binding: 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect' privateKey: 'MIIE...' # Optional SAML settings baseurl: 'https://myapp.com/saml/' # Default: '<request_scheme_and_host>/saml/' strict: true debug: true security: nameIdEncrypted: false authnRequestsSigned: false logoutRequestSigned: false logoutResponseSigned: false signMetadata: false wantMessagesSigned: false wantAssertionsEncrypted: false wantAssertionsSigned: true wantNameId: false wantNameIdEncrypted: false requestedAuthnContext: true requestedAuthnContextComparison: 'exact' wantXMLValidation: false relaxDestinationValidation: false destinationStrictlyMatches: true allowRepeatAttributeName: false rejectUnsolicitedResponsesWithInResponseTo: false signatureAlgorithm: 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha256' digestAlgorithm: 'http://www.w3.org/2001/04/xmlenc#sha256' encryption_algorithm: 'http://www.w3.org/2001/04/xmlenc#aes256-cbc' lowercaseUrlencoding: false contactPerson: technical: givenName: 'Tech User' emailAddress: 'techuser@example.com' support: givenName: 'Support User' emailAddress: 'supportuser@example.com' administrative: givenName: 'Administrative User' emailAddress: 'administrativeuser@example.com' organization: en-US: name: 'Example' displayname: 'Example' url: 'http://example.com' compress: requests: false responses: false # Optional another one SAML settings (see Multiple IdP below) another: idp: # ... sp: # ... # ... # Optional parameters use_proxy_vars: true idp_parameter_name: 'custom-idp' entity_manager_name: 'custom-em'
There are few extra parameters for idp
and sp
sections. You can read more about them from
OneLogin PHP SAML docs.
Instead of specify IdP and SP x509 certificates and private keys, you can store them in OneLogin PHP
SAML certs directory or use global constant
ONELOGIN_CUSTOMPATH
to specify custom directory (complete path will be
ONELOGIN_CUSTOMPATH.'certs/'
).
If you do not want to set some contactPerson or organization info, do not add those parameters instead of leaving them blank.
Configure user provider and firewall in config/packages/security.yaml
:
security: # ... providers: saml_provider: ## Basic provider instantiates a user with identifier and default roles saml: user_class: 'App\Entity\User' default_roles: ['ROLE_USER'] firewalls: main: pattern: ^/ saml: ## Match SAML attribute 'uid' with user identifier. ## Otherwise, used \OneLogin\Saml2\Auth::getNameId() method by default. identifier_attribute: uid ## Use the attribute's friendlyName instead of the name. use_attribute_friendly_name: true check_path: saml_acs login_path: saml_login logout: path: saml_logout access_control: - { path: ^/saml/(metadata|login|acs), roles: PUBLIC_ACCESS } - { path: ^/, roles: ROLE_USER }
Edit your config/routes.yaml
:
nbgrp_saml: resource: "@NbgrpOneloginSamlBundle/Resources/config/routes.php"
Multiple IdP
You can configure more than one OneLogin PHP SAML settings for multiple IdP. To do this you need to
specify SAML settings for each IdP (sections with default
and another
keys in configuration
above) and pass the name of the necessary IdP by a query string parameter idp
or a request
attribute with the same name. You can use another name with help of idp_parameter_name
bundle
parameter.
To use appropriate SAML settings, all requests to bundle routes should contain correct IdP parameter.
If a request has no query parameter or attribute with IdP value, the first key
in onelogin_settings
section will be used as default IdP.
Using reverse proxy
When you use your application behind a reverse proxy and use X-Forwarded-*
headers, you need to
set parameter nbgrp_onelogin_saml.use_proxy_vars = true
to allow underlying OneLogin library
determine request protocol, host and port correctly.
Optional features
Inject SAML attributes into User object
To be able to inject SAML attributes into user object, you must implement SamlUserInterface
.
<?php namespace App\Entity; use Nbgrp\OneloginSamlBundle\Security\User\SamlUserInterface; class User implements SamlUserInterface { private $username; private $email; // ... public function setSamlAttributes(array $attributes) { $this->email = $attributes['mail'][0]; } }
In addition to injecting SAML attributes to user, you can get them by
getAttributes
method from current security token (that should be an instance ofNbgrp\OneloginSamlBundle\Security\Http\Authenticator\Token\SamlToken
).
Integration with classic login form
You can integrate SAML authentication with traditional login form by editing your security.yaml
:
security: enable_authenticator_manager: true providers: user_provider: # ... firewalls: main: saml: # ... ## Traditional login form form_login: login_path: /login check_path: /login_check # ... logout: path: saml_logout
Then you can add a link to route saml_login
in your login page in order to start SAML sign-on.
<a href="{{ path('saml_login') }}">SAML Login</a>
If you use multiple IdP, you should specify it by path
argument:
<a href="{{ path('saml_login', { idp: 'another' }) }}">SAML Login</a>
Just-in-time user provisioning
In order for a user to be provisioned, you must use a user provider that throws
UserNotFoundException
(e.g.EntityUserProvider
as used in the example above). TheSamlUserProvider
does not throw this exception which will cause an empty user to be returned (if your user class not implementsNbgrp\OneloginSamlBundle\Security\User\SamlUserInterface
).
It is possible to have a new user provisioned based on the received SAML attributes when the user provider cannot find a user.
Create the user factory service by editing services.yaml
:
services: saml_user_factory: class: Nbgrp\OneloginSamlBundle\Security\User\SamlUserFactory arguments: ## User class - App\Entity\User ## Attribute mapping - password: 'notused' email: $mail name: $cn lastname: $sn roles: ['ROLE_USER'] groups: $groups[]
Mapping items with '$' at the beginning of values references to SAML attribute value.
Values with '[]' at the end will be presented as arrays (even if they originally are scalars).
Then add the created service id as the user_factory
parameter into your firewall settings in
security.yaml
:
security: # ... providers: saml_provider: ## Loads user from user repository entity: class: App\Entity\User property: username firewalls: main: provider: saml_provider saml: identifier_attribute: uid ## User factory service user_factory: saml_user_factory # ...
Also, you can create your own User Factory that implements
Nbgrp\OneloginSamlBundle\Security\User\SamlUserFactoryInterface
.
<?php namespace App\Security; use App\Entity\User; use Nbgrp\OneloginSamlBundle\Security\User\SamlUserFactoryInterface; use Symfony\Component\Security\Core\User\UserInterface; class UserFactory implements SamlUserFactoryInterface { public function createUser(string $identifier, array $attributes): UserInterface { $user = new User(); $user->setRoles(['ROLE_USER']); $user->setUsername($username); $user->setEmail($attributes['mail'][0]); // ... return $user; } }
And add it into services.yaml
:
services: saml_user_factory: class: App\Security\UserFactory
Persist user on creation and SAML attributes injection
Symfony EventDispatcher component and Doctrine ORM are required.
If you want to persist user object after success authentication, you need to add persist_user
in you firewall settings in security.yaml
:
security: # ... firewalls: # ... main: saml: # ... persist_user: true
To use non-default entity manager, specify its name in the nbgrp_onelogin_saml.entity_manager_name
bundle configuration parameter.
User persistence is performing by the event
listeners Nbgrp\OneloginSamlBundle\EventListener\User\UserCreatedListener
and Nbgrp\OneloginSamlBundle\EventListener\User\UserModifiedListener
that can be decorated if you
need to override the default behavior.
Also, you can make your own listeners for Nbgrp\OneloginSamlBundle\Event\UserCreatedEvent
and Nbgrp\OneloginSamlBundle\Event\UserModifiedEvent
events:
<?php namespace App\Security; use Nbgrp\OneloginSamlBundle\Event\UserCreatedEvent; use Symfony\Component\EventDispatcher\Attribute\AsEventListener; #[AsEventListener(event: UserCreatedEvent::class, dispatcher: 'security.event_dispatcher.main')] class UserCreatedListener { public function __invoke(UserCreatedEvent $event): void { // ... } }
Important: you must specify the dispatcher
option corresponding the firewall which will
trigger the event (main
in the example above). Read more
about Security Events.