{"id":13617170,"url":"https://github.com/symfonycorp/connect-bundle","last_synced_at":"2025-04-14T03:33:04.700Z","repository":{"id":6963159,"uuid":"8215596","full_name":"symfonycorp/connect-bundle","owner":"symfonycorp","description":null,"archived":true,"fork":false,"pushed_at":"2021-06-08T15:47:26.000Z","size":88,"stargazers_count":35,"open_issues_count":0,"forks_count":18,"subscribers_count":14,"default_branch":"master","last_synced_at":"2025-04-12T20:20:48.120Z","etag":null,"topics":["authentication","bundle","php","symfony","symfony-bundle"],"latest_commit_sha":null,"homepage":null,"language":"PHP","has_issues":false,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/symfonycorp.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2013-02-15T09:25:39.000Z","updated_at":"2023-05-31T13:55:35.000Z","dependencies_parsed_at":"2022-09-16T02:00:44.184Z","dependency_job_id":null,"html_url":"https://github.com/symfonycorp/connect-bundle","commit_stats":null,"previous_names":[],"tags_count":37,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/symfonycorp%2Fconnect-bundle","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/symfonycorp%2Fconnect-bundle/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/symfonycorp%2Fconnect-bundle/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/symfonycorp%2Fconnect-bundle/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/symfonycorp","download_url":"https://codeload.github.com/symfonycorp/connect-bundle/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248815758,"owners_count":21165977,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["authentication","bundle","php","symfony","symfony-bundle"],"created_at":"2024-08-01T20:01:37.754Z","updated_at":"2025-04-14T03:32:59.691Z","avatar_url":"https://github.com/symfonycorp.png","language":"PHP","funding_links":[],"categories":["PHP"],"sub_categories":[],"readme":"symfony/connect-bundle\n======================\n\nAbout\n-----\n\nThis is the official bundle of the [SymfonyConnect SDK](https://github.com/symfonycorp/connect).\n\nInstallation\n------------\n\n### Step 1: Install symfony/connect-bundle using [Composer](http://getcomposer.org)\n\n```bash\n$ composer require symfonycorp/connect-bundle\n```\n\nIf you're not using Symfony Flex, please take inspiration from\n[this bundle's recipe](https://github.com/symfony/recipes-contrib/blob/master/symfonycorp/connect-bundle/)\nto enable it.\n\n### Step 2: Configure your `.env.local` file\n\n```sh\nSYMFONY_CONNECT_APP_ID='Your app id'\nSYMFONY_CONNECT_APP_SECRET='Your app secret'\n```\n\nUsage\n-----\n\n### Use SymfonyConnect to authenticate your user\n\n#### Step 1: Configure the security\n\n\u003e **Note:** If you want to persist your users, read the *Cookbooks* section.\n\nIf you don't want to persist your users, you can use `ConnectInMemoryUserProvider`:\n\n```yaml\n# config/packages/security.yaml\nsecurity:\n    providers:\n        symfony_connect:\n            connect_memory: ~\n    firewalls:\n        # [...]\n\n        secured_area:\n            pattern: ^/\n            symfony_connect:\n                check_path: symfony_connect_callback\n                login_path: symfony_connect_login\n                failure_path: home # need to be adapted to your config, see step 4\n                remember_me: false\n                provider: symfony_connect\n            anonymous: true\n```\n\nYou can also load specific roles for some users:\n\n```yaml\n# config/packages/security.yaml\nsecurity:\n    providers:\n        symfony_connect:\n            connect_memory:\n                users:\n                    90f28e69-9ce9-4a42-8b0e-e8c7fcc27713: \"ROLE_CONNECT_USER ROLE_ADMIN\"\n```\n\n**Note:** The `username` is the user uuid.\n\n#### Step 2: Add some link to your templates\n\nYou can generate a link to the SymfonyConnect login page:\n\n```twig\n\u003ca href=\"{{ url('symfony_connect_login') }}\"\u003eConnect\u003c/a\u003e\n```\n\nYou can also specify the target URL after connection:\n\n```twig\n\u003ca href=\"{{ url('symfony_connect_login') }}?target=XXX\"\u003eConnect\u003c/a\u003e\n```\n\n#### Step 3: Play with the user\n\nThe API user is available through the token storage, which you can get by autowiring\n`Symfony\\Component\\Security\\Core\\Authentication\\Token\\Storage\\TokenStorageInterface $tokenStorage`.\n\n```php\n$user = $tokenStorage-\u003egetToken()-\u003egetApiUser();\n```\n\nIf you use the built-in security component, you can access to the root api\ndirectly by autowiring `SymfonyCorp\\Connect\\Api\\Api $api`:\n\n```\n$user = $api-\u003egetRoot()-\u003egetCurrentUser();\n```\n\nYou can also get access to the API root object by providing an access token explicitly:\n\n```php\n$accessToken = $tokenStorage-\u003egetToken()-\u003egetAccessToken();\n$api-\u003esetAccessToken($accessToken);\n$root = $api-\u003egetRoot();\n$user = $root-\u003egetCurrentUser();\n```\n\n#### Step 4: Handling Failures\n\nSeveral errors can occur during the OAuth dance, for example the user can\ndeny your application or the scope you defined in `symfony_connect.yaml` can be different\nfrom what you selected while creating your application on SymfonyConnect.\nTheses failures arehandled by the default Symfony failure handling.\n\nTherefore, if an error occurred, the error is stored in the session (with a\nfallback on query attributes) and the user is redirected to the route/path\nspecificed in `failure_path` node of the `symfony_connect` section of your\nfirewall in `security.yaml`.\n\n\u003e **Warning**: You **need** to specifiy `failure_path`. If you don't, the user\n\u003e will be redirected back to `login_path`, meaning that will launch the\n\u003e SymfonyConnect authentication and redirect the user to SymfonyConnect\n\u003e which can lead to a redirection loop.\n\nThis means you need to fetch the authentication error if there is one and display\nit in the view. This is similar to what you do for a typical login form on\nSymfony (here we assume you have a `home` route pointing to the following controller):\n\n```php\n// src/Controller/HomeController.php\n\nnamespace App\\Controller;\n\nuse Symfony\\Bundle\\FrameworkBundle\\Controller\\AbstractController;\nuse Symfony\\Component\\HttpFoundation\\Request;\nuse Symfony\\Component\\Routing\\Annotation\\Route;\nuse Symfony\\Component\\Security\\Core\\Security;\n\nclass HomeController extends AbstractController\n{\n    /**\n     * @Route(\"/\", name=\"home\")\n     */\n    public function home(Request $request)\n    {\n        $session = $request-\u003ehasSession() ? $request-\u003egetSession() : null;\n\n        // get the authentication error if there is one\n        if ($request-\u003eattributes-\u003ehas(Security::AUTHENTICATION_ERROR)) {\n            $error = $request-\u003eattributes-\u003eget(Security::AUTHENTICATION_ERROR);\n        } elseif (null !== $session \u0026\u0026 $session-\u003ehas(Security::AUTHENTICATION_ERROR)) {\n            $error = $session-\u003eget(Security::AUTHENTICATION_ERROR);\n            $session-\u003eremove(Security::AUTHENTICATION_ERROR);\n        } else {\n            $error = '';\n        }\n\n        return $this-\u003erender('home.html.twig', ['error' =\u003e $error]);\n    }\n}\n```\n\nAnd then adapt your twig template:\n\n```twig\n{# templates/home.html.twig #}\n\n{% if app.user %}\n    Congrats! You are authenticated with SymfonyConnect\n{% elseif error %}\n    {{ error.messageKey | trans(error.messageData, 'security') }}\n{% else %}\n    \u003ca href=\"{{ url('symfony_connect_login') }}\"\u003eLog in with SymfonyConnect\u003c/a\u003e\n{% endif %}\n```\n\nCookbooks\n---------\n\n### How to persist users\n\n#### Step 1 - Create a `User` entity\n\n```php\n\u003c?php\n\nnamespace App\\Entity;\n\nuse Doctrine\\ORM\\Mapping as ORM;\nuse SymfonyCorp\\Connect\\Api\\Entity\\User as ConnectApiUser;\nuse Symfony\\Component\\Security\\Core\\User\\UserInterface;\n\n/**\n * @ORM\\Table(name=\"user\")\n * @ORM\\Entity(repositoryClass=\"App\\Repository\\UserRepository\")\n */\nclass User implements UserInterface\n{\n    /** @ORM\\Column(type=\"integer\") @ORM\\Id @ORM\\GeneratedValue(strategy=\"AUTO\") */\n    private $id;\n\n    /** @ORM\\Column(type=\"string\", length=255) */\n    private $uuid;\n\n    /** @ORM\\Column(type=\"string\", length=255) */\n    private $username;\n\n    /** @ORM\\Column(type=\"string\", length=255) */\n    private $name;\n\n    public function __construct($uuid)\n    {\n        $this-\u003euuid = $uuid;\n    }\n\n    public function updateFromConnect(ConnectApiUser $apiUser)\n    {\n        $this-\u003eusername = $apiUser-\u003egetUsername();\n        $this-\u003ename = $apiUser-\u003egetName();\n    }\n\n    public function getUuid()\n    {\n        return $this-\u003euuid;\n    }\n\n    public function getUsername()\n    {\n        return $this-\u003eusername;\n    }\n\n    public function getName()\n    {\n        return $this-\u003ename;\n    }\n\n    public function getRoles()\n    {\n        return ['ROLE_USER'];\n    }\n\n    public function getPassword()\n    {\n    }\n\n    public function getSalt()\n    {\n    }\n\n    public function eraseCredentials()\n    {\n    }\n}\n```\n\n#### Step 2 - Create the repository\n\n```php\n\u003c?php\n\nnamespace App\\Repository;\n\nuse App\\Entity\\User;\nuse Doctrine\\Bundle\\DoctrineBundle\\Repository\\ServiceEntityRepository;\nuse Symfony\\Component\\Security\\Core\\Exception\\UnsupportedUserException;\nuse Symfony\\Component\\Security\\Core\\User\\UserInterface;\nuse Symfony\\Component\\Security\\Core\\User\\UserProviderInterface;\n\nclass UserRepository extends ServiceEntityRepository implements UserProviderInterface\n{\n    public function __construct(RegistryInterface $registry)\n    {\n        parent::__construct($registry, User::class);\n    }\n\n    public function loadUserByUsername($uuid)\n    {\n        return $this-\u003efindOneByUuid($uuid) ?: new User($uuid);\n    }\n\n    public function refreshUser(UserInterface $user)\n    {\n        if (!$user instanceof User) {\n            throw new UnsupportedUserException(sprintf('class %s is not supported', get_class($user)));\n        }\n\n        return $this-\u003eloadUserByUsername($user-\u003egetUuid());\n    }\n\n    public function supportsClass($class)\n    {\n        return User::class === $class;\n    }\n}\n```\n\nDon't forget to update your database.\n\n#### Step 3 - Create the event listener\n\n```php\n\u003c?php\n\nnamespace App\\EventListener;\n\nuse Doctrine\\ORM\\EntityManagerInterface;\nuse SymfonyCorp\\Connect\\Security\\Authentication\\Token\\ConnectToken;\nuse Symfony\\Component\\EventDispatcher\\EventSubscriberInterface;\nuse Symfony\\Component\\Security\\Http\\Event\\InteractiveLoginEvent;\nuse Symfony\\Component\\Security\\Http\\SecurityEvents;\n\nclass SecurityInteractiveLoginListener implements EventSubscriberInterface\n{\n    private $em;\n\n    public function __construct(EntityManagerInterface $em)\n    {\n        $this-\u003eem = $em;\n    }\n\n    public function registerUser(InteractiveLoginEvent $event)\n    {\n        $token = $event-\u003egetAuthenticationToken();\n\n        if (!$token instanceof ConnectToken) {\n            return;\n        }\n\n        $user = $token-\u003egetUser();\n        $user-\u003eupdateFromConnect($token-\u003egetApiUser());\n\n        $this-\u003eem-\u003epersist($user);\n        $this-\u003eem-\u003eflush($user);\n    }\n\n    public static function getSubscribedEvents()\n    {\n        return [\n            SecurityEvents::INTERACTIVE_LOGIN =\u003e 'registerUser',\n        ];\n    }\n}\n```\n\n#### Step 4 - Configure security\n\n```yaml\n# config/packages/security.yaml\nsecurity:\n    encoders:\n        App\\Entity\\User: plaintext\n\n    providers:\n        symfony_connect:\n            id: App\\Repository\\UserRepository\n```\n\n#### Step 5 - Enjoy\n\nYou can store more things if you want. But don't forget to update your\napplication scope.\n\nLicense\n-------\n\nThis bundle is licensed under the MIT license.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsymfonycorp%2Fconnect-bundle","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsymfonycorp%2Fconnect-bundle","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsymfonycorp%2Fconnect-bundle/lists"}