Skip to content

Commit

Permalink
Add github via OAuth2 auth as login method
Browse files Browse the repository at this point in the history
- adds value for yform Formbuilder
- adds settings file for oauth2_github
- add special Provider for github
- add documentation
  • Loading branch information
elricco committed Sep 23, 2023
1 parent fbf059d commit 61c7bec
Show file tree
Hide file tree
Showing 9 changed files with 415 additions and 3 deletions.
3 changes: 2 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@
"league/oauth2-client": "^2",
"psr/log": "^1",
"vertisan/oauth2-twitch-helix": "^2.0",
"league/oauth2-google": "^4.0"
"league/oauth2-google": "^4.0",
"league/oauth2-github": "^3.1"
},
"replace": {
"psr/container": "*",
Expand Down
68 changes: 67 additions & 1 deletion composer.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

25 changes: 25 additions & 0 deletions docs/08_extern_auth.md
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,31 @@ ycom_auth_oauth2_google|label|error_msg|[allowed returnTo domains: DomainA,Domai
#### 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

Expand Down
3 changes: 3 additions & 0 deletions plugins/auth/boot.php
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,9 @@
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;
Expand Down
2 changes: 1 addition & 1 deletion plugins/auth/install.php
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@
rex_sql::factory()->setQuery('UPDATE rex_article SET `ycom_auth_type` = `ycom_auth_type` -1');
}

foreach (['saml', 'oauth2', 'oauth2_twitch', 'oauth2_google', '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)) {
Expand Down
7 changes: 7 additions & 0 deletions plugins/auth/install/oauth2_github.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<?php

$settings = [
'clientId' => '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
];
166 changes: 166 additions & 0 deletions plugins/auth/lib/yform/trait_value_auth_oauth2_github.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
<?php

trait rex_yform_trait_value_auth_oauth2_github
{
/**
* @throws rex_exception
* @return array|string[]
*/
private function auth_loadSettings(): array
{
$SettingFile = $this->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<int|string, mixed> $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);
}
}
}
Loading

0 comments on commit 61c7bec

Please sign in to comment.