Skip to content

Commit

Permalink
Add SimpleLogin SSO (#762)
Browse files Browse the repository at this point in the history
Co-authored-by: e-five <[email protected]>
  • Loading branch information
debounced and e-five256 authored May 14, 2024
1 parent 60ebebd commit 3e9fb21
Show file tree
Hide file tree
Showing 14 changed files with 412 additions and 2 deletions.
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
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 ' ';
}
}
58 changes: 58 additions & 0 deletions src/Provider/SimpleLoginResourceOwner.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
<?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 getName()
{
return $this->getResponseValue('name');
}

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

0 comments on commit 3e9fb21

Please sign in to comment.