Git Product home page Git Product logo

onelogin-saml-bundle's Introduction

OneloginSamlBundle

Latest Stable Version Latest Unstable Version Total Downloads License Gitter

PHP Version Require Codecov Audit

SymfonyInsight

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.

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 the Request 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
                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: '[email protected]'
                support:
                    givenName: 'Support User'
                    emailAddress: '[email protected]'
                administrative:
                    givenName: 'Administrative User'
                    emailAddress: '[email protected]'
            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 of Nbgrp\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). The SamlUserProvider does not throw this exception which will cause an empty user to be returned (if your user class not implements Nbgrp\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.

onelogin-saml-bundle's People

Contributors

a-menshchikov avatar dependabot[bot] avatar guillaume-ro-fr avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

onelogin-saml-bundle's Issues

Inject <saml:Subject><saml:NameID> into User-Entity

I'm using Wordpress as IDP.
The free version of the WP-Plugin (https://de.wordpress.org/plugins/miniorange-wp-as-saml-idp/) does not set any attributes.
The only property which is set in the response is in saml:Subject

Bildschirmfoto-2022-02-10-um-21 55 04

This value exists in OneLogin\Saml2\Auth $nameId, SamlAuthenticator can access this property.
But i don't see a way to inject that property into the UserEntity.

SamlAuthenticator cannot be replaced via DI, there is no Event.
Am I missing something? Is there a little hack?

By the way...
As i can see Nbgrp\OneloginSamlBundle\Security\Http\Authenticator\SamlAuthenticator::extractIdentifier() would return exact this value, if identifier_attribute is not set in options.

if (\array_key_exists('identifier_attribute', $this->options)) {

Problem: As i can see, it's not possible to delete "identifier_attribute" from $this->options via yaml. It's not possible to delete / unset keys with yaml.

ADFS Problem

We get an error, when using adfs server as IDP
Error details: Found invalid data while decoding.

What do we have to configure to get adfs server working?
Thanks for your helping.

No AuthNRequest sent from SP

I'm trying to get a docker network set up with an SP and IdP - my SP is a Symfony 6 app on localhost:8000, and the IdP is a docker image kenchan0130/simplesamlphp:develop - mapped to local-idp.local:4000, which is effectively a wrapper for SimpleSAMLphp.org codebase configured as an IdP.

When I go to the url on my SP that is marked as protected, I'm redirected to the IdP login page as expected, but there is no AuthNRequest SAML payload - is this by design?

Dynamic IDP configuration based on user's domain

Hello,

I am setting up SAML authentication in my Symfony application using the bundle. However, I need to configure the IDP dynamically based on the user's domain when logging in.

Currently, it seems that the bundle does not support this functionality, as the IDP configuration is defined in the config/packages/nbgrp_onelogin_saml.yaml configuration file that is loaded during initialization.
Is there another way to do this with the current bundle? Is there a function to inject a custom config?

Thank you in advance for your help.

JIT provisioning and persisting User doesn't change anything in the database

Hello !

So I'm having with using the JIT provisioning and the persist_user.

What I'm trying to achieve is that at every connexion of the user, if in the attributes "roles" something has changed, than it changes its roles into the database.

What my code looks like :

in the security.yaml ->

providers:
        saml_provider:
            ##  Basic provider instantiates a user with identifier and default roles
            entity:
                class: App\Entity\User
                property: email

...


main:
            pattern: ^/
            provider: saml_provider
            saml:
                ##  Match SAML attribute 'uid' with user identifier.
                ##  Otherwise, used \OneLogin\Saml2\Auth::getNameId() method by default.
                identifier_attribute: email
                check_path: saml_acs
                login_path: saml_login
                user_factory: saml_user_factory
                persist_user: true
            logout:
                path: saml_logout

in the services.yaml ->

services:
    saml_user_factory:
          class: App\Security\UserFactory

And in my UserFactory.php ->

 public function createUser(string $identifier, array $attributes): UserInterface
 {
        $user = new User();
        $user->setEmail($attributes['email'][0]);
        $user->setFirstname($attributes['firstname'][0]);
        $user->setLastname($attributes['lastname'][0]);
        $user->setTrigram($attributes['trigram'][0]);

        $rolesAttributes = $attributes['roles'];
        if ($_ENV['APP_ENV'] == 'prod') {
            $user->setRoles($this->setProdRoles($rolesAttributes));
        } else if ($_ENV['APP_ENV'] == 'dev') {
            $user->setRoles($this->setDevRoles($rolesAttributes));
        } else {
            $user->setRoles([]);
        }
        return $user;
}

And then for an exemple of the prod roles function ->

public function setProdRoles(array $rolesAttributes): array
    {
        $roles = [];
        if (in_array('roles1', $rolesAttributes)) {
            $roles[] = 'ROLE_USER';
        }
        if (in_array('roles2', $rolesAttributes)) {
            $roles[] = 'ROLE_TEST1';
        }
        if (in_array('roles3', $rolesAttributes)) {
            $roles[] = 'ROLE_TEST2';
        }
        return $roles;
    }

For the use case, what happens is that when a user load for the first time on my application, and in his attribute roles he has only "roles1", then he's going to have 'ROLE_USER' in the database.
But then, in the saml server we add the 'roles2', and what happens is that when he reconnects to the website, he doesn't have the 'ROLE_TEST1' in the database, even though we tried to delete cookies, etc... I have to manually change in the database...

How can i achieve this please ?

Nbgrp_onlelogin_saml.yaml and .env variables

Hello,

I have found a problem with the config file of this library.

The "binding" parameters don't seems to allow environnement variables.

Example :

nbgrp_onelogin_saml:
  use_proxy_vars: true
  onelogin_settings:
    default:
      # Basic settings
      idp:
        entityId: "%env(SAML_IDP_ENTITY_ID)%"
        singleSignOnService:
          url: "%env(SAML_SINGLE_SIGN_ON_SERVICE_URL)%"
          binding: "%env(SAML_SINGLE_SIGN_ON_SERVICE_BINDING)%"

The error we encounter :

Invalid configuration for path "nbgrp_onelogin_saml.onelogin_settings.default.idp.singleSignOnService.binding": invalid value.

How i fixed this error :

binding: "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"

This behavior is managed in src/DependencyInjection/Configuration.php :

->scalarNode('binding')
    ->validate()
        ->ifTrue(static fn ($value): bool => !str_starts_with($value, 'urn:oasis:names:tc:SAML:2.0:bindings:'))
        ->thenInvalid('invalid value.')
    ->end()
->end()

It seems like this value " %env()%" is not replaced before the call of validate() method.

Is it really mandatory to have this verification or is there another way to achieve the desired result ?

Thanks.

Configuration :

Php : 8.1.8
Symfony : 6.2
Nbgrp_login_saml : 1.3.2

Deprecation Note in Symfony 6.2

Using the current Symfony 6.2 release, I get the following deprecation warning:

The "Nbgrp\OneloginSamlBundle\Onelogin\AuthArgumentResolver" class implements "Symfony\Component\HttpKernel\Controller\ArgumentValueResolverInterface" that is deprecated since Symfony 6.2, implement ValueResolverInterface instead.

I would appreciate, if you would fix this. Thank you.

500 Error: Attribute not found, but present in SAML data

I have a Symfony 6.4 container and a boxy/mock-saml container configured in a docker compose file. I believe I have everything connected / configured properly - the containers start up, and when I go to the app home page (localhost:8000) I am redirected to the saml container's login (localhost:4000).

The Attributes configured in the AttributeStatement of the SAML data are minimal - id, email, firstName, lastName - I updated the security.yaml to use the id as the identifier:

security:
    firewalls:
        main:
            provider: saml_provider
            saml:
               identifier_attribute: id

However, after logging in to the SAML container, I'm redirected back to the app container on 8000, but an error is thrown saying that the id attribute cannot be found:

RuntimeException:
Attribute "id" not found in SAML data.

  at vendor/nbgrp/onelogin-saml-bundle/src/Security/Http/Authenticator/SamlAuthenticator.php:198
  at Nbgrp\OneloginSamlBundle\Security\Http\Authenticator\SamlAuthenticator->extractIdentifier()
     (vendor/nbgrp/onelogin-saml-bundle/src/Security/Http/Authenticator/SamlAuthenticator.php:145)
  at Nbgrp\OneloginSamlBundle\Security\Http\Authenticator\SamlAuthenticator->createPassport()
     (vendor/nbgrp/onelogin-saml-bundle/src/Security/Http/Authenticator/SamlAuthenticator.php:96)
  at Nbgrp\OneloginSamlBundle\Security\Http\Authenticator\SamlAuthenticator->authenticate()
     (vendor/symfony/security-http/Authenticator/Debug/TraceableAuthenticator.php:70)
  at Symfony\Component\Security\Http\Authenticator\Debug\TraceableAuthenticator->authenticate()
     (vendor/symfony/security-http/Authentication/AuthenticatorManager.php:176)
  at Symfony\Component\Security\Http\Authentication\AuthenticatorManager->executeAuthenticator()
     (vendor/symfony/security-http/Authentication/AuthenticatorManager.php:158)
  at Symfony\Component\Security\Http\Authentication\AuthenticatorManager->executeAuthenticators()
     (vendor/symfony/security-http/Authentication/AuthenticatorManager.php:140)
  at Symfony\Component\Security\Http\Authentication\AuthenticatorManager->authenticateRequest()
     (vendor/symfony/security-http/Firewall/AuthenticatorManagerListener.php:40)
  at Symfony\Component\Security\Http\Firewall\AuthenticatorManagerListener->authenticate()
     (vendor/symfony/security-http/Authenticator/Debug/TraceableAuthenticatorManagerListener.php:68)
  at Symfony\Component\Security\Http\Authenticator\Debug\TraceableAuthenticatorManagerListener->authenticate()
     (vendor/symfony/security-bundle/Debug/WrappedLazyListener.php:46)
  at Symfony\Bundle\SecurityBundle\Debug\WrappedLazyListener->authenticate()
     (vendor/symfony/security-http/Firewall/AbstractListener.php:26)
  at Symfony\Component\Security\Http\Firewall\AbstractListener->__invoke()
     (vendor/symfony/security-bundle/Debug/TraceableFirewallListener.php:83)
  at Symfony\Bundle\SecurityBundle\Debug\TraceableFirewallListener->callListeners()
     (vendor/symfony/security-http/Firewall.php:95)
  at Symfony\Component\Security\Http\Firewall->onKernelRequest()
     (vendor/symfony/event-dispatcher/Debug/WrappedListener.php:116)
  at Symfony\Component\EventDispatcher\Debug\WrappedListener->__invoke()
     (vendor/symfony/event-dispatcher/EventDispatcher.php:220)
  at Symfony\Component\EventDispatcher\EventDispatcher->callListeners()
     (vendor/symfony/event-dispatcher/EventDispatcher.php:56)
  at Symfony\Component\EventDispatcher\EventDispatcher->dispatch()
     (vendor/symfony/event-dispatcher/Debug/TraceableEventDispatcher.php:139)
  at Symfony\Component\EventDispatcher\Debug\TraceableEventDispatcher->dispatch()
     (vendor/symfony/http-kernel/HttpKernel.php:157)
  at Symfony\Component\HttpKernel\HttpKernel->handleRaw()
     (vendor/symfony/http-kernel/HttpKernel.php:76)
  at Symfony\Component\HttpKernel\HttpKernel->handle()
     (vendor/symfony/http-kernel/Kernel.php:197)
  at Symfony\Component\HttpKernel\Kernel->handle()
     (vendor/symfony/runtime/Runner/Symfony/HttpKernelRunner.php:35)
  at Symfony\Component\Runtime\Runner\Symfony\HttpKernelRunner->run()
     (vendor/autoload_runtime.php:29)
  at require_once('/var/www/html/vendor/autoload_runtime.php')
     (public/index.php:5)               

But inspecting the SAML data with the Chrome extensions clearly shows the id attribute present:

Screen Shot 2024-04-03 at 3 10 18 PM

Thoughts? work arounds?

(Is there something broken with that SAML XML structure?)

Misleading information about ONELOGIN_CUSTOMPATH

As of today it's impossible to configure custom path for certs because when this bundle is configuring \OneLogin\Saml2\Settings it's already passing $settings will all information resolved.

Workaround: use %env()% with cert-content.

Need to use urn:federation:authentication:windows for requestedAuthnContext

Hello,

Thanks for your work !

I need to specify the value 'urn:federation:authentication:windows' for requestedAuthnContext conffiguration, but there is configuration control with do not permit to use something diffrent from 'urn:oasis:names:tc:SAML:2.0:ac:classes:' (Configuration.php, line 169)

Why (It does not appear that php-saml lib is so restricted) ?
How can I fix this ?

->variableNode('requestedAuthnContext')
                                        ->validate()
                                            ->ifTrue(static fn ($value) => !(\is_bool($value) || \is_array($value)))
                                            ->thenInvalid('must be an array or a boolean.')
                                        ->end()
                                        ->validate()
                                            ->ifTrue(static fn ($value) => \is_array($value) && array_filter($value, static fn ($item): bool => !str_starts_with($item, 'urn:oasis:names:tc:SAML:2.0:ac:classes:')))
                                            ->thenInvalid('invalid value.')
                                        ->end()
                                    ->end()
`

Thanks for your reply.
N.B : using last release version (1.3.2)

After logging in via IdP 'authentication failed'

I'm trying to get a docker network set up with an SP and IdP - my SP is a Symfony 6 app on localhost:8000, and the IdP is a docker image kenchan0130/simplesamlphp:develop - mapped to local-idp.local:4000, which is effectively a wrapper for SimpleSAMLphp.org codebase configured as an IdP.

I believe I have everything set up properly - the SP's nbgrp_onelogin_saml.yaml is pretty much boilerplate with the IdP's domains as below:

 default:
            # Mandatory SAML settings
            idp:
                entityId: 'http://local-idp.local:4000/simplesaml/shib13/idp/metadata.php'
                singleSignOnService:
                    url: "http://local-idp.local:4000/simplesaml/saml2/idp/SSOService.php?spentityid=http://localhost:8000/saml/metadata"
                    binding: 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect'
                singleLogoutService:
                    url: 'http://local-idp.local:4000/simplesaml/saml2/idp/SingleLogoutService.php'
                    binding: 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect'
                x509cert: 'MIIDb...'
            sp:
                entityId: 'http://localhost:8000/saml/metadata'  #  Default: '<request_scheme_and_host>/saml/metadata'
                assertionConsumerService:
                    url: 'http://localhost:8000/saml/acs'  #  Default: '<request_scheme_and_host>/saml/acs'
                    binding: 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST'
                singleLogoutService:
                    url: 'http://localhost:8000/saml/logout'  #  Default: '<request_scheme_and_host>/saml/logout'
                    binding: 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect'
                privateKey: 'MIIEv...'
            # Optional SAML settings

The sp.privateKey and idp.x509cert values are directly out of the certificates in the IdP image.

This more or less works as expected - I have the /admin path set up to be SSO log in only in config/security.yaml, and if I go to localhost:8000/admin, I am redirected to the IdP site. I log in with the credentials set in the authsources.php file in the IdP config user the SimpleSaml docs, and am redirected back to the SP with a SAML payload, including the matching x509 cert value.

But when I get back to the SP, I get an 'authentication failed' exception error:

RuntimeException:
The authentication failed.

at vendor/nbgrp/onelogin-saml-bundle/src/Controller/Login.php:45
  at Nbgrp\OneloginSamlBundle\Controller\Login->__invoke()
     (vendor/symfony/http-kernel/HttpKernel.php:181)
  at Symfony\Component\HttpKernel\HttpKernel->handleRaw()
     (vendor/symfony/http-kernel/HttpKernel.php:76)
  at Symfony\Component\HttpKernel\HttpKernel->handle()
     (vendor/symfony/http-kernel/Kernel.php:197)
  at Symfony\Component\HttpKernel\Kernel->handle()
     (vendor/symfony/runtime/Runner/Symfony/HttpKernelRunner.php:35)
  at Symfony\Component\Runtime\Runner\Symfony\HttpKernelRunner->run()
     (vendor/autoload_runtime.php:29)
  at require_once('/var/www/html/vendor/autoload_runtime.php')
     (public/index.php:5)

Thoughts? happy to post more of the config or SAML payload if that's helpful.

Thx!

After upgrading, got a ServiceNotFoundException

Got this exception after upgrading my app to SF6 and using this bundle.
Symfony packages are updated and all the recipes too. Old hslavich bundle is removed.

The service "security.authenticator.saml.main" has a dependency on a non-existent service "".

My security.yaml is like following as exception refers to authenticator in "main" firewall:

security:
    enable_authenticator_manager: true
    # https://symfony.com/doc/current/security.html#registering-the-user-hashing-passwords
    password_hashers:
        Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface: 'auto'
    # https://symfony.com/doc/current/security.html#loading-the-user-the-user-provider
    providers:
        #users_in_memory: { memory: null }
        saml_provider:
            # Basic provider instantiates a user with default roles
            saml:
                user_class: 'App\Entity\User'
                default_roles: [ 'ROLE_USER' ]
    firewalls:
        dev:
            pattern: ^/(_(profiler|wdt)|css|images|js)/
            security: false
        main:
            pattern: ^/
            lazy: true
            provider: saml_provider

            # activate different ways to authenticate
            # https://symfony.com/doc/current/security.html#the-firewall

            # https://symfony.com/doc/current/security/impersonating_user.html
            # switch_user: true
            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: false
                check_path: saml_acs
                login_path: saml_login
            logout:
                path: saml_logout

    # Easy way to control access for large sections of your site
    # Note: Only the *first* access control that matches will be used
    access_control:
        - { path: ^/databases, roles: ROLE_ADMIN }
        #- { path: ^/rooms, roles: ROLE_ADMIN }
        - { path: ^/server, roles: ROLE_ADMIN }
        - { path: ^/audit, roles: ROLE_ADMIN }
        - { path: ^/saml/(metadata|login|acs), roles: PUBLIC_ACCESS }
        - { path: ^/, roles: ROLE_USER }

Failed to Load Resources

Hi,
There is an issue with the resources when routes.yaml file looks like this
nbgrp_saml: resource: "@NbgrpOneloginSamlBundle/Resources/config/routes.php"
I got this error
There is no extension able to load the configuration for "nbgrp_saml"

And when I change the bundle name like below
nbgrp_onelogin_saml: resource: "@NbgrpOneloginSamlBundle/Resources/config/routes.php"
There is another error
Unrecognized option "resource" under "nbgrp_onelogin_saml". Available options are "entity_manager_name", "idp_parameter_name", "onelogin_settings", "use_proxy_vars".

I also checked bundles.php and this line is added
Nbgrp\OneloginSamlBundle\NbgrpOneloginSamlBundle::class => ['all' => true],

Symfony 6.2.2
PHP 8.1.1

If someone can help, thank you a lot in advance!

Multiple idP: restrict email by format

Hi,

I'm using nbgrp/onelogin-saml-bundle with multiple idP.
Users are mapped and logged by email.

I would like to restrict sign in and login only for email like "@company-idp-name.com".

Could you please help me to do this ?

Thanks

Events not firing (UserCreatedEvent, UserUpdatedEvent)

Tried with Listener and Subscriber, but despite DeferredUserListener is calling dispatch nothing is kicking in.

# service.yaml
    App\EventListener\SamlUserCreatedListener:
        tags:
            - { name: kernel.event_listener, event: 'Nbgrp\OneloginSamlBundle\Event\UserCreatedEvent', method: onSamlUserCreated }

Subscriber:

class CustomEventSubscriber implements EventSubscriberInterface
{

  public function __construct(private LoggerInterface $logger)
  {
  }

  public static function getSubscribedEvents(): array
  {
    // return the subscribed events, their methods and priorities
    return [
      UserCreatedEvent::class => 'onCreated',
    ];
  }

  public function onCreated(UserCreatedEvent $event)
  {
    $this->logger->info($event->getUser()->getUserIdentifier());
  }
}

Not called listeners:

Nbgrp\OneloginSamlBundle\Event\UserCreatedEvent
--
0 | "App\EventListener\SamlUserCreatedListener::onSamlUserCreated(UserCreatedEvent $event)"
-1000 | "App\EventSubscriber\CustomEventSubscriber::onCreated(UserCreatedEvent $event)"

User provider: load by SAML attributes?

Hi and thanks for your work on this bundle!
I'm currently in the process of integrating it inside a new Symfony site.

In my use-case I don't want the user provider to create users on SAML success, but rather:

  • retrieve the SAML attributes
  • process them depending on the IdP
  • and determine the username/identifier based on these attributes and IdP.

I used to use aerialship/saml-sp-bundle which had a handy UserManagerInterface with loadUserBySamlInfo(SamlSpInfo $samlInfo): UserInterface. The user provider could implement this interface, and that method would be called on SAML auth success. See the docs for a little more details.

Note: It also has a createUserFromSamlInfo(SamlSpInfo $samlInfo): UserInterface but I'm currently not interested in this feature ๐Ÿ˜‰

Is something similar currently possible (maybe listening to an event)?
If not, do you think it could improve the bundle?

Thanks!

Multiple IDP use without specifying which one to use on idp side

Hello !

First of all, thank you very much for your work !

In my app I need to use multiple IDP settings, but I don't want to specify which one to use, I want it to be the role of the idp (Azure in my case)

My use case is :
The user comes to my app, he's redirected to login.microsoftonline.com, where he can connect with his e-mail address, then microsoft redirect him on his specific tenant according his e-mail.

Once he is logged in azure, it knows where to redirect him as I have configured it in each of my tenants (https://my-idp.com/acs?idp=correct-idp).

I don't think this is possible in the current state of your bundle, am I wrong ? It takes the default IDP setting if we do not specify which IDP setting to use when sending the first login request

Is it possible to set returnTo?

Is it possible to set returnTo parametr via $_GET or something like that?

I have url /saml/login. I want to redirect always from page, where I click on login button. I would like to set something like /saml/login?returnTo=/account/me and after login (or logout) get to page /account/me etc.

Symfony 7 support?

As this is mentioned to be for Symfony 6 and newer?

Maybe create just pre-release where composer constraints are updated so people can test themselves if this works?

Problem with the settings for Azure

Hello,

I am using onelogin-saml-bundle on symfony 6.
With google, everything work fine.
But I have issue on Microsoft Azure.
When my application launch the connection to azure, I receive the error AADSTS750055: SAML message was not properly DEFLATE-encoded.

Unlikely to google connection, I think Azure required the request to the idp to be encoded and deflated.
But i can't find the right settings to set.
In the azure xml metada, I have a digest value and a signature value.
But I don't know where to put them in my bundle setting.

Here is my setting:

nbgrp_onelogin_saml:
  onelogin_settings:
    default:
      # Mandatory SAML settings
      idp:
        entityId: "%env(SAML_ENTITY_ID)%"
        singleSignOnService:
          url: "%env(SAML_SSO_URI)%"
          binding: 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect'
        x509cert: "%env(SAML_CERT)%"
      sp:
        entityId: "%env(LIGHT__SAML_ENTITY__ID)%" 
        assertionConsumerService:
          url: '%env(LIGHT__SAML_ENTITY__ID)%/saml/acs' 
          binding: 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST'
        singleLogoutService:
          url: '%env(LIGHT__SAML_ENTITY__ID)%/logout'
          binding: 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect'
      # Optional SAML settings
      baseurl: '%env(LIGHT__SAML_ENTITY__ID)%/saml/' 
      strict: true
      debug: true
      security:
        nameIdEncrypted: false
        authnRequestsSigned: false
        logoutRequestSigned: false
        logoutResponseSigned: false
        signMetadata: false
        wantMessagesSigned: false
        wantAssertionsEncrypted: false
        wantAssertionsSigned: false
        wantNameId: false
        wantNameIdEncrypted: false
        requestedAuthnContext: true
        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
      compress:
        requests: false
        responses: false
  # Optional parameters
  use_proxy_vars: true
  idp_parameter_name: 'custom-idp'
  entity_manager_name: 'custom-em'

Can someone please help me ?

Marion

Need to disable onelogin behaviours

Our application is deployed for differents clients with differents need of configuration (including SSO activation).

There is no configuration attribute to disable the OneLogin Bundle functionnalities, so i tried to comment all configuration from nbgrp_onelogin_saml.yaml and even if it is not perfect, I can "hide" sso behaviours by this way... except on logout !

The SamlLogoutListener is registered on LogoutEvent.

#[AsEventListener(LogoutEvent::class)]
    public function processSingleLogout(LogoutEvent $event): void
    {
        $authService = $this->getAuthService($event->getRequest());
        if (!$authService) {
            return;
        }

        $token = $event->getToken();
        if (!$token instanceof SamlToken) {
            return;
        }

        try {
            $authService->processSLO();
        } catch (\OneLogin\Saml2\Error) {
            if (!empty($authService->getSLOurl())) {
                /** @var string|null $sessionIndex */
                $sessionIndex = $token->hasAttribute(SamlAuthenticator::SESSION_INDEX_ATTRIBUTE)
                    ? $token->getAttribute(SamlAuthenticator::SESSION_INDEX_ATTRIBUTE)
                    : null;
                $authService->logout(null, [], $token->getUserIdentifier(), $sessionIndex);
            }
        }
    }
`

And getAuthService throws an exception when there is no services configured ! But, if we take a look on the processSingleLogout function, there could be a small change on the code that can prevent unobvious exception, by testing $token before $authService.

```php
#[AsEventListener(LogoutEvent::class)]
    public function processSingleLogout(LogoutEvent $event): void
    {

        $token = $event->getToken();
        if (!$token instanceof SamlToken) {
            return;
        }

        $authService = $this->getAuthService($event->getRequest());
        if (!$authService) {
            return;
        }
`

What do you think about this change ? Could it be inplemented into a new version of onelogin-saml-bundle ?

And more globally, what do you think about manage a enable o disable attribute into configuration ?

Override reply (assertion customer service) doesn't work.

routes.php in this bundle use hard-coded paths. The bundle configuration allows us to specify an ACS URL, but this doesn't override the default /saml/acs route. I need this to be /saml2. How should I approach this problem?

Edit:

I've worked around the issue by manually defining routes (based on the ones from the bundle). I don't think this is a good solution though. In case routes get added or removed in the future, this would break future updates on my end.

saml_metadata:
    path: '/saml/metadata'
    controller: Nbgrp\OneloginSamlBundle\Controller\Metadata
    defaults: {'idp': ~}

saml_acs:
    path: '/saml2' # <--- This is the one I changed.
    controller: Nbgrp\OneloginSamlBundle\Controller\AssertionConsumerService
    defaults: {'idp': ~}
    methods: ['POST']

saml_login:
    path: '/saml/login'
    controller: Nbgrp\OneloginSamlBundle\Controller\Login
    defaults: { 'idp': ~ }

saml_logout:
    path: '/saml/logout'
    controller: Nbgrp\OneloginSamlBundle\Controller\Logout
    defaults: { 'idp': ~ }

Routes not working after fresh installation

Background

After upgrading from Symfony 5.4 to Symfony 6.3, I had to change from hslavich/oneloginsaml-bundle to nbgrp/onelogin-saml-bundle too. Once removed hslavich/oneloginsaml-bundle, I did a fresh installation of nbgrp/onelogin-saml-bundle.

Problem

The routes (like saml_login and saml_acs) coudn't be found.

Solution

I added this manually into /config/routes.yaml:

nbgrp_onelogin_saml:
    resource: "@NbgrpOneloginSamlBundle/Resources/config/routes.php"

Proposal:

I don't know if this is a bug, but it would be helpful to know, if it should be working like this. If yes, it would be beneficial to have this in your README.md-file. (and/or to fix the bug)

new user created at each connection

Hello !
I'm having an issue where each time I log into my application, a new entity is created and I would like to know how can I have just 1 entity user related to the identifier please.

For example, if I log in with the same credentials twice, than I'll have 2 entity of the same user in my database, though I'd like to only have one (that's the point).

If someone can help, thank you a lot in advance !

<request_scheme_and_host> with x-forwarded-prefix

Hi,

It seems that the <request_scheme_and_host> variable is not compliant with the "x-forwarded-prefix" proxy header.
Is it possible to implement it ? <request_scheme_and_host> is a must-have ๐Ÿ˜Š

Thanks

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.