Jednoduché přihlašování přes Google

- Tečník
 - Člen | 18
 
Hledám nějaké jednoduché řešení, které bude schopen rozchodit i začátečník. Ideálně i s příkladem.
Našel jsem https://github.com/Kdyby/Google https://github.com/…social-login a https://github.com/martenb/google.
Šel jsem podle dokumentace a postupně řešil chyby, které vyskakovaly, ale nepodařilo se mi žádný z nich rozchodit.

- jiri.pudil
 - Nette Blogger | 1034
 
Používám napřímo league/oauth2-google, zhruba takto:
parameters:
	google:
		clientId: # fill in config.local.neon
		clientSecret: # fill in config.local.neon
services:
	oauth2.google.factory:
		create: MyApp\Google\Login\GoogleProviderFactory(%google.clientId%, %google.clientSecret%)
		autowired: false
	oauth2.google:
		type: League\OAuth2\Client\Provider\Google
		create: @oauth2.google.factory::create()
namespace MyApp\Google\Login;
use League\OAuth2\Client\Provider\Google;
use Nette\Application\LinkGenerator;
final class GoogleProviderFactory
{
	/** @var string */
	private $clientId;
	/** @var string */
	private $clientSecret;
	/** @var LinkGenerator */
	private $linkGenerator;
	public function __construct(
		string $clientId,
		string $clientSecret,
		LinkGenerator $linkGenerator
	)
	{
		$this->clientId = $clientId;
		$this->clientSecret = $clientSecret;
		$this->linkGenerator = $linkGenerator;
	}
	public function create(): Google
	{
		return new Google([
			'clientId' => $this->clientId,
			'clientSecret' => $this->clientSecret,
			'redirectUri' => $this->linkGenerator->link('Front:Login:google'),
		]);
	}
}
namespace MyApp\FrontModule;
use League\OAuth2\Client\Provider\Google;
use League\OAuth2\Client\Provider\GoogleUser;
use Nette\Application\UI\Presenter;
class LoginPresenter extends Presenter
{
	/** @var Google */
	private $google;
	public function handleGoogleLogin(): void
	{
		$authorizationUrl = $this->google->getAuthorizationUrl([
			'redirect_uri' => $this->link('//google'),
		]);
		$this->getSession(Google::class)->state = $this->google->getState();
		$this->redirectUrl($authorizationUrl);
	}
	public function actionGoogle(): void
	{
		$error = $this->getParameter('error');
		if ($error !== null) {
			$this->flashMessage('... google login error ...', 'error');
			$this->redirect('default');
		}
		$state = $this->getParameter('state');
		$stateInSession = $this->getSession(Google::class)->state;
		if ($state === null || $stateInSession === null || ! \hash_equals($stateInSession, $state)) {
			$this->flashMessage('... invalid CSRF token ...', 'error');
			$this->redirect('default');
		}
		// reset CSRF protection, it has done its job
		unset($this->getSession(Google::class)->state);
		$accessToken = $this->google->getAccessToken('authorization_code', [
			'code' => $this->getParameter('code'),
			'redirect_uri' => $this->link('//google'),
		]);
		try {
			/** @var GoogleUser $googleUser */
			$googleUser = $this->google->getResourceOwner($accessToken);
		} catch (\Throwable $e) {
			$this->flashMessage('... cannot retrieve user profile ...', 'error');
			$this->redirect('default');
		}
		$googleId = $googleUser->getId();
		if ($user = $this->userRepository->findByGoogleId($googleId)) {
			// found existing user by googleId, login and redirect
			$this->user->login($user);
			$this->redirect('Homepage:');
		}
		$googleEmail = $googleUser->getEmail();
		if ($user = $this->userRepository->findByEmail($googleEmail)) {
			// found existing user with the same email, error and force them to login using password
			$this->flashMessage('... somebody already signed up with given email ...', 'error');
			$this->redirect('default');
		}
		// new user, register them, login and redirect
		$user = $this->userRepository->registerFromGoogle($googleUser);
		$this->user->login($user);
		$this->redirect('Homepage:');
	}
}
Je potřeba dávat si pozor hlavně na to, abys měl u své aplikace
v Google Developer Console nastavené všechny možné správné
redirect_uris, Google je ohledně nich hodně striktní.

- jiri.pudil
 - Nette Blogger | 1034
 
@MajklNajt heslo tady neřeším a IAuthenticator vůbec
neimplementuji; do Nette\Security\User::login() lze předat rovnou
identitu (kterou mi implementuje entita uživatele) a v takovém případě už
se
authenticator nevolá.

- Tečník
 - Člen | 18
 
Děkuji za odpověď a velice se omlouvám, ale jsem skutečně začátečník a přiloženým souborům rozumím ještě méně než těm, které jsou v dokumentaci řešení, která jsem zmínil.
Budu rád, když mi poradíte blíže, co s nimi, nebo ještě lépe, kdyby někdo měl funkční příklad, který bych si u sebe rozbalil a mohl se podívat, co kde je.
Podařilo se mi pomocí composeru nainstalovat league/oauth2-google a mám i potřebné hodnoty clientId a clientSecret.
Pochopil jsem, co mám přidat do config.neon, bohužel ale nevím, kam nahrát druhý soubor s továrnou a pod jakým názvem a jak pak upravit cestu k němu v nastavení služby.
Chápu, že druhý soubor je presenter, který se bude používat, ale bohužel nevím, jak jej pak použít v šabloně. A nevím, co přesně dělá handleGoogleLogin().

- CZechBoY
 - Člen | 3608
 
Soubory umísti tam kde odpovídá cesta namespace – tzn. Factory umísti
do App/Google/Login/GoogleProviderFactory.php a presenter třeba do
App/Presenters.
Vesměs je jedno kam ty soubory umístíš, RobotLoader si je najde pod složkou
App kdekoliv.
Celý tohle bys měl v šabloně použít asi takto (v Login šabloně):
<a n:href="googleLogin!">Přihlásit přes Google</a>
				
- jiri.pudil
 - Nette Blogger | 1034
 
Nechybí tam někde nějaká část, kde se ta proměnná $google inicializuje?
Omlouvám se, to jsem v rámci zjednodušení příkladu vynechal.
Nejsnáze úpravou atributu v presenteru takto:
/** @var Google @inject */
public $google;
a nejčistěji vyžádáním v konstruktoru presenteru:
/** @var Google */
private $google;
public function __construct(Google $google)
{
	$this->google = $google;
}
V obou případech se o dosazení správné instance postará DI kontejner.
Editoval jiri.pudil (28. 2. 2019 14:52)

- Tečník
 - Člen | 18
 
Pokouším se použít přihlašování ze sandboxu a umožnit uživateli
registrovat/přihlásit se přes heslo nebo přes google.
Ale bohužel moc nevím, kudy na to.
Začal jsem tím, že jsem tabulku rozšířil o googleId a do třídy UserManager ze sandboxu přidal metodu registerFromGoogle() a ještě to bude chtít findByGoogleId() a findByEmail(). To by neměl být problém.
Otázka ale je, jak napsat dva různé autentikátory.
- Upravit metodu authenticate ze stávajícího UserManager.php tak, aby uměla reagovat na heslo i gogleId?
 - Nebo všechno rozdělit na dvě části (UserManager a GoogleUserManager) a pouštět přihlášení podobně, jako je to popsáno v dokumentaci?
 
Nebo na to jdu úplně špatně?

- unjustolaf
 - Člen | 29
 
@jiripudil
Ahoj, parádní tutoriál ale mám jednu chybu se kterou si nevím rady, při zaregistrování komponenty v configu mi vyskakuje error „Service ‚oauth2.google‘: Unknown or deprecated key ‚type‘ in definition of service.“ zkoušel jsem už všechno a nedokázal jsem na to přijít, nějaké nápady co by mohlo být špatně?
Předem děkuji.
Jakub

- MajklNajt
 - Člen | 518
 
@Tečník o tomto bol práve môj príspevok – na prihlasovanie
cez google nepotrebuješ vôbec authenticator, túto funkciu za teba plní
google – z managera si vrátiš už rovno entitu (ktorá implementuje
IIdentity), a metóde Nette\Security\User::login()
dáš už len túto entitou, kedy sa authenticator obíde

- jiri.pudil
 - Nette Blogger | 1034
 
@unjustolaf @CZechBoY class je myslím deprecated,
type ho nahrazuje, ale až od nette/di ^2.4.10
@Tečník v tom mém řešení právě žádný speciální
autentikátor pro Google není potřeba. Souvisí to s tím, co jsme tu
řešili s @MajklNajt – dole se volá
$this->user->login($user), tj. do login metody předávám
rovnou nějakou svou vlastní implementaci IIdentity. V tom
případě Nette danou identitu rovnou přihlásí a autentikátor už se
nevolá.

- cvit84
 - Člen | 45
 
Zdravím.
Používám league/oauth2-google, a píše mi to: „1 passed to App\Models\Googleaccount\GoogleButton::__construct() must be of the type string, null given, called in C:\xampp\htdocs\nalezce\temp\cache\nette.configurator\Container_406aa2b1f1.php on line 615 search►“
nevím co je špatně. Myslel jsem si že stačí když budu přihlašeny na google a po kliknutí na handle to se údaje uloží. Ale píše mi to že je parametr nula. A já nevím jak tam mám dostat ty vstupní data. Může mi prosím někdo pomoct. Děkuji Vítek

- cvit84
 - Člen | 45
 
Zdravím. nedaří se mi uložit data z google učtu. Hlasí mi to:
Argument 1 passed to Nette\Database\Table\Selection::insert() must be iterable, object given, called in /var/www/site-local/nalezce.cz/app/Models/Db/AbstractTable.php on line 54
…/site-local/nalezce.cz/app/Models/Db/AbstractTable.php:54
44: public function save($data, $id = null) {
45: if (is_null($id)) {
46: $this->findAll()->insert($data);
47: } else {
48: $this->findAll()->where($this::ID_COLUMN_NAME,
$id)->update($data);
49: }
50: }
51:
52: public function saveg($data) {
53:
54: $this->findAll()->insert($data);
55:
56: }
57:
58: public function saveAll($data) {
$data
League\OAuth2\Client\Provider\GoogleUser
response: array
‚sub‘ ⇒ ‚116217675099718320994‘
‚name‘ ⇒ ‚Vít Cigánek‘
‚given_name‘ ⇒ ‚Vít‘
‚family_name‘ ⇒ ‚Cigánek‘
‚picture‘ ⇒
‚https://lh3.googleusercontent.com/a/AATXAJycOoEzCl9l5RR9KCkpUxtXeW48yQiV6keC4G8p=s96-c‘
‚email‘ ⇒ ‚ciganekv84@gmail.com'
'email_verified‘ ⇒ true
‚locale‘ ⇒ ‚cs‘
…/www/site-local/nalezce.cz/app/Models/Db/DbUserg.php:27
…/site-local/nalezce.cz/app/Presenters/GooglePresenter.php:74
inner-code
App\Presenters\GooglePresenter::actionGoogle ()
…/nette/application/src/Application/UI/Component.php:120
…/nette/application/src/Application/UI/Presenter.php:216
…/vendor/nette/application/src/Application/Application.php:163
…/vendor/nette/application/src/Application/Application.php:90
/var/www/site-local/nalezce.cz/www/index.php:10
Nevím jak uložit pole. Podle mě to chce object. Ale v parametru mam pole. Prosím o radu.

- petr.pavel
 - Člen | 535
 
Jak praví chybová hláška, předáváš objekt, akceptuje to pole.
Když se podíváš do definice GoogleUser, najdeš metodu
toArray().
https://github.com/…ogleUser.php#L5