Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add SimpleLogin SSO #762

Merged
18 commits merged into from
May 14, 2024
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,8 @@ OAUTH_KEYCLOAK_SECRET=
OAUTH_KEYCLOAK_URI=
OAUTH_KEYCLOAK_REALM=
OAUTH_KEYCLOAK_VERSION=
OAUTH_SIMPLELOGIN_ID=
OAUTH_SIMPLELOGIN_SECRET=
OAUTH_ZITADEL_ID=
OAUTH_ZITADEL_SECRET=
OAUTH_ZITADEL_BASE_URL=
Expand Down
2 changes: 2 additions & 0 deletions .env.example_docker
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,8 @@ OAUTH_KEYCLOAK_SECRET=
OAUTH_KEYCLOAK_URI=
OAUTH_KEYCLOAK_REALM=
OAUTH_KEYCLOAK_VERSION=
OAUTH_SIMPLELOGIN_ID=
OAUTH_SIMPLELOGIN_SECRET=
OAUTH_ZITADEL_ID=
OAUTH_ZITADEL_SECRET=
OAUTH_ZITADEL_BASE_URL=
Expand Down
10 changes: 10 additions & 0 deletions config/kbin_routes/security.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,16 @@ oauth_keycloak_verify:
path: /oauth/keycloak/verify
methods: [ GET ]

oauth_simplelogin_connect:
controller: App\Controller\Security\SimpleLoginController::connect
path: /oauth/simplelogin/connect
methods: [ GET ]

oauth_simplelogin_verify:
controller: App\Controller\Security\SimpleLoginController::verify
path: /oauth/simplelogin/verify
methods: [ GET ]

oauth_zitadel_connect:
controller: App\Controller\Security\ZitadelController::connect
path: /oauth/zitadel/connect
Expand Down
7 changes: 7 additions & 0 deletions config/packages/knpu_oauth2_client.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,13 @@ knpu_oauth2_client:
version: '%oauth_keycloak_version%'
redirect_route: oauth_keycloak_verify
redirect_params: { }
simplelogin:
type: generic
client_id: '%oauth_simplelogin_id%'
client_secret: '%oauth_simplelogin_secret%'
redirect_route: oauth_simplelogin_verify
redirect_params: { }
provider_class: 'App\Provider\SimpleLogin'
zitadel:
type: generic
client_id: '%oauth_zitadel_id%'
Expand Down
1 change: 1 addition & 0 deletions config/packages/security.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ security:
- App\Security\GoogleAuthenticator
- App\Security\GithubAuthenticator
- App\Security\KeycloakAuthenticator
- App\Security\SimpleLoginAuthenticator
- App\Security\ZitadelAuthenticator
logout:
enable_csrf: true
Expand Down
3 changes: 3 additions & 0 deletions config/services.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,9 @@ parameters:
oauth_keycloak_realm: "%env(OAUTH_KEYCLOAK_REALM)%"
oauth_keycloak_version: "%env(OAUTH_KEYCLOAK_VERSION)%"

oauth_simplelogin_id: "%env(default::OAUTH_SIMPLELOGIN_ID)%"
oauth_simplelogin_secret: "%env(OAUTH_SIMPLELOGIN_SECRET)%"

oauth_zitadel_id: "%env(default::OAUTH_ZITADEL_ID)%"
oauth_zitadel_secret: "%env(OAUTH_ZITADEL_SECRET)%"
oauth_zitadel_base_url: "%env(OAUTH_ZITADEL_BASE_URL)%"
Expand Down
29 changes: 29 additions & 0 deletions migrations/Version20240503224350.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<?php

declare(strict_types=1);

namespace DoctrineMigrations;

use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;

/**
* Auto-generated Migration: Please modify to your needs!
*/
final class Version20240503224350 extends AbstractMigration
{
public function getDescription(): string
{
return 'Add SimpleLogin SSO';
}

public function up(Schema $schema): void
{
$this->addSql('ALTER TABLE "user" ADD oauth_simple_login_id VARCHAR(255) DEFAULT NULL');
}

public function down(Schema $schema): void
{
$this->addSql('ALTER TABLE "user" DROP oauth_simple_login_id');
}
}
28 changes: 28 additions & 0 deletions src/Controller/Security/SimpleLoginController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?php

declare(strict_types=1);

namespace App\Controller\Security;

use App\Controller\AbstractController;
use KnpU\OAuth2ClientBundle\Client\ClientRegistry;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;

class SimpleLoginController extends AbstractController
{
public function connect(ClientRegistry $clientRegistry): Response
{
return $clientRegistry
->getClient('simplelogin')
->redirect([
'openid',
'email',
'profile',
]);
}

public function verify(Request $request, ClientRegistry $client)
{
}
}
4 changes: 3 additions & 1 deletion src/Entity/User.php
This conversation was marked as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,8 @@ class User implements UserInterface, PasswordAuthenticatedUserInterface, Visibil
#[Column(type: 'string', nullable: true)]
public ?string $oauthKeycloakId = null;
#[Column(type: 'string', nullable: true)]
public ?string $oauthSimpleLoginId = null;
#[Column(type: 'string', nullable: true)]
public ?string $oauthZitadelId = null;
#[Column(type: 'boolean', nullable: false, options: ['default' => true])]
public bool $hideAdult = true;
Expand Down Expand Up @@ -755,7 +757,7 @@ public function removeOAuth2UserConsent(OAuth2UserConsent $oAuth2UserConsent): s

public function isSsoControlled(): bool
{
return $this->oauthAzureId || $this->oauthGithubId || $this->oauthGoogleId || $this->oauthFacebookId || $this->oauthKeycloakId || $this->oauthZitadelId;
return $this->oauthAzureId || $this->oauthGithubId || $this->oauthGoogleId || $this->oauthFacebookId || $this->oauthKeycloakId || $this->oauthSimpleLoginId || $this->oauthZitadelId;
}

public function getCustomCss(): ?string
Expand Down
72 changes: 72 additions & 0 deletions src/Provider/SimpleLogin.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
<?php

declare(strict_types=1);

namespace App\Provider;

use League\OAuth2\Client\Provider\AbstractProvider;
use League\OAuth2\Client\Provider\Exception\IdentityProviderException;
use League\OAuth2\Client\Token\AccessToken;
use League\OAuth2\Client\Tool\BearerAuthorizationTrait;
use Psr\Http\Message\ResponseInterface;

class SimpleLogin extends AbstractProvider
{
use BearerAuthorizationTrait;

protected $baseUrl = 'https://app.simplelogin.io/';

public function __construct(array $options = [], array $collaborators = [])
{
parent::__construct($options, $collaborators);
}

protected function getBaseUrl()
{
return rtrim($this->baseUrl, '/').'/';
}

protected function getAuthorizationHeaders($token = null)
{
return ['Authorization' => 'Bearer '.$token];
}

public function getBaseAuthorizationUrl()
{
return $this->getBaseUrl().'oauth2/authorize';
}

public function getBaseAccessTokenUrl(array $params)
{
return $this->getBaseUrl().'oauth2/token';
}

public function getResourceOwnerDetailsUrl(AccessToken $token)
{
return $this->getBaseUrl().'oauth2/userinfo';
}

protected function getDefaultScopes()
{
return ['openid', 'profile', 'email'];
}

protected function checkResponse(ResponseInterface $response, $data)
{
if (!empty($data['error'])) {
$error = htmlentities($data['error'], ENT_QUOTES, 'UTF-8');
$message = htmlentities($data['error_description'], ENT_QUOTES, 'UTF-8');
throw new IdentityProviderException($message, $response->getStatusCode(), $response);
}
}

protected function createResourceOwner(array $response, AccessToken $token)
{
return new SimpleLoginResourceOwner($response);
}

protected function getScopeSeparator()
{
return ' ';
}
}
53 changes: 53 additions & 0 deletions src/Provider/SimpleLoginResourceOwner.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
<?php

declare(strict_types=1);

namespace App\Provider;

use League\OAuth2\Client\Provider\ResourceOwnerInterface;

class SimpleLoginResourceOwner implements ResourceOwnerInterface
{
protected $response;

public function __construct(array $response)
{
$this->response = $response;
}

public function getId()
{
return $this->getResponseValue('sub');
}

public function getEmail()
{
return $this->getResponseValue('email');
}

public function getPictureUrl()
{
return $this->getResponseValue('avatar_url');
}

public function toArray()
{
return $this->response;
}

protected function getResponseValue($key)
{
$keys = explode('.', $key);
$value = $this->response;

foreach ($keys as $k) {
if (isset($value[$k])) {
$value = $value[$k];
} else {
return null;
}
}

return $value;
}
}
Loading
Loading