diff --git a/composer.json b/composer.json index 83847ded..3f86b8b9 100644 --- a/composer.json +++ b/composer.json @@ -4,7 +4,10 @@ "onelogin/php-saml": "^3", "apereo/phpcas": "^1", "league/oauth2-client": "^2", - "psr/log": "^1" + "psr/log": "^1", + "vertisan/oauth2-twitch-helix": "^2.0", + "league/oauth2-google": "^4.0", + "league/oauth2-github": "^3.1" }, "replace": { "psr/container": "*", diff --git a/composer.lock b/composer.lock index 2dbca8d3..aa33b0b5 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": "19f9ec63bb47c63141499bf4c7d593f1", + "content-hash": "68acfc6b8f4014c56ff44220b1b07e88", "packages": [ { "name": "apereo/phpcas", @@ -472,6 +472,127 @@ }, "time": "2023-04-16T18:19:15+00:00" }, + { + "name": "league/oauth2-github", + "version": "3.1.0", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/oauth2-github.git", + "reference": "97f31cd70e76f81e8f5b4e2ab6f3708e2db7ac18" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/oauth2-github/zipball/97f31cd70e76f81e8f5b4e2ab6f3708e2db7ac18", + "reference": "97f31cd70e76f81e8f5b4e2ab6f3708e2db7ac18", + "shasum": "" + }, + "require": { + "ext-json": "*", + "league/oauth2-client": "^2.0", + "php": "^7.3 || ^8.0" + }, + "require-dev": { + "mockery/mockery": "^1.4", + "phpunit/phpunit": "^9.5", + "squizlabs/php_codesniffer": "^3.6" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "League\\OAuth2\\Client\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Steven Maguire", + "email": "stevenmaguire@gmail.com", + "homepage": "https://github.com/stevenmaguire" + }, + { + "name": "Woody Gilk", + "email": "woody.gilk@gmail.com", + "homepage": "https://github.com/shadowhand" + } + ], + "description": "Github OAuth 2.0 Client Provider for The PHP League OAuth2-Client", + "keywords": [ + "authorisation", + "authorization", + "client", + "github", + "oauth", + "oauth2" + ], + "support": { + "issues": "https://github.com/thephpleague/oauth2-github/issues", + "source": "https://github.com/thephpleague/oauth2-github/tree/3.1.0" + }, + "time": "2022-11-04T14:01:49+00:00" + }, + { + "name": "league/oauth2-google", + "version": "4.0.1", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/oauth2-google.git", + "reference": "1b01ba18ba31b29e88771e3e0979e5c91d4afe76" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/oauth2-google/zipball/1b01ba18ba31b29e88771e3e0979e5c91d4afe76", + "reference": "1b01ba18ba31b29e88771e3e0979e5c91d4afe76", + "shasum": "" + }, + "require": { + "league/oauth2-client": "^2.0", + "php": "^7.3 || ^8.0" + }, + "require-dev": { + "eloquent/phony-phpunit": "^6.0 || ^7.1", + "phpunit/phpunit": "^8.0 || ^9.0", + "squizlabs/php_codesniffer": "^3.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "League\\OAuth2\\Client\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Woody Gilk", + "email": "hello@shadowhand.com", + "homepage": "https://shadowhand.com" + } + ], + "description": "Google OAuth 2.0 Client Provider for The PHP League OAuth2-Client", + "keywords": [ + "Authentication", + "authorization", + "client", + "google", + "oauth", + "oauth2" + ], + "support": { + "issues": "https://github.com/thephpleague/oauth2-google/issues", + "source": "https://github.com/thephpleague/oauth2-google/tree/4.0.1" + }, + "time": "2023-03-17T15:20:52+00:00" + }, { "name": "onelogin/php-saml", "version": "3.6.1", @@ -836,6 +957,63 @@ } ], "time": "2023-05-23T14:45:45+00:00" + }, + { + "name": "vertisan/oauth2-twitch-helix", + "version": "2.0.0", + "source": { + "type": "git", + "url": "https://github.com/vertisan/oauth2-twitch-helix.git", + "reference": "08d997ec0d2e1caf7bcb12ac820734d9c8a810a9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/vertisan/oauth2-twitch-helix/zipball/08d997ec0d2e1caf7bcb12ac820734d9c8a810a9", + "reference": "08d997ec0d2e1caf7bcb12ac820734d9c8a810a9", + "shasum": "" + }, + "require": { + "league/oauth2-client": "^2.2.1", + "php": ">=7.1" + }, + "require-dev": { + "ext-json": "*", + "jakub-onderka/php-parallel-lint": "^1.0", + "mockery/mockery": "^1.2", + "phpunit/phpunit": "^10.0.5", + "squizlabs/php_codesniffer": "^3.4" + }, + "type": "library", + "autoload": { + "psr-4": { + "Vertisan\\OAuth2\\Client\\Provider\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Paweł Farys", + "email": "pmg.farys@gmail.com", + "homepage": "https://github.com/vertisan" + } + ], + "description": "Twitch (new version Helix) OAuth 2.0 Client Provider for The PHP League OAuth2-Client", + "keywords": [ + "client", + "helix", + "league", + "oauth", + "package", + "twitch" + ], + "support": { + "issues": "https://github.com/vertisan/oauth2-twitch-helix/issues", + "source": "https://github.com/vertisan/oauth2-twitch-helix/tree/2.0.0" + }, + "time": "2023-06-19T21:11:30+00:00" } ], "packages-dev": [ diff --git a/docs/08_extern_auth.md b/docs/08_extern_auth.md index 5e828058..390bc84f 100644 --- a/docs/08_extern_auth.md +++ b/docs/08_extern_auth.md @@ -65,6 +65,99 @@ rex_extension::register('YCOM_AUTH_SAML_MATCHING', function (rex_extension_point ## OAuth2 +## OAuth2 mit twitch + +Mit der OAuth2 Authentifizierung via twitch ist es möglich, sich mit einem twitch-Account in der YCOM zu registrieren und einzuloggen. +Dazu muss dieser Provider entsprechend vorbereitet sein. + +### Einrichtung + +Im ersten Schritt muss man eine App anlegen bei twitch. Hierfür einmal zu https://dev.twitch.tv/console/apps wechseln. Dort über den Button "Deine Anwendung registrieren" eine neue App erstellen - Kategorie "Website integration" auswählen. + +Anschließend die App bearbeiten und die folgenden Einstellungen vornehmen: + +- Name: Name der App +- OAuth Redirect URLs: https://your-url.com/maybe-a-subpage/?rex_ycom_auth_mode=oauth2_twitch&rex_ycom_auth_func=code +- Category: Website Integration + +Anschließend die App speichern und die Client-ID kopieren. +Dann ein neues Secret erzeugen und dieses ebenfalls kopieren / sichern. + +In den Ordner `redaxo/data/addons/ycom/` sollte bereits die Datei `oauth2_twitch.php` kopiert worden sein. Diese Datei muss nun entsprechend angepasst werden mit den kopierten Daten. + +Damit die Authentifizierung funktioniert, muss im Loginformular von YCOM folgender String (angepasst auf die eigenen Bedürfnisse) eingefügt werden: + +```php +ycom_auth_oauth2_twitch|twitch|error_msg|[allowed returnTo domains: DomainA,DomainB]|{"ycom_groups": 1, "termsofuse_accepted": 1}|direct_link 0,1 +``` + +#### Scope + +Zusätzlice Scopes lassen sich in der Datei `oauth2_twitch.php` als Array in der Variable `scopes` eintragen. Die Scopes müssen mit einem Komma getrennt werden. Weitere Scopes findet man hier: https://dev.twitch.tv/docs/authentication/scopes/ - z.B. `user:read:email` um die E-Mail-Adresse des Users zu erhalten (diese bringt der Provider aber bereits nativ mit). + +## OAuth2 mit Google + +Mit der OAuth2 Authentifizierung via Google ist es möglich, sich mit einem Google-Account in der YCOM zu registrieren und einzuloggen. Dazu muss dieser Provider entsprechend vorbereitet sein. + +### Einrichtung + +Im ersten Schritt muss man eine App anlegen bei Google. Hierfür einmal zu https://console.developers.google.com/ wechseln. Dort über den Button "Projekt auswählen" eine neues Projekt erstellen. Anschließend die App bearbeiten und die folgenden Einstellungen vornehmen: + +- OAuth Zustimmungsbildschirm: Einstellungen vornehmen +-- Anwendungsname: Name der App +-- Nutzersupport-E-Mail: E-Mail-Adresse für Support +-- Anwendungslogo: Logo der App +-- Startseite der App: Deine Domain +-- Kontaktdaten des Entwicklers: E-Mail-Adresse für Support +-- Autorisierte Domains: Deine Domain (optional für GSuite Nutzer) +-- Bereiche: +--- ./auth/userinfo.email (E-Mail-Adresse des Users) +--- ./auth/userinfo.profile (Profilinformationen des Users) + +Anschließend auf Anmeldedaten klicken und die folgenden Einstellungen vornehmen: +- Anmeldedaten erstellen: OAuth 2.0 Client IDs erstellen +-- Autorisierte JavaScript-Quellen: Deine Domain +-- Autorisierte Weiterleitungs-URIs: https://your-url.com/maybe-a-subpage/?rex_ycom_auth_mode=oauth2_google&rex_ycom_auth_func=code + +Danach kann die Client ID und der Clientschlüssel kopiert oder als JSON Datei heruntergeladen werden. + +In den Ordner `redaxo/data/addons/ycom/` sollte bereits die Datei `oauth2_google.php` kopiert worden sein. Diese Datei muss nun entsprechend angepasst werden mit den kopierten Daten. + +Damit die Authentifizierung funktioniert, muss im Loginformular von YCOM folgender String (angepasst auf die eigenen Bedürfnisse) eingefügt werden: + +```php +ycom_auth_oauth2_google|label|error_msg|[allowed returnTo domains: DomainA,DomainB]|default Userdata as Json{"ycom_groups": 3, "termsofuse_accepted": 1}|direct_link 0,1 +``` + +#### GSuite / Google Workspace Nutzer +In der Datei `oauth2_google.php` kann die Variable `hostedDomain` mit der Domain des GSuite / Google Workspace Accounts befüllt werden. Damit wird die Anmeldung auf Nutzer mit dieser Domain beschränkt. + +## OAuth2 mit GitHub + +Mit der OAuth2 Authentifizierung via GitHub ist es möglich, sich mit einem GitHub-Account in der YCOM zu registrieren und einzuloggen. Dazu muss dieser Provider entsprechend vorbereitet sein. + +### Einrichtung + +Im ersten Schritt muss man eine App anlegen bei GitHub. Hierfür einmal zu https://github.com/settings/apps wechseln. Dort über den Button "New GitHub App" eine neue App erstellen. Anschließend die App bearbeiten und die folgenden Einstellungen vornehmen: + +- Name: Name der App +- Callback-URL: https://your-url.com/maybe-a-subpage/?rex_ycom_auth_mode=oauth2_github&rex_ycom_auth_func=code +- Haken setzen bei "Request user authorization (OAuth) during installation" +- Webhook kann ausgemacht werden +- Account Permissions: Email adresses => "Read-only" und Profile=> "Read & write" auswählen + +App speichern und die Client-ID kopieren. +Dann ein neues Secret erzeugen und dieses ebenfalls kopieren / sichern. + +In den Ordner `redaxo/data/addons/ycom/` sollte bereits die Datei `oauth2_github.php` kopiert worden sein. Diese Datei muss nun entsprechend angepasst werden mit den kopierten Daten. + +Damit die Authentifizierung funktioniert, muss im Loginformular von YCOM folgender String (angepasst auf die eigenen Bedürfnisse) eingefügt werden: + +```php +ycom_auth_oauth2_github|label|error_msg|[allowed returnTo domains: DomainA,DomainB]|default Userdata as Json{"ycom_groups": 2, "termsofuse_accepted": 1}|direct_link 0,1 +``` + + ## Allgemeines ### Loginseite diff --git a/install/tablesets/yform_user.json b/install/tablesets/yform_user.json index b2015e1f..4b783668 100644 --- a/install/tablesets/yform_user.json +++ b/install/tablesets/yform_user.json @@ -390,6 +390,24 @@ "attributes": "", "relation_table": "", "filter": "" + }, + { + "table_name": "rex_ycom_user", + "prio": 25, + "type_id": "value", + "type_name": "text", + "db_type": "", + "list_hidden": 1, + "search": 1, + "name": "user_image", + "label": "translate:image", + "not_required": "", + "default": "", + "no_db": "", + "notice": "", + "attributes": "", + "prepend": "", + "append": "" } ] }, diff --git a/lang/de_de.lang b/lang/de_de.lang index 105c716f..333f6fe6 100644 --- a/lang/de_de.lang +++ b/lang/de_de.lang @@ -26,6 +26,7 @@ rex_ycom_user = User email = E-Mail status = Status firstname = Vorname +image = Bild activation_key = Aktivierungsschlüssel session_key = Sessionschlüssel last_login_time = Letzter erfolgreicher Login diff --git a/plugins/auth/boot.php b/plugins/auth/boot.php index ff755f47..161bdcdc 100644 --- a/plugins/auth/boot.php +++ b/plugins/auth/boot.php @@ -49,6 +49,15 @@ case 'oauth2': $data = rex_extension::registerPoint(new rex_extension_point('YCOM_AUTH_OAUTH2_MATCHING', $data, ['Userdata' => $Userdata])); break; + case 'oauth2_twitch': + $data = rex_extension::registerPoint(new rex_extension_point('YCOM_AUTH_OAUTH2_TWITCH_MATCHING', $data, ['Userdata' => $Userdata])); + break; + case 'oauth2_google': + $data = rex_extension::registerPoint(new rex_extension_point('YCOM_AUTH_OAUTH2_GOOGLE_MATCHING', $data, ['Userdata' => $Userdata])); + break; + case 'oauth2_github': + $data = rex_extension::registerPoint(new rex_extension_point('YCOM_AUTH_OAUTH2_GITHUB_MATCHING', $data, ['Userdata' => $Userdata])); + break; case 'saml': $data = rex_extension::registerPoint(new rex_extension_point('YCOM_AUTH_SAML_MATCHING', $data, ['Userdata' => $Userdata])); break; diff --git a/plugins/auth/install.php b/plugins/auth/install.php index 0d7d19c0..c6c5d91a 100644 --- a/plugins/auth/install.php +++ b/plugins/auth/install.php @@ -46,7 +46,7 @@ rex_sql::factory()->setQuery('UPDATE rex_article SET `ycom_auth_type` = `ycom_auth_type` -1'); } -foreach (['saml', 'oauth2', 'cas'] as $settingType) { +foreach (['saml', 'oauth2', 'oauth2_twitch', 'oauth2_google', 'oauth2_github', 'cas'] as $settingType) { $pathFrom = __DIR__ . '/install/' . $settingType . '.php'; $pathTo = rex_addon::get('ycom')->getDataPath($settingType . '.php'); if (!file_exists($pathTo)) { diff --git a/plugins/auth/install/oauth2_github.php b/plugins/auth/install/oauth2_github.php new file mode 100644 index 00000000..73e5883b --- /dev/null +++ b/plugins/auth/install/oauth2_github.php @@ -0,0 +1,7 @@ + 'someJibberish', // Go to https://github.com/settings/apps and setup a Github app. Fill out, add return url, set scopes in Account permissions: Email addresses => Read only, Profile => Read and write + 'clientSecret' => 'moreJibbereish', // In your project at https://github.com/settings/apps, click on your created project and then "Generate a new client secret". + 'redirectUri' => 'https://your-url.com/maybe-a-subpage/?rex_ycom_auth_mode=oauth2_google&rex_ycom_auth_func=code', // do not fill out first and wait for the login error message to fill it out +]; diff --git a/plugins/auth/install/oauth2_google.php b/plugins/auth/install/oauth2_google.php new file mode 100644 index 00000000..d3bb6c8a --- /dev/null +++ b/plugins/auth/install/oauth2_google.php @@ -0,0 +1,8 @@ + 'someJibberish', // Go to https://console.cloud.google.com/ and setup a project. Setup an OAuth consent screen and choose scopes + 'clientSecret' => 'moreJibbereish', // In your project at https://console.cloud.google.com/, click on "Credential" and then "Create credentials => OAuth client ID". Fill out, add return url, get ID and secret + 'redirectUri' => 'https://your-url.com/maybe-a-subpage/?rex_ycom_auth_mode=oauth2_google&rex_ycom_auth_func=code', // do not fill out first and wait for the login error message to fill it out + // 'hostedDomain' => 'your-url.com', // optional; used to restrict access to users on your G Suite/Google Apps for Business accounts +]; diff --git a/plugins/auth/install/oauth2_twitch.php b/plugins/auth/install/oauth2_twitch.php new file mode 100644 index 00000000..e38cdd07 --- /dev/null +++ b/plugins/auth/install/oauth2_twitch.php @@ -0,0 +1,8 @@ + 'someJibberish', // Go to https://dev.twitch.tv/console/apps and create an app. The client ID from your app + 'clientSecret' => 'moreJibbereish', // In your app at https://dev.twitch.tv/console/apps, click on "Manage" and then "New Secret". The client password from your app + 'redirectUri' => 'https://your-url.com/maybe-a-subpage/?rex_ycom_auth_mode=oauth2_twitch&rex_ycom_auth_func=code', // do not fill out first and wait for the login error message to fill it out - add it to your allowed domains in your app at https://dev.twitch.tv/console/apps + // 'scope' => 'user:read:email,user:read:follows', +]; diff --git a/plugins/auth/lib/yform/trait_value_auth_oauth2_github.php b/plugins/auth/lib/yform/trait_value_auth_oauth2_github.php new file mode 100644 index 00000000..3bd1ae2f --- /dev/null +++ b/plugins/auth/lib/yform/trait_value_auth_oauth2_github.php @@ -0,0 +1,166 @@ +auth_ClassKey . '.php'; + $SettingsPath = rex_addon::get('ycom')->getDataPath($SettingFile); + if (!file_exists($SettingsPath)) { + throw new rex_exception($this->auth_ClassKey . '-Settings file not found [' . $SettingsPath . ']'); + } + + $settings = []; + include $SettingsPath; + return $settings; + } + + private function auth_getReturnTo(): string + { + $returnTos = []; + $returnTos[] = rex_request('returnTo', 'string'); // wenn returnTo übergeben wurde, diesen nehmen + $returnTos[] = rex_getUrl(rex_config::get('ycom/auth', 'article_id_jump_ok'), '', [], '&'); // Auth Ok -> article_id_jump_ok / Current Language will be selected + return rex_ycom_auth::getReturnTo($returnTos, ('' == $this->getElement(3)) ? [] : explode(',', $this->getElement(3))); + } + + private function auth_FormOutput(string $url): void + { + if ($this->needsOutput()) { + $this->params['form_output'][$this->getId()] = $this->parse(['value.ycom_auth_' . $this->auth_ClassKey . '.tpl.php', 'value.ycom_auth_extern.tpl.php'], [ + 'url' => $url, + 'name' => '{{ ' . $this->auth_ClassKey . '_auth }}', + ]); + } + } + + private function auth_redirectToFailed(string $message = ''): string + { + if ($this->params['debug']) { + dump($message); + return $message; + } + if ($this->auth_directLink) { + rex_response::sendCacheControl(); + rex_response::sendRedirect(rex_getUrl(rex_config::get('ycom/auth', 'article_id_jump_not_ok'))); + } + return ''; + } + + /** + * @param array $Userdata + * @throws rex_exception + */ + private function auth_createOrUpdateYComUser(array $Userdata, string $returnTo): void + { + $defaultUserAttributes = []; + if ('' != $this->getElement(4)) { + if (null == $defaultUserAttributes = json_decode($this->getElement(4), true)) { + throw new rex_exception($this->auth_ClassKey . '-DefaultUserAttributes is not a json' . $this->getElement(4)); + } + } + + $data = []; + $data['email'] = ''; + foreach (['User.email', 'emailAddress', 'email'] as $Key) { + if (isset($Userdata[$Key])) { + $data['email'] = is_array($Userdata[$Key]) ? implode(' ', $Userdata[$Key]) : $Userdata[$Key]; + } + } + + $data['firstname'] = ''; + foreach (['login'] as $Key) { + if (isset($Userdata[$Key])) { + $data['firstname'] = is_array($Userdata[$Key]) ? implode(' ', $Userdata[$Key]) : $Userdata[$Key]; + } + } + + $data['name'] = ''; + foreach (['name'] as $Key) { + if (isset($Userdata[$Key])) { + $data['name'] = is_array($Userdata[$Key]) ? implode(' ', $Userdata[$Key]) : $Userdata[$Key]; + } + } + + $data['user_image'] = ''; + foreach (['avatar_url'] as $Key) { + if (isset($Userdata[$Key])) { + $data['user_image'] = is_array($Userdata[$Key]) ? implode(' ', $Userdata[$Key]) : $Userdata[$Key]; + } + } + + foreach ($defaultUserAttributes as $defaultUserAttributeKey => $defaultUserAttributeValue) { + $data[$defaultUserAttributeKey] = $defaultUserAttributeValue; + } + + $data = rex_extension::registerPoint(new rex_extension_point('YCOM_AUTH_MATCHING', $data, ['Userdata' => $Userdata, 'AuthType' => $this->auth_ClassKey])); + + self::auth_clearUserSession(); + + // not logged in - check if available + $params = [ + 'loginName' => $data['email'], + 'loginPassword' => '', + 'loginStay' => true, + 'filter' => 'status > 0', + 'ignorePassword' => true, + ]; + + $loginStatus = rex_ycom_auth::login($params); + if (2 == $loginStatus) { + // already logged in + rex_ycom_user::updateUser($data); + rex_response::sendCacheControl(); + rex_response::sendRedirect($returnTo); + } + + // if user not found, check if exists, but no permission + $user = rex_ycom_user::query()->where('email', $data['email'])->findOne(); + if ($user) { + $this->auth_redirectToFailed('{{ ' . $this->auth_ClassKey . '.error.ycom_login_failed }}'); + $this->params['warning_messages'][] = ('' != $this->getElement(2)) ? $this->getElement(2) : '{{ ' . $this->auth_ClassKey . '.error.ycom_login_failed }}'; + return; + } + + $user = rex_ycom_user::createUserByEmail($data); + if (!$user || count($user->getMessages()) > 0) { + if ($user && $this->params['debug']) { + dump($user->getMessages()); + } + $this->auth_redirectToFailed('{{ ' . $this->auth_ClassKey . '.error.ycom_create_user }}'); + $this->params['warning_messages'][] = ('' != $this->getElement(2)) ? $this->getElement(2) : '{{ ' . $this->auth_ClassKey . '.error.ycom_create_user }}'; + return; + } + + $params = []; + $params['loginName'] = $user->getValue('email'); + $params['ignorePassword'] = true; + $params['loginStay'] = false; + $params['filter'] = 'status > 0'; + $params['loginPassword'] = ''; + $loginStatus = rex_ycom_auth::login($params); + + if (2 != $loginStatus) { + if ($this->params['debug']) { + dump($loginStatus); + dump($user); + } + $this->auth_redirectToFailed('{{ ' . $this->auth_ClassKey . '.error.ycom_login_created_user }}'); + $this->params['warning_messages'][] = ('' != $this->getElement(2)) ? $this->getElement(2) : '{{ ' . $this->auth_ClassKey . '.error.ycom_login_created_user }}'; + return; + } + + rex_response::sendCacheControl(); + rex_response::sendRedirect($returnTo); + } + + private function auth_clearUserSession(): void + { + foreach ($this->auth_SessionVars as $SessionKey) { + rex_ycom_auth::unsetSessionVar($SessionKey); + } + } +} diff --git a/plugins/auth/lib/yform/trait_value_auth_oauth2_google.php b/plugins/auth/lib/yform/trait_value_auth_oauth2_google.php new file mode 100644 index 00000000..03a8048a --- /dev/null +++ b/plugins/auth/lib/yform/trait_value_auth_oauth2_google.php @@ -0,0 +1,166 @@ +auth_ClassKey . '.php'; + $SettingsPath = rex_addon::get('ycom')->getDataPath($SettingFile); + if (!file_exists($SettingsPath)) { + throw new rex_exception($this->auth_ClassKey . '-Settings file not found [' . $SettingsPath . ']'); + } + + $settings = []; + include $SettingsPath; + return $settings; + } + + private function auth_getReturnTo(): string + { + $returnTos = []; + $returnTos[] = rex_request('returnTo', 'string'); // wenn returnTo übergeben wurde, diesen nehmen + $returnTos[] = rex_getUrl(rex_config::get('ycom/auth', 'article_id_jump_ok'), '', [], '&'); // Auth Ok -> article_id_jump_ok / Current Language will be selected + return rex_ycom_auth::getReturnTo($returnTos, ('' == $this->getElement(3)) ? [] : explode(',', $this->getElement(3))); + } + + private function auth_FormOutput(string $url): void + { + if ($this->needsOutput()) { + $this->params['form_output'][$this->getId()] = $this->parse(['value.ycom_auth_' . $this->auth_ClassKey . '.tpl.php', 'value.ycom_auth_extern.tpl.php'], [ + 'url' => $url, + 'name' => '{{ ' . $this->auth_ClassKey . '_auth }}', + ]); + } + } + + private function auth_redirectToFailed(string $message = ''): string + { + if ($this->params['debug']) { + dump($message); + return $message; + } + if ($this->auth_directLink) { + rex_response::sendCacheControl(); + rex_response::sendRedirect(rex_getUrl(rex_config::get('ycom/auth', 'article_id_jump_not_ok'))); + } + return ''; + } + + /** + * @param array $Userdata + * @throws rex_exception + */ + private function auth_createOrUpdateYComUser(array $Userdata, string $returnTo): void + { + $defaultUserAttributes = []; + if ('' != $this->getElement(4)) { + if (null == $defaultUserAttributes = json_decode($this->getElement(4), true)) { + throw new rex_exception($this->auth_ClassKey . '-DefaultUserAttributes is not a json' . $this->getElement(4)); + } + } + + $data = []; + $data['email'] = ''; + foreach (['User.email', 'emailAddress', 'email'] as $Key) { + if (isset($Userdata[$Key])) { + $data['email'] = is_array($Userdata[$Key]) ? implode(' ', $Userdata[$Key]) : $Userdata[$Key]; + } + } + + $data['firstname'] = ''; + foreach (['given_name'] as $Key) { + if (isset($Userdata[$Key])) { + $data['firstname'] = is_array($Userdata[$Key]) ? implode(' ', $Userdata[$Key]) : $Userdata[$Key]; + } + } + + $data['name'] = ''; + foreach (['family_name'] as $Key) { + if (isset($Userdata[$Key])) { + $data['name'] = is_array($Userdata[$Key]) ? implode(' ', $Userdata[$Key]) : $Userdata[$Key]; + } + } + + $data['user_image'] = ''; + foreach (['picture'] as $Key) { + if (isset($Userdata[$Key])) { + $data['user_image'] = is_array($Userdata[$Key]) ? implode(' ', $Userdata[$Key]) : $Userdata[$Key]; + } + } + + foreach ($defaultUserAttributes as $defaultUserAttributeKey => $defaultUserAttributeValue) { + $data[$defaultUserAttributeKey] = $defaultUserAttributeValue; + } + + $data = rex_extension::registerPoint(new rex_extension_point('YCOM_AUTH_MATCHING', $data, ['Userdata' => $Userdata, 'AuthType' => $this->auth_ClassKey])); + + self::auth_clearUserSession(); + + // not logged in - check if available + $params = [ + 'loginName' => $data['email'], + 'loginPassword' => '', + 'loginStay' => true, + 'filter' => 'status > 0', + 'ignorePassword' => true, + ]; + + $loginStatus = rex_ycom_auth::login($params); + if (2 == $loginStatus) { + // already logged in + rex_ycom_user::updateUser($data); + rex_response::sendCacheControl(); + rex_response::sendRedirect($returnTo); + } + + // if user not found, check if exists, but no permission + $user = rex_ycom_user::query()->where('email', $data['email'])->findOne(); + if ($user) { + $this->auth_redirectToFailed('{{ ' . $this->auth_ClassKey . '.error.ycom_login_failed }}'); + $this->params['warning_messages'][] = ('' != $this->getElement(2)) ? $this->getElement(2) : '{{ ' . $this->auth_ClassKey . '.error.ycom_login_failed }}'; + return; + } + + $user = rex_ycom_user::createUserByEmail($data); + if (!$user || count($user->getMessages()) > 0) { + if ($user && $this->params['debug']) { + dump($user->getMessages()); + } + $this->auth_redirectToFailed('{{ ' . $this->auth_ClassKey . '.error.ycom_create_user }}'); + $this->params['warning_messages'][] = ('' != $this->getElement(2)) ? $this->getElement(2) : '{{ ' . $this->auth_ClassKey . '.error.ycom_create_user }}'; + return; + } + + $params = []; + $params['loginName'] = $user->getValue('email'); + $params['ignorePassword'] = true; + $params['loginStay'] = false; + $params['filter'] = 'status > 0'; + $params['loginPassword'] = ''; + $loginStatus = rex_ycom_auth::login($params); + + if (2 != $loginStatus) { + if ($this->params['debug']) { + dump($loginStatus); + dump($user); + } + $this->auth_redirectToFailed('{{ ' . $this->auth_ClassKey . '.error.ycom_login_created_user }}'); + $this->params['warning_messages'][] = ('' != $this->getElement(2)) ? $this->getElement(2) : '{{ ' . $this->auth_ClassKey . '.error.ycom_login_created_user }}'; + return; + } + + rex_response::sendCacheControl(); + rex_response::sendRedirect($returnTo); + } + + private function auth_clearUserSession(): void + { + foreach ($this->auth_SessionVars as $SessionKey) { + rex_ycom_auth::unsetSessionVar($SessionKey); + } + } +} diff --git a/plugins/auth/lib/yform/trait_value_auth_oauth2_twitch.php b/plugins/auth/lib/yform/trait_value_auth_oauth2_twitch.php new file mode 100644 index 00000000..b7772bfc --- /dev/null +++ b/plugins/auth/lib/yform/trait_value_auth_oauth2_twitch.php @@ -0,0 +1,166 @@ +auth_ClassKey . '.php'; + $SettingsPath = rex_addon::get('ycom')->getDataPath($SettingFile); + if (!file_exists($SettingsPath)) { + throw new rex_exception($this->auth_ClassKey . '-Settings file not found [' . $SettingsPath . ']'); + } + + $settings = []; + include $SettingsPath; + return $settings; + } + + private function auth_getReturnTo(): string + { + $returnTos = []; + $returnTos[] = rex_request('returnTo', 'string'); // wenn returnTo übergeben wurde, diesen nehmen + $returnTos[] = rex_getUrl(rex_config::get('ycom/auth', 'article_id_jump_ok'), '', [], '&'); // Auth Ok -> article_id_jump_ok / Current Language will be selected + return rex_ycom_auth::getReturnTo($returnTos, ('' == $this->getElement(3)) ? [] : explode(',', $this->getElement(3))); + } + + private function auth_FormOutput(string $url): void + { + if ($this->needsOutput()) { + $this->params['form_output'][$this->getId()] = $this->parse(['value.ycom_auth_' . $this->auth_ClassKey . '.tpl.php', 'value.ycom_auth_extern.tpl.php'], [ + 'url' => $url, + 'name' => '{{ ' . $this->auth_ClassKey . '_auth }}', + ]); + } + } + + private function auth_redirectToFailed(string $message = ''): string + { + if ($this->params['debug']) { + dump($message); + return $message; + } + if ($this->auth_directLink) { + rex_response::sendCacheControl(); + rex_response::sendRedirect(rex_getUrl(rex_config::get('ycom/auth', 'article_id_jump_not_ok'))); + } + return ''; + } + + /** + * @param array $Userdata + * @throws rex_exception + */ + private function auth_createOrUpdateYComUser(array $Userdata, string $returnTo): void + { + $defaultUserAttributes = []; + if ('' != $this->getElement(4)) { + if (null == $defaultUserAttributes = json_decode($this->getElement(4), true)) { + throw new rex_exception($this->auth_ClassKey . '-DefaultUserAttributes is not a json' . $this->getElement(4)); + } + } + + $data = []; + $data['email'] = ''; + foreach (['User.email', 'emailAddress', 'email'] as $Key) { + if (isset($Userdata[$Key])) { + $data['email'] = is_array($Userdata[$Key]) ? implode(' ', $Userdata[$Key]) : $Userdata[$Key]; + } + } + + $data['firstname'] = ''; + foreach (['login'] as $Key) { + if (isset($Userdata[$Key])) { + $data['firstname'] = is_array($Userdata[$Key]) ? implode(' ', $Userdata[$Key]) : $Userdata[$Key]; + } + } + + $data['name'] = ''; + foreach (['display_name'] as $Key) { + if (isset($Userdata[$Key])) { + $data['name'] = is_array($Userdata[$Key]) ? implode(' ', $Userdata[$Key]) : $Userdata[$Key]; + } + } + + $data['user_image'] = ''; + foreach (['profile_image_url'] as $Key) { + if (isset($Userdata[$Key])) { + $data['user_image'] = is_array($Userdata[$Key]) ? implode(' ', $Userdata[$Key]) : $Userdata[$Key]; + } + } + + foreach ($defaultUserAttributes as $defaultUserAttributeKey => $defaultUserAttributeValue) { + $data[$defaultUserAttributeKey] = $defaultUserAttributeValue; + } + + $data = rex_extension::registerPoint(new rex_extension_point('YCOM_AUTH_MATCHING', $data, ['Userdata' => $Userdata, 'AuthType' => $this->auth_ClassKey])); + + self::auth_clearUserSession(); + + // not logged in - check if available + $params = [ + 'loginName' => $data['email'], + 'loginPassword' => '', + 'loginStay' => true, + 'filter' => 'status > 0', + 'ignorePassword' => true, + ]; + + $loginStatus = rex_ycom_auth::login($params); + if (2 == $loginStatus) { + // already logged in + rex_ycom_user::updateUser($data); + rex_response::sendCacheControl(); + rex_response::sendRedirect($returnTo); + } + + // if user not found, check if exists, but no permission + $user = rex_ycom_user::query()->where('email', $data['email'])->findOne(); + if ($user) { + $this->auth_redirectToFailed('{{ ' . $this->auth_ClassKey . '.error.ycom_login_failed }}'); + $this->params['warning_messages'][] = ('' != $this->getElement(2)) ? $this->getElement(2) : '{{ ' . $this->auth_ClassKey . '.error.ycom_login_failed }}'; + return; + } + + $user = rex_ycom_user::createUserByEmail($data); + if (!$user || count($user->getMessages()) > 0) { + if ($user && $this->params['debug']) { + dump($user->getMessages()); + } + $this->auth_redirectToFailed('{{ ' . $this->auth_ClassKey . '.error.ycom_create_user }}'); + $this->params['warning_messages'][] = ('' != $this->getElement(2)) ? $this->getElement(2) : '{{ ' . $this->auth_ClassKey . '.error.ycom_create_user }}'; + return; + } + + $params = []; + $params['loginName'] = $user->getValue('email'); + $params['ignorePassword'] = true; + $params['loginStay'] = false; + $params['filter'] = 'status > 0'; + $params['loginPassword'] = ''; + $loginStatus = rex_ycom_auth::login($params); + + if (2 != $loginStatus) { + if ($this->params['debug']) { + dump($loginStatus); + dump($user); + } + $this->auth_redirectToFailed('{{ ' . $this->auth_ClassKey . '.error.ycom_login_created_user }}'); + $this->params['warning_messages'][] = ('' != $this->getElement(2)) ? $this->getElement(2) : '{{ ' . $this->auth_ClassKey . '.error.ycom_login_created_user }}'; + return; + } + + rex_response::sendCacheControl(); + rex_response::sendRedirect($returnTo); + } + + private function auth_clearUserSession(): void + { + foreach ($this->auth_SessionVars as $SessionKey) { + rex_ycom_auth::unsetSessionVar($SessionKey); + } + } +} diff --git a/plugins/auth/lib/yform/value/ycom_auth_oauth2_github.php b/plugins/auth/lib/yform/value/ycom_auth_oauth2_github.php new file mode 100644 index 00000000..4ce78b3e --- /dev/null +++ b/plugins/auth/lib/yform/value/ycom_auth_oauth2_github.php @@ -0,0 +1,133 @@ +www.yakamara.de + */ + +use League\OAuth2\Client\Provider\Exception\IdentityProviderException; +use League\OAuth2\Client\Provider\Github; + +class rex_yform_value_ycom_auth_oauth2_github extends rex_yform_value_abstract +{ + use rex_yform_trait_value_auth_oauth2_github; + + /** @var array|string[] */ + private array $auth_requestFunctions = ['init', 'code', 'state']; + private bool $auth_directLink = false; + /** @var array|string[] */ + private array $auth_SessionVars = ['OAUTH2_oauth2state']; + private string $auth_ClassKey = 'oauth2_github'; + + public function enterObject(): void + { + if (rex::isFrontend()) { + $this->auth_directLink = 1 == $this->getElement(5) ? true : false; + } + + rex_login::startSession(); + + $settings = $this->auth_loadSettings(); + $returnTo = $this->auth_getReturnTo(); + $this->auth_FormOutput(rex_getUrl('', '', ['rex_ycom_auth_mode' => 'oauth2_github', 'rex_ycom_auth_func' => 'init', 'returnTo' => $returnTo])); + + $requestMode = rex_request('rex_ycom_auth_mode', 'string', ''); + $requestFunction = rex_request('rex_ycom_auth_func', 'string', ''); + if (!in_array($requestFunction, $this->auth_requestFunctions, true) || $this->auth_ClassKey != $requestMode) { + if ($this->auth_directLink) { + $requestFunction = 'init'; + } else { + return; + } + } + + if ('' == $settings['redirectUri']) { + echo 'use this URL for redirect'; + dump(rex_yrewrite::getFullUrlByArticleId(rex_article::getCurrentId(), '', ['rex_ycom_auth_mode' => 'oauth2_github', 'rex_ycom_auth_func' => 'code'], '&')); + return; + } + + $provider = new Github($settings); + + $Userdata = []; + switch ($requestFunction) { + case 'code': + $code = rex_request('code', 'string'); + if ('' != $code) { + if ('' == rex_ycom_auth::getSessionVar('OAUTH2_oauth2state') || empty($_GET['state']) || $_GET['state'] != rex_ycom_auth::getSessionVar('OAUTH2_oauth2state')) { + if ($this->params['debug']) { + echo 'OAuth session saved state != OAuth State'; + dump(rex_ycom_auth::getSessionVar('OAUTH2_oauth2state')); + dump($_GET['state']); + } + $this->auth_redirectToFailed('{{ oauth.error.state_code }}'); + $this->params['warning_messages'][] = ('' != $this->getElement(3)) ? $this->getElement(3) : '{{ oauth.error.state_code }}'; + return; + } + + $accessToken = null; + try { + /** @var \League\OAuth2\Client\Token\AccessToken $accessToken */ + $accessToken = $provider->getAccessToken('authorization_code', [ + 'code' => $code, + ]); + + if ($accessToken->hasExpired()) { + $this->auth_redirectToFailed('{{ oauth.error.access_expired }}'); + $this->params['warning_messages'][] = ('' != $this->getElement(3)) ? $this->getElement(3) : '{{ oauth.error.access_expired }}'; + return; + } + $resourceOwner = $provider->getResourceOwner($accessToken); + $Userdata = $resourceOwner->toArray(); + // print_r($Userdata); + // exit; + $returnTo = rex_ycom_auth::getSessionVar('OAUTH2_oauth2returnTo'); + rex_ycom_auth::unsetSessionVar('OAUTH2_oauth2returnTo'); + } catch (IdentityProviderException $e) { + if ($this->params['debug']) { + dump($e); + } + $this->auth_redirectToFailed('{{ oauth.error.code }}'); + $this->params['warning_messages'][] = ('' != $this->getElement(3)) ? $this->getElement(3) : '{{ oauth.error.code }}'; + return; + } catch (Exception $e) { + if ($this->params['debug']) { + echo 'OAuth Error'; + dump($accessToken); + dump($e); + dump($e->getMessage()); + } + $this->auth_redirectToFailed('{{ oauth.error.code }}'); + $this->params['warning_messages'][] = ('' != $this->getElement(3)) ? $this->getElement(3) : '{{ oauth.error.code }}'; + return; + } + } else { + $this->auth_redirectToFailed('{{ oauth.error.no_code }}'); + $this->params['warning_messages'][] = ('' != $this->getElement(3)) ? $this->getElement(3) : '{{ oauth.error.no_code }}'; + return; + } + break; + + case 'init': + $authorizationUrl = $provider->getAuthorizationUrl(); + rex_ycom_auth::setSessionVar('OAUTH2_oauth2state', $provider->getState()); + rex_ycom_auth::setSessionVar('OAUTH2_oauth2returnTo', $returnTo); + rex_response::sendCacheControl(); + rex_response::sendRedirect($authorizationUrl); + } + + $this->auth_createOrUpdateYComUser($Userdata, $returnTo); + } + + public function getDescription(): string + { + return 'ycom_auth_oauth2_github|label|error_msg|[allowed returnTo domains: DomainA,DomainB]|default Userdata as Json{"ycom_groups": 3, "termsofuse_accepted": 1}|direct_link 0,1'; + } +} diff --git a/plugins/auth/lib/yform/value/ycom_auth_oauth2_google.php b/plugins/auth/lib/yform/value/ycom_auth_oauth2_google.php new file mode 100644 index 00000000..28cd5fe9 --- /dev/null +++ b/plugins/auth/lib/yform/value/ycom_auth_oauth2_google.php @@ -0,0 +1,133 @@ +www.yakamara.de + */ + +use League\OAuth2\Client\Provider\Exception\IdentityProviderException; +use League\OAuth2\Client\Provider\Google; + +class rex_yform_value_ycom_auth_oauth2_google extends rex_yform_value_abstract +{ + use rex_yform_trait_value_auth_oauth2_google; + + /** @var array|string[] */ + private array $auth_requestFunctions = ['init', 'code', 'state']; + private bool $auth_directLink = false; + /** @var array|string[] */ + private array $auth_SessionVars = ['OAUTH2_oauth2state']; + private string $auth_ClassKey = 'oauth2_google'; + + public function enterObject(): void + { + if (rex::isFrontend()) { + $this->auth_directLink = 1 == $this->getElement(5) ? true : false; + } + + rex_login::startSession(); + + $settings = $this->auth_loadSettings(); + $returnTo = $this->auth_getReturnTo(); + $this->auth_FormOutput(rex_getUrl('', '', ['rex_ycom_auth_mode' => 'oauth2_google', 'rex_ycom_auth_func' => 'init', 'returnTo' => $returnTo])); + + $requestMode = rex_request('rex_ycom_auth_mode', 'string', ''); + $requestFunction = rex_request('rex_ycom_auth_func', 'string', ''); + if (!in_array($requestFunction, $this->auth_requestFunctions, true) || $this->auth_ClassKey != $requestMode) { + if ($this->auth_directLink) { + $requestFunction = 'init'; + } else { + return; + } + } + + if ('' == $settings['redirectUri']) { + echo 'use this URL for redirect'; + dump(rex_yrewrite::getFullUrlByArticleId(rex_article::getCurrentId(), '', ['rex_ycom_auth_mode' => 'oauth2_google', 'rex_ycom_auth_func' => 'code'], '&')); + return; + } + + $provider = new Google($settings); + + $Userdata = []; + switch ($requestFunction) { + case 'code': + $code = rex_request('code', 'string'); + if ('' != $code) { + if ('' == rex_ycom_auth::getSessionVar('OAUTH2_oauth2state') || empty($_GET['state']) || $_GET['state'] != rex_ycom_auth::getSessionVar('OAUTH2_oauth2state')) { + if ($this->params['debug']) { + echo 'OAuth session saved state != OAuth State'; + dump(rex_ycom_auth::getSessionVar('OAUTH2_oauth2state')); + dump($_GET['state']); + } + $this->auth_redirectToFailed('{{ oauth.error.state_code }}'); + $this->params['warning_messages'][] = ('' != $this->getElement(3)) ? $this->getElement(3) : '{{ oauth.error.state_code }}'; + return; + } + + $accessToken = null; + try { + /** @var \League\OAuth2\Client\Token\AccessToken $accessToken */ + $accessToken = $provider->getAccessToken('authorization_code', [ + 'code' => $code, + ]); + + if ($accessToken->hasExpired()) { + $this->auth_redirectToFailed('{{ oauth.error.access_expired }}'); + $this->params['warning_messages'][] = ('' != $this->getElement(3)) ? $this->getElement(3) : '{{ oauth.error.access_expired }}'; + return; + } + $resourceOwner = $provider->getResourceOwner($accessToken); + $Userdata = $resourceOwner->toArray(); + // print_r($Userdata); + // exit; + $returnTo = rex_ycom_auth::getSessionVar('OAUTH2_oauth2returnTo'); + rex_ycom_auth::unsetSessionVar('OAUTH2_oauth2returnTo'); + } catch (IdentityProviderException $e) { + if ($this->params['debug']) { + dump($e); + } + $this->auth_redirectToFailed('{{ oauth.error.code }}'); + $this->params['warning_messages'][] = ('' != $this->getElement(3)) ? $this->getElement(3) : '{{ oauth.error.code }}'; + return; + } catch (Exception $e) { + if ($this->params['debug']) { + echo 'OAuth Error'; + dump($accessToken); + dump($e); + dump($e->getMessage()); + } + $this->auth_redirectToFailed('{{ oauth.error.code }}'); + $this->params['warning_messages'][] = ('' != $this->getElement(3)) ? $this->getElement(3) : '{{ oauth.error.code }}'; + return; + } + } else { + $this->auth_redirectToFailed('{{ oauth.error.no_code }}'); + $this->params['warning_messages'][] = ('' != $this->getElement(3)) ? $this->getElement(3) : '{{ oauth.error.no_code }}'; + return; + } + break; + + case 'init': + $authorizationUrl = $provider->getAuthorizationUrl(); + rex_ycom_auth::setSessionVar('OAUTH2_oauth2state', $provider->getState()); + rex_ycom_auth::setSessionVar('OAUTH2_oauth2returnTo', $returnTo); + rex_response::sendCacheControl(); + rex_response::sendRedirect($authorizationUrl); + } + + $this->auth_createOrUpdateYComUser($Userdata, $returnTo); + } + + public function getDescription(): string + { + return 'ycom_auth_oauth2_google|label|error_msg|[allowed returnTo domains: DomainA,DomainB]|default Userdata as Json{"ycom_groups": 3, "termsofuse_accepted": 1}|direct_link 0,1'; + } +} diff --git a/plugins/auth/lib/yform/value/ycom_auth_oauth2_twitch.php b/plugins/auth/lib/yform/value/ycom_auth_oauth2_twitch.php new file mode 100644 index 00000000..1ae63c81 --- /dev/null +++ b/plugins/auth/lib/yform/value/ycom_auth_oauth2_twitch.php @@ -0,0 +1,133 @@ +www.yakamara.de + */ + +use League\OAuth2\Client\Provider\Exception\IdentityProviderException; +use Vertisan\OAuth2\Client\Provider\TwitchHelix; + +class rex_yform_value_ycom_auth_oauth2_twitch extends rex_yform_value_abstract +{ + use rex_yform_trait_value_auth_oauth2_twitch; + + /** @var array|string[] */ + private array $auth_requestFunctions = ['init', 'code', 'state']; + private bool $auth_directLink = false; + /** @var array|string[] */ + private array $auth_SessionVars = ['OAUTH2_oauth2state']; + private string $auth_ClassKey = 'oauth2_twitch'; + + public function enterObject(): void + { + if (rex::isFrontend()) { + $this->auth_directLink = 1 == $this->getElement(5) ? true : false; + } + + rex_login::startSession(); + + $settings = $this->auth_loadSettings(); + $returnTo = $this->auth_getReturnTo(); + $this->auth_FormOutput(rex_getUrl('', '', ['rex_ycom_auth_mode' => 'oauth2_twitch', 'rex_ycom_auth_func' => 'init', 'returnTo' => $returnTo])); + + $requestMode = rex_request('rex_ycom_auth_mode', 'string', ''); + $requestFunction = rex_request('rex_ycom_auth_func', 'string', ''); + if (!in_array($requestFunction, $this->auth_requestFunctions, true) || $this->auth_ClassKey != $requestMode) { + if ($this->auth_directLink) { + $requestFunction = 'init'; + } else { + return; + } + } + + if ('' == $settings['redirectUri']) { + echo 'use this URL for redirect'; + dump(rex_yrewrite::getFullUrlByArticleId(rex_article::getCurrentId(), '', ['rex_ycom_auth_mode' => 'oauth2_twitch', 'rex_ycom_auth_func' => 'code'], '&')); + return; + } + + $provider = new TwitchHelix($settings); + + $Userdata = []; + switch ($requestFunction) { + case 'code': + $code = rex_request('code', 'string'); + if ('' != $code) { + if ('' == rex_ycom_auth::getSessionVar('OAUTH2_oauth2state') || empty($_GET['state']) || $_GET['state'] != rex_ycom_auth::getSessionVar('OAUTH2_oauth2state')) { + if ($this->params['debug']) { + echo 'OAuth session saved state != OAuth State'; + dump(rex_ycom_auth::getSessionVar('OAUTH2_oauth2state')); + dump($_GET['state']); + } + $this->auth_redirectToFailed('{{ oauth.error.state_code }}'); + $this->params['warning_messages'][] = ('' != $this->getElement(3)) ? $this->getElement(3) : '{{ oauth.error.state_code }}'; + return; + } + + $accessToken = null; + try { + /** @var \League\OAuth2\Client\Token\AccessToken $accessToken */ + $accessToken = $provider->getAccessToken('authorization_code', [ + 'code' => $code, + ]); + + if ($accessToken->hasExpired()) { + $this->auth_redirectToFailed('{{ oauth.error.access_expired }}'); + $this->params['warning_messages'][] = ('' != $this->getElement(3)) ? $this->getElement(3) : '{{ oauth.error.access_expired }}'; + return; + } + $resourceOwner = $provider->getResourceOwner($accessToken); + $Userdata = $resourceOwner->toArray(); + // print_r($Userdata); + // exit; + $returnTo = rex_ycom_auth::getSessionVar('OAUTH2_oauth2returnTo'); + rex_ycom_auth::unsetSessionVar('OAUTH2_oauth2returnTo'); + } catch (IdentityProviderException $e) { + if ($this->params['debug']) { + dump($e); + } + $this->auth_redirectToFailed('{{ oauth.error.code }}'); + $this->params['warning_messages'][] = ('' != $this->getElement(3)) ? $this->getElement(3) : '{{ oauth.error.code }}'; + return; + } catch (Exception $e) { + if ($this->params['debug']) { + echo 'OAuth Error'; + dump($accessToken); + dump($e); + dump($e->getMessage()); + } + $this->auth_redirectToFailed('{{ oauth.error.code }}'); + $this->params['warning_messages'][] = ('' != $this->getElement(3)) ? $this->getElement(3) : '{{ oauth.error.code }}'; + return; + } + } else { + $this->auth_redirectToFailed('{{ oauth.error.no_code }}'); + $this->params['warning_messages'][] = ('' != $this->getElement(3)) ? $this->getElement(3) : '{{ oauth.error.no_code }}'; + return; + } + break; + + case 'init': + $authorizationUrl = $provider->getAuthorizationUrl(); + rex_ycom_auth::setSessionVar('OAUTH2_oauth2state', $provider->getState()); + rex_ycom_auth::setSessionVar('OAUTH2_oauth2returnTo', $returnTo); + rex_response::sendCacheControl(); + rex_response::sendRedirect($authorizationUrl); + } + + $this->auth_createOrUpdateYComUser($Userdata, $returnTo); + } + + public function getDescription(): string + { + return 'ycom_auth_oauth2_twitch|label|error_msg|[allowed returnTo domains: DomainA,DomainB]|default Userdata as Json{"ycom_groups": 3, "termsofuse_accepted": 1}|direct_link 0,1'; + } +} diff --git a/plugins/auth/ytemplates/bootstrap/value.ycom_auth_oauth2_github.tpl.php b/plugins/auth/ytemplates/bootstrap/value.ycom_auth_oauth2_github.tpl.php new file mode 100644 index 00000000..541e687e --- /dev/null +++ b/plugins/auth/ytemplates/bootstrap/value.ycom_auth_oauth2_github.tpl.php @@ -0,0 +1,11 @@ +name . '" href="' . $url . '">' . $this->name . ''; diff --git a/plugins/auth/ytemplates/bootstrap/value.ycom_auth_oauth2_google.tpl.php b/plugins/auth/ytemplates/bootstrap/value.ycom_auth_oauth2_google.tpl.php new file mode 100644 index 00000000..541e687e --- /dev/null +++ b/plugins/auth/ytemplates/bootstrap/value.ycom_auth_oauth2_google.tpl.php @@ -0,0 +1,11 @@ +name . '" href="' . $url . '">' . $this->name . ''; diff --git a/plugins/auth/ytemplates/bootstrap/value.ycom_auth_oauth2_twitch.tpl.php b/plugins/auth/ytemplates/bootstrap/value.ycom_auth_oauth2_twitch.tpl.php new file mode 100644 index 00000000..541e687e --- /dev/null +++ b/plugins/auth/ytemplates/bootstrap/value.ycom_auth_oauth2_twitch.tpl.php @@ -0,0 +1,11 @@ +name . '" href="' . $url . '">' . $this->name . '';