From 06f20d2a4f3d9c399de8a31b897c1b653b48236d Mon Sep 17 00:00:00 2001 From: stan220 Date: Sat, 11 Jan 2020 18:50:04 +0300 Subject: [PATCH] Added Google auth. --- .env | 3 +- composer.json | 2 + composer.lock | 456 ++++++++++++++++++++++- config/bundles.php | 1 + config/packages/knpu_oauth2_client.yaml | 13 + config/packages/security.yaml | 27 +- config/routes.yaml | 4 + src/Controller/GoogleController.php | 40 ++ src/Entity/User.php | 176 +++++++++ src/Migrations/Version20200111120854.php | 35 ++ src/Repository/UserRepository.php | 50 +++ src/Security/GoogleAuthenticator.php | 129 +++++++ src/Security/UserProvider.php | 84 +++++ symfony.lock | 33 ++ templates/base.html.twig | 1 + templates/main.twig | 36 +- 16 files changed, 1074 insertions(+), 16 deletions(-) create mode 100644 config/packages/knpu_oauth2_client.yaml create mode 100644 src/Controller/GoogleController.php create mode 100644 src/Entity/User.php create mode 100644 src/Migrations/Version20200111120854.php create mode 100644 src/Repository/UserRepository.php create mode 100644 src/Security/GoogleAuthenticator.php create mode 100644 src/Security/UserProvider.php diff --git a/.env b/.env index b3ea64e..7581cf4 100644 --- a/.env +++ b/.env @@ -29,5 +29,6 @@ APP_SECRET=2a37fd338bcb9eeec4645dd50db620e0 # For an SQLite database, use: "sqlite:///%kernel.project_dir%/var/data.db" # For a PostgreSQL database, use: "postgresql://db_user:db_password@127.0.0.1:5432/db_name?serverVersion=11&charset=utf8" # IMPORTANT: You MUST configure your server version, either here or in config/packages/doctrine.yaml -DATABASE_URL=mysql://db_user:db_password@127.0.0.1:3306/db_name?serverVersion=5.7 +#DATABASE_URL=mysql://db_user:db_password@127.0.0.1:3306/db_name?serverVersion=5.7 ###< doctrine/doctrine-bundle ### + diff --git a/composer.json b/composer.json index ac74db4..0757b19 100644 --- a/composer.json +++ b/composer.json @@ -5,6 +5,8 @@ "php": "^7.1.3", "ext-ctype": "*", "ext-iconv": "*", + "knpuniversity/oauth2-client-bundle": "^1.32", + "league/oauth2-google": "^3.0", "sensio/framework-extra-bundle": "^5.5", "symfony/apache-pack": "^1.0", "symfony/asset": "4.4.*", diff --git a/composer.lock b/composer.lock index c5d87a5..1bfa45a 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "d6e8bc8b46d0e946a35999599cf79156", + "content-hash": "067b0e9ecd420b1b48945900bf4c8987", "packages": [ { "name": "doctrine/annotations", @@ -1204,6 +1204,195 @@ ], "time": "2020-01-05T14:11:20+00:00" }, + { + "name": "guzzlehttp/guzzle", + "version": "6.5.2", + "source": { + "type": "git", + "url": "https://github.com/guzzle/guzzle.git", + "reference": "43ece0e75098b7ecd8d13918293029e555a50f82" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/guzzle/zipball/43ece0e75098b7ecd8d13918293029e555a50f82", + "reference": "43ece0e75098b7ecd8d13918293029e555a50f82", + "shasum": "" + }, + "require": { + "ext-json": "*", + "guzzlehttp/promises": "^1.0", + "guzzlehttp/psr7": "^1.6.1", + "php": ">=5.5" + }, + "require-dev": { + "ext-curl": "*", + "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.4 || ^7.0", + "psr/log": "^1.1" + }, + "suggest": { + "ext-intl": "Required for Internationalized Domain Name (IDN) support", + "psr/log": "Required for using the Log middleware" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "6.5-dev" + } + }, + "autoload": { + "psr-4": { + "GuzzleHttp\\": "src/" + }, + "files": [ + "src/functions_include.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + } + ], + "description": "Guzzle is a PHP HTTP client library", + "homepage": "http://guzzlephp.org/", + "keywords": [ + "client", + "curl", + "framework", + "http", + "http client", + "rest", + "web service" + ], + "time": "2019-12-23T11:57:10+00:00" + }, + { + "name": "guzzlehttp/promises", + "version": "v1.3.1", + "source": { + "type": "git", + "url": "https://github.com/guzzle/promises.git", + "reference": "a59da6cf61d80060647ff4d3eb2c03a2bc694646" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/promises/zipball/a59da6cf61d80060647ff4d3eb2c03a2bc694646", + "reference": "a59da6cf61d80060647ff4d3eb2c03a2bc694646", + "shasum": "" + }, + "require": { + "php": ">=5.5.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.4-dev" + } + }, + "autoload": { + "psr-4": { + "GuzzleHttp\\Promise\\": "src/" + }, + "files": [ + "src/functions_include.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + } + ], + "description": "Guzzle promises library", + "keywords": [ + "promise" + ], + "time": "2016-12-20T10:07:11+00:00" + }, + { + "name": "guzzlehttp/psr7", + "version": "1.6.1", + "source": { + "type": "git", + "url": "https://github.com/guzzle/psr7.git", + "reference": "239400de7a173fe9901b9ac7c06497751f00727a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/psr7/zipball/239400de7a173fe9901b9ac7c06497751f00727a", + "reference": "239400de7a173fe9901b9ac7c06497751f00727a", + "shasum": "" + }, + "require": { + "php": ">=5.4.0", + "psr/http-message": "~1.0", + "ralouphie/getallheaders": "^2.0.5 || ^3.0.0" + }, + "provide": { + "psr/http-message-implementation": "1.0" + }, + "require-dev": { + "ext-zlib": "*", + "phpunit/phpunit": "~4.8.36 || ^5.7.27 || ^6.5.8" + }, + "suggest": { + "zendframework/zend-httphandlerrunner": "Emit PSR-7 responses" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.6-dev" + } + }, + "autoload": { + "psr-4": { + "GuzzleHttp\\Psr7\\": "src/" + }, + "files": [ + "src/functions_include.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "Tobias Schultze", + "homepage": "https://github.com/Tobion" + } + ], + "description": "PSR-7 message implementation that also provides common utility methods", + "keywords": [ + "http", + "message", + "psr-7", + "request", + "response", + "stream", + "uri", + "url" + ], + "time": "2019-07-01T23:21:34+00:00" + }, { "name": "jdorn/sql-formatter", "version": "v1.2.17", @@ -1254,6 +1443,181 @@ ], "time": "2014-01-12T16:20:24+00:00" }, + { + "name": "knpuniversity/oauth2-client-bundle", + "version": "v1.32.0", + "source": { + "type": "git", + "url": "https://github.com/knpuniversity/oauth2-client-bundle.git", + "reference": "825b0c4512e51615318c8fbd4a57c7e65a57216d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/knpuniversity/oauth2-client-bundle/zipball/825b0c4512e51615318c8fbd4a57c7e65a57216d", + "reference": "825b0c4512e51615318c8fbd4a57c7e65a57216d", + "shasum": "" + }, + "require": { + "league/oauth2-client": "^1.0|^2.0", + "php": "^7.1.3", + "symfony/dependency-injection": "^3.4|^4.0|^5.0", + "symfony/framework-bundle": "^3.4|^4.0|^5.0", + "symfony/http-foundation": "^3.4|^4.0|^5.0", + "symfony/routing": "^3.4|^4.0|^5.0" + }, + "require-dev": { + "league/oauth2-facebook": "^1.1|^2.0", + "phpspec/prophecy": "^1.8", + "phpstan/phpstan": "^0.11.16", + "symfony/phpunit-bridge": "^4.3|^5.0", + "symfony/security-guard": "^3.4|^4.0|^5.0", + "symfony/yaml": "^3.4|^4.0|^5.0" + }, + "suggest": { + "symfony/security-guard": "For integration with Symfony's Guard Security layer" + }, + "type": "symfony-bundle", + "autoload": { + "psr-4": { + "KnpU\\OAuth2ClientBundle\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ryan Weaver", + "email": "ryan@knpuniversity.com" + } + ], + "description": "Integration with league/oauth2-client to provide services", + "homepage": "http://knpuniversity.com", + "keywords": [ + "oauth", + "oauth2" + ], + "time": "2020-01-10T15:50:30+00:00" + }, + { + "name": "league/oauth2-client", + "version": "2.4.1", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/oauth2-client.git", + "reference": "cc114abc622a53af969e8664722e84ca36257530" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/oauth2-client/zipball/cc114abc622a53af969e8664722e84ca36257530", + "reference": "cc114abc622a53af969e8664722e84ca36257530", + "shasum": "" + }, + "require": { + "guzzlehttp/guzzle": "^6.0", + "paragonie/random_compat": "^1|^2|^9.99", + "php": "^5.6|^7.0" + }, + "require-dev": { + "eloquent/liberator": "^2.0", + "eloquent/phony-phpunit": "^1.0|^3.0", + "jakub-onderka/php-parallel-lint": "^0.9.2", + "phpunit/phpunit": "^5.7|^6.0", + "squizlabs/php_codesniffer": "^2.3|^3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-2.x": "2.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "League\\OAuth2\\Client\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Alex Bilbie", + "email": "hello@alexbilbie.com", + "homepage": "http://www.alexbilbie.com", + "role": "Developer" + }, + { + "name": "Woody Gilk", + "homepage": "https://github.com/shadowhand", + "role": "Contributor" + } + ], + "description": "OAuth 2.0 Client Library", + "keywords": [ + "Authentication", + "SSO", + "authorization", + "identity", + "idp", + "oauth", + "oauth2", + "single sign on" + ], + "time": "2018-11-22T18:33:57+00:00" + }, + { + "name": "league/oauth2-google", + "version": "3.0.2", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/oauth2-google.git", + "reference": "cfe9b0ae69eb5dbac64a282f04410b731f6b9c04" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/oauth2-google/zipball/cfe9b0ae69eb5dbac64a282f04410b731f6b9c04", + "reference": "cfe9b0ae69eb5dbac64a282f04410b731f6b9c04", + "shasum": "" + }, + "require": { + "league/oauth2-client": "^2.0" + }, + "require-dev": { + "eloquent/phony-phpunit": "^2.0", + "php-coveralls/php-coveralls": "^2.1", + "phpunit/phpunit": "^6.0", + "squizlabs/php_codesniffer": "^2.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "League\\OAuth2\\Client\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Woody Gilk", + "email": "woody.gilk@gmail.com", + "homepage": "http://shadowhand.me" + } + ], + "description": "Google OAuth 2.0 Client Provider for The PHP League OAuth2-Client", + "keywords": [ + "Authentication", + "authorization", + "client", + "google", + "oauth", + "oauth2" + ], + "time": "2019-11-16T14:11:06+00:00" + }, { "name": "monolog/monolog", "version": "1.25.3", @@ -1698,6 +2062,56 @@ ], "time": "2017-02-14T16:28:37+00:00" }, + { + "name": "psr/http-message", + "version": "1.0.1", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-message.git", + "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-message/zipball/f6561bf28d520154e4b0ec72be95418abe6d9363", + "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Message\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Common interface for HTTP messages", + "homepage": "https://github.com/php-fig/http-message", + "keywords": [ + "http", + "http-message", + "psr", + "psr-7", + "request", + "response" + ], + "time": "2016-08-06T14:39:51+00:00" + }, { "name": "psr/link", "version": "1.0.0", @@ -1794,6 +2208,46 @@ ], "time": "2019-11-01T11:05:21+00:00" }, + { + "name": "ralouphie/getallheaders", + "version": "3.0.3", + "source": { + "type": "git", + "url": "https://github.com/ralouphie/getallheaders.git", + "reference": "120b605dfeb996808c31b6477290a714d356e822" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ralouphie/getallheaders/zipball/120b605dfeb996808c31b6477290a714d356e822", + "reference": "120b605dfeb996808c31b6477290a714d356e822", + "shasum": "" + }, + "require": { + "php": ">=5.6" + }, + "require-dev": { + "php-coveralls/php-coveralls": "^2.1", + "phpunit/phpunit": "^5 || ^6.5" + }, + "type": "library", + "autoload": { + "files": [ + "src/getallheaders.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ralph Khattar", + "email": "ralph.khattar@gmail.com" + } + ], + "description": "A polyfill for getallheaders.", + "time": "2019-03-08T08:55:37+00:00" + }, { "name": "sensio/framework-extra-bundle", "version": "v5.5.3", diff --git a/config/bundles.php b/config/bundles.php index 7745edb..00950cb 100644 --- a/config/bundles.php +++ b/config/bundles.php @@ -12,4 +12,5 @@ return [ Symfony\Bundle\MonologBundle\MonologBundle::class => ['all' => true], Symfony\Bundle\DebugBundle\DebugBundle::class => ['dev' => true, 'test' => true], Symfony\Bundle\MakerBundle\MakerBundle::class => ['dev' => true], + KnpU\OAuth2ClientBundle\KnpUOAuth2ClientBundle::class => ['all' => true], ]; diff --git a/config/packages/knpu_oauth2_client.yaml b/config/packages/knpu_oauth2_client.yaml new file mode 100644 index 0000000..fce32af --- /dev/null +++ b/config/packages/knpu_oauth2_client.yaml @@ -0,0 +1,13 @@ +knpu_oauth2_client: + clients: + # configure your clients as described here: https://github.com/knpuniversity/oauth2-client-bundle#configuration + + # the key "google" can be anything, it + # will create a service: "knpu.oauth2.client.google" + google: + type: google + client_id: '%env(OAUTH_GOOGLE_ID)%' + client_secret: '%env(OAUTH_GOOGLE_SECRET)%' + # the route that you're redirected to after + redirect_route: connect_google_check + redirect_params: {} diff --git a/config/packages/security.yaml b/config/packages/security.yaml index ce69ba7..5b8d00e 100644 --- a/config/packages/security.yaml +++ b/config/packages/security.yaml @@ -1,22 +1,23 @@ security: # https://symfony.com/doc/current/security.html#where-do-users-come-from-user-providers + # https://symfony.com/doc/current/security.html#b-configuring-how-users-are-loaded providers: - in_memory: { memory: null } + my_provider: + entity: { class: App:User, property: username } + firewalls: + # disables authentication for assets and the profiler, adapt it according to your needs dev: pattern: ^/(_(profiler|wdt)|css|images|js)/ security: false + main: - anonymous: lazy + anonymous: ~ + logout: + path: /logout + target: / + logout_on_user_change: true - # activate different ways to authenticate - # https://symfony.com/doc/current/security.html#firewalls-authentication - - # https://symfony.com/doc/current/security/impersonating_user.html - # switch_user: true - - # 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: ^/admin, roles: ROLE_ADMIN } - # - { path: ^/profile, roles: ROLE_USER } + guard: + authenticators: + - App\Security\GoogleAuthenticator diff --git a/config/routes.yaml b/config/routes.yaml index c3283aa..e52801b 100644 --- a/config/routes.yaml +++ b/config/routes.yaml @@ -1,3 +1,7 @@ #index: # path: / # controller: App\Controller\DefaultController::index + +app_logout: + path: /logout + methods: GET diff --git a/src/Controller/GoogleController.php b/src/Controller/GoogleController.php new file mode 100644 index 0000000..498d8f7 --- /dev/null +++ b/src/Controller/GoogleController.php @@ -0,0 +1,40 @@ +getClient('google')->redirect(); + } + + /** + * Facebook redirects to back here afterwards + * + * @Route("/connect/google/check", name="connect_google_check") + * @param Request $request + * @return JsonResponse|\Symfony\Component\HttpFoundation\RedirectResponse + */ + public function connectCheckAction(Request $request) + { + if (!$this->getUser()) { + return new JsonResponse(['status' => false, 'message' => 'User not found!']); + } + + return $this->redirectToRoute('app_main_index'); + } +} \ No newline at end of file diff --git a/src/Entity/User.php b/src/Entity/User.php new file mode 100644 index 0000000..1a5080c --- /dev/null +++ b/src/Entity/User.php @@ -0,0 +1,176 @@ +id; + } + + public function getEmail(): ?string + { + return $this->email; + } + + public function setEmail(string $email): self + { + $this->email = $email; + + return $this; + } + + public function getName(): ?string + { + return $this->name; + } + + public function setName(string $name): self + { + $this->name = $name; + + return $this; + } + + public function getCreatedAt(): ?\DateTimeInterface + { + return $this->created_at; + } + + public function setCreatedAt(\DateTimeInterface $created_at): self + { + $this->created_at = $created_at; + + return $this; + } + + public function getAvatar(): ?string + { + return $this->avatar; + } + + public function setAvatar(?string $avatar): self + { + $this->avatar = $avatar; + + return $this; + } + + public function getGuid(): ?string + { + return $this->guid; + } + + public function setGuid(?string $guid): self + { + $this->guid = $guid; + + return $this; + } + + /** + * Returns the roles granted to the user. + * + * + * public function getRoles() + * { + * return array('ROLE_USER'); + * } + * + * + * Alternatively, the roles might be stored on a ``roles`` property, + * and populated in any number of different ways when the user object + * is created. + * + * @return (Role|string)[] The user roles + */ + public function getRoles() + { + return ['ROLE_USER']; + } + + /** + * Returns the password used to authenticate the user. + * + * This should be the encoded password. On authentication, a plain-text + * password will be salted, encoded, and then compared to this value. + * + * @return string The password + */ + public function getPassword() + { + return null; + } + + /** + * Returns the salt that was originally used to encode the password. + * + * This can return null if the password was not encoded using a salt. + * + * @return string|null The salt + */ + public function getSalt() + { + return null; + } + + /** + * Returns the username used to authenticate the user. + * + * @return string The username + */ + public function getUsername() + { + return $this->email; + } + + /** + * Removes sensitive data from the user. + * + * This is important if, at any given point, sensitive information like + * the plain-text password is stored on this object. + */ + public function eraseCredentials() + { + return null; + } +} diff --git a/src/Migrations/Version20200111120854.php b/src/Migrations/Version20200111120854.php new file mode 100644 index 0000000..46351a0 --- /dev/null +++ b/src/Migrations/Version20200111120854.php @@ -0,0 +1,35 @@ +abortIf($this->connection->getDatabasePlatform()->getName() !== 'mysql', 'Migration can only be executed safely on \'mysql\'.'); + + $this->addSql('CREATE TABLE user (id INT AUTO_INCREMENT NOT NULL, email VARCHAR(255) NOT NULL, name VARCHAR(20) NOT NULL, created_at DATETIME NOT NULL, avatar VARCHAR(255) DEFAULT NULL, guid VARCHAR(255) DEFAULT NULL, PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB'); + } + + public function down(Schema $schema) : void + { + // this down() migration is auto-generated, please modify it to your needs + $this->abortIf($this->connection->getDatabasePlatform()->getName() !== 'mysql', 'Migration can only be executed safely on \'mysql\'.'); + + $this->addSql('DROP TABLE user'); + } +} diff --git a/src/Repository/UserRepository.php b/src/Repository/UserRepository.php new file mode 100644 index 0000000..64f692f --- /dev/null +++ b/src/Repository/UserRepository.php @@ -0,0 +1,50 @@ +createQueryBuilder('u') + ->andWhere('u.exampleField = :val') + ->setParameter('val', $value) + ->orderBy('u.id', 'ASC') + ->setMaxResults(10) + ->getQuery() + ->getResult() + ; + } + */ + + /* + public function findOneBySomeField($value): ?User + { + return $this->createQueryBuilder('u') + ->andWhere('u.exampleField = :val') + ->setParameter('val', $value) + ->getQuery() + ->getOneOrNullResult() + ; + } + */ +} diff --git a/src/Security/GoogleAuthenticator.php b/src/Security/GoogleAuthenticator.php new file mode 100644 index 0000000..4b0a517 --- /dev/null +++ b/src/Security/GoogleAuthenticator.php @@ -0,0 +1,129 @@ +clientRegistry = $clientRegistry; + $this->em = $em; + } + + public function supports(Request $request) + { + return $request->getPathInfo() === '/connect/google/check' && $request->isMethod('GET'); + } + + public function getCredentials(Request $request) + { + return $this->fetchAccessToken($this->getGoogleClient()); + } + + public function getUser($credentials, UserProviderInterface $userProvider) + { + /** @var GoogleUser $googleUser */ + $googleUser = $this->getGoogleClient()->fetchUserFromToken($credentials); + + $email = $googleUser->getEmail(); + + $user = $this->em->getRepository('App:User')->findOneBy(['email' => $email]); + + if (!$user) { + $user = new User(); + $user->setEmail($googleUser->getEmail()); + $user->setName($googleUser->getName()); + $user->setAvatar($googleUser->getAvatar()); + $user->setGuid($googleUser->getId()); + $user->setCreatedAt(new \DateTime(date('Y-m-d H:i:s'))); + + $this->em->persist($user); + $this->em->flush(); + } + + return $user; + } + + /** + * @return \KnpU\OAuth2ClientBundle\Client\OAuth2Client + */ + private function getGoogleClient() + { + return $this->clientRegistry->getClient('google'); + } + + /** + * Returns a response that directs the user to authenticate. + * + * This is called when an anonymous request accesses a resource that + * requires authentication. The job of this method is to return some + * response that "helps" the user start into the authentication process. + * + * Examples: + * A) For a form login, you might redirect to the login page + * return new RedirectResponse('/login'); + * B) For an API token authentication system, you return a 401 response + * return new Response('Auth header required', 401); + * + * @param Request $request The request that resulted in an AuthenticationException + * @param \Symfony\Component\Security\Core\Exception\AuthenticationException $authException The exception that started the authentication process + * + * @return \Symfony\Component\HttpFoundation\Response + */ + public function start(Request $request, \Symfony\Component\Security\Core\Exception\AuthenticationException $authException = null) + { + return new RedirectResponse('/login'); + } + + /** + * Called when authentication executed, but failed (e.g. wrong username password). + * + * This should return the Response sent back to the user, like a + * RedirectResponse to the login page or a 403 response. + * + * If you return null, the request will continue, but the user will + * not be authenticated. This is probably not what you want to do. + * + * @param Request $request + * @param \Symfony\Component\Security\Core\Exception\AuthenticationException $exception + * + * @return \Symfony\Component\HttpFoundation\Response|null + */ + public function onAuthenticationFailure(Request $request, \Symfony\Component\Security\Core\Exception\AuthenticationException $exception) + { + // TODO: Implement onAuthenticationFailure() method. + } + + /** + * Called when authentication executed and was successful! + * + * This should return the Response sent back to the user, like a + * RedirectResponse to the last page they visited. + * + * If you return null, the current request will continue, and the user + * will be authenticated. This makes sense, for example, with an API. + * + * @param Request $request + * @param \Symfony\Component\Security\Core\Authentication\Token\TokenInterface $token + * @param string $providerKey The provider (i.e. firewall) key + * + * @return void + */ + public function onAuthenticationSuccess(Request $request, \Symfony\Component\Security\Core\Authentication\Token\TokenInterface $token, $providerKey) + { + // TODO: Implement onAuthenticationSuccess() method. + } +} diff --git a/src/Security/UserProvider.php b/src/Security/UserProvider.php new file mode 100644 index 0000000..5b925fd --- /dev/null +++ b/src/Security/UserProvider.php @@ -0,0 +1,84 @@ +entityManager = $entityManager; + } + + /** + * Loads the user for the given username. + * + * This method must throw UsernameNotFoundException if the user is not + * found. + * + * @param string $username The username + * + * @return UserInterface + * + * @throws \Doctrine\ORM\NonUniqueResultException + */ + public function loadUserByUsername($username) + { + return $this->entityManager->createQueryBuilder('u') + ->where('u.email = :email') + ->setParameter('email', $username) + ->getQuery() + ->getOneOrNullResult(); + } + + /** + * Refreshes the user. + * + * It is up to the implementation to decide if the user data should be + * totally reloaded (e.g. from the database), or if the UserInterface + * object can just be merged into some internal array of users / identity + * map. + * + * @param UserInterface $user + * @return UserInterface + * + */ + public function refreshUser(UserInterface $user) + { + if (!$user instanceof User) { + throw new UnsupportedUserException( + sprintf('Instances of "%s" are not supported.', get_class($user)) + ); + } + return $user; + } + + /** + * Whether this provider supports the given user class. + * + * @param string $class + * + * @return bool + */ + public function supportsClass($class) + { + return $class === 'App\Security\User'; + } +} diff --git a/symfony.lock b/symfony.lock index 4753282..fc32fcd 100644 --- a/symfony.lock +++ b/symfony.lock @@ -90,9 +90,36 @@ "egulias/email-validator": { "version": "2.1.14" }, + "guzzlehttp/guzzle": { + "version": "6.5.2" + }, + "guzzlehttp/promises": { + "version": "v1.3.1" + }, + "guzzlehttp/psr7": { + "version": "1.6.1" + }, "jdorn/sql-formatter": { "version": "v1.2.17" }, + "knpuniversity/oauth2-client-bundle": { + "version": "1.20", + "recipe": { + "repo": "github.com/symfony/recipes-contrib", + "branch": "master", + "version": "1.20", + "ref": "80acc9223a205cbf5d9828fd616c82a374d281dd" + }, + "files": [ + "config/packages/knpu_oauth2_client.yaml" + ] + }, + "league/oauth2-client": { + "version": "2.4.1" + }, + "league/oauth2-google": { + "version": "3.0.2" + }, "monolog/monolog": { "version": "1.25.3" }, @@ -123,12 +150,18 @@ "psr/container": { "version": "1.0.0" }, + "psr/http-message": { + "version": "1.0.1" + }, "psr/link": { "version": "1.0.0" }, "psr/log": { "version": "1.1.2" }, + "ralouphie/getallheaders": { + "version": "3.0.3" + }, "sensio/framework-extra-bundle": { "version": "5.2", "recipe": { diff --git a/templates/base.html.twig b/templates/base.html.twig index 043f42d..c61820a 100644 --- a/templates/base.html.twig +++ b/templates/base.html.twig @@ -3,6 +3,7 @@ {% block title %}Welcome!{% endblock %} + {% block stylesheets %}{% endblock %} diff --git a/templates/main.twig b/templates/main.twig index 469b510..ca09b5a 100644 --- a/templates/main.twig +++ b/templates/main.twig @@ -1,5 +1,39 @@ {% extends 'base.html.twig' %} {% block body %} -

Your lucky number is {{ number }}

+ +
+
+ +
+ +
+

Content

+ +

+ Content +

+
+ +

Your lucky number is {{ number }}

+ +
+ {% endblock %} \ No newline at end of file