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

Login via social media #3154

Open
wants to merge 11 commits into
base: 15.0
Choose a base branch
from
Open

Login via social media #3154

wants to merge 11 commits into from

Conversation

sspooky13
Copy link
Contributor

@sspooky13 sspooky13 commented May 4, 2024

Q A
Description, reason for the PR Implementation of login via social media (Facebook, Google, Seznam)
New feature Yes
BC breaks Yes
Fixes issues ...
Have you read and signed our License Agreement for contributions? Yes

🌐 Live Preview:

@sspooky13 sspooky13 force-pushed the pt-login-with-social-media branch 2 times, most recently from 8e15c2b to 433672b Compare May 4, 2024 14:08
Copy link
Member

@henzigo henzigo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hello, here are some of my thoughts.

I'm missing one feature. On some platforms, you can see connected networks in your profile and you can remove this connection. It should be more likely addressed to Bussines, but I would love this feature.

Comment on lines 37 to 25
public function createConfig(string $redirectUrl): array
{
return [
'callback' => $redirectUrl,
'providers' => [
self::PROVIDER_GOOGLE => [
'enabled' => true,
'keys' => [
'id' => $this->googleClientId,
'secret' => $this->googleClientSecret,
],
],
self::PROVIDER_FACEBOOK => [
'enabled' => true,
'keys' => [
'id' => $this->facebookClientId,
'secret' => $this->facebookClientSecret,
],
],
self::PROVIDER_SEZNAM => [
'enabled' => true,
'keys' => [
'id' => $this->seznamClientId,
'secret' => $this->seznamClientSecret,
],
'scope' => 'identity',
'adapter' => Seznam::class,
],
],
];
}
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be nice to have this configuration via yaml in project-base. It should be possible to create some Configuration wrapper and then parse it to some structure.

The main purpose of this change is to give programmers an easier way to work with this feature and only one place where you can configure this feature.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed in 68ae66f

@@ -1,5 +1,5 @@
monolog:
channels: ["cron", "queue", "slow"]
channels: ["cron", "queue", "slow", "socialNetwork"]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there any benefit to having a custom monolog channel only for social networks?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yep, you can filter your social network messages in kibana by, e.g.: message: socialNetwork.INFO or message: socialNetwork.ERROR etc. but yes, you can do it same filter by message 🤷🤷‍♂️🤷‍♀️. If you don't use filter this way I can remove it

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this is necessary, but it's up to you

defaults: { _controller: App\Controller\Front\SocialNetworkController::loginAction }
path: /social-network/login/{type}
requirements:
type: Google|Facebook|Seznam #copy from /packages/framework/src/Model/SocialNetwork/SocialNetworkConfigFactory.php
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is another part that you need to edit as a programmer if you want to add/remove any type of login. It would be nice to enable all types (or simply anything) here and check enabled types directly in controller

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

changed in 68ae66f

@@ -0,0 +1,80 @@
<?php
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we move this controller into the Framework package? I know that this is primarily used for Frontend, but I don't see any benefit to having this class in project-base. This file will probably not be edited on projects.

Comment on lines 37 to 45
const [socialNetworkLoginFacebook, socialNetworkLoginGoogle, socialNetworkLoginSeznam] =
getInternationalizedStaticUrls(
[
{ url: '/social-network/login/:type', param: 'Facebook' },
{ url: '/social-network/login/:type', param: 'Google' },
{ url: '/social-network/login/:type', param: 'Seznam' },
],
url,
);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be nice to provide those data in SettingsQuery. Now you have to synchronize FE and BE separately. What if some project will want to have social logins only on some domains? Then you need to duplicate logic on both sides.

Comment on lines 103 to 116

/**
* @param \App\Model\Customer\User\CustomerUser $customerUser
* @return array{accessToken: string, refreshToken: string}
*/
public function loginAndReturnAccessAndRefreshToken(CustomerUser $customerUser): array
{
$deviceId = Uuid::uuid4()->toString();

return [
'accessToken' => $this->tokenFacade->createAccessTokenAsString($customerUser, $deviceId),
'refreshToken' => $this->tokenFacade->createRefreshTokenAsString($customerUser, $deviceId),
];
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can be moved to the parent class.

@@ -61,4 +64,25 @@ public function createWithArgument(Argument $argument): RegistrationData

return $registrationData;
}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems like you should be able to move this file into the Framework package easily. If you do that it will be easier to maintain this file for us.

@@ -0,0 +1,63 @@
<?php
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This file should be in the Framework bundle as well.


namespace Shopsys\FrameworkBundle\Model\SocialNetwork\Exception;

interface SocialNetworkExceptionInterface
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this interface in not necessary (we do not create the interfaces anymore, unless there is a valid use-case which I do not see here)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

deleted in 68ae66f


class SocialNetworkConfigFactory
{
public const PROVIDER_GOOGLE = 'Google';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
public const PROVIDER_GOOGLE = 'Google';
public const string PROVIDER_GOOGLE = 'Google';

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed in 68ae66f

class SocialNetworkConfigFactory
{
public const PROVIDER_GOOGLE = 'Google';
public const PROVIDER_FACEBOOK = 'Facebook';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
public const PROVIDER_FACEBOOK = 'Facebook';
public const string PROVIDER_FACEBOOK = 'Facebook';

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed in 68ae66f

{
public const PROVIDER_GOOGLE = 'Google';
public const PROVIDER_FACEBOOK = 'Facebook';
public const PROVIDER_SEZNAM = 'Seznam';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
public const PROVIDER_SEZNAM = 'Seznam';
public const string PROVIDER_SEZNAM = 'Seznam';

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed in 68ae66f

* @param string $seznamClientId
* @param string $seznamClientSecret
*/
public function __construct(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TBH, I am not a fan of this solution...I think it is not ideal for future extensions and adding more providers.

I have an idea that I would like to discuss - what about creating a configuration yaml in project-base that would be loaded here and processed? It could look like this:

parameters:
    social_network_login_config:
        providers:
            google:
                enabled: true
                id: '%env(GOOGLE_CLIENT_ID)%'
                secret: '%env(GOOGLE_CLIENT_SECRET)%'
            facebook:
                enabled: true
                id: '%env(FACEBOOK_CLIENT_ID)%'
                secret: '%env(FACEBOOK_CLIENT_SECRET)%'
...

Then, SocialNetworkConfigFactory construct would accept just the config array:

public function __construct(array $socialNetworkLoginConfig)
services.yaml:

Shopsys\FrameworkBundle\Model\SocialNetwork\SocialNetworkConfigFactory:
    arguments:
         $socialNetworkLoginConfig: '%social_network_login_config%'

The SocialNetworkConfigFactory::createConfig() method would dynamically create the config from the input array or the factory would not be needed anymore if the array in the yaml file is defined precisely 🤔 When typing this comment, I realized this is something I would expect from a Symfony bundle so I tried to search the bundle and I found this - https://github.com/ITLized/social - it seems like it is not maintained but the idea with the config in yaml was implemented there 🤔

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If config in yaml, I would rather create a true framework bundle config than use parameters and have to validate them by hand – you can see inspiration in 9636a0f

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@grossmannmartin Honestly, solution from Rosta seems to me better because creating "yaml configuration" (or how to name it) is not good with this solution because with configuration you tell what can be used and what is expected will come. This library for login by social network has many solutions and I can't all of them solve + if library whatever change you have to update your configuration ++ if on project you need to do something that is not expected you have to edit this configuration too. Because I don't think creating configuration is not good solution and I accept solution of creating only yaml file where I will send only config as array as Rosta suggest.

Do you agree or do you want to call so I can better describe issue with configuration solution? 🙏

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

changed by discussion and suggestion by Rosta, see 68ae66f. After discussion, I take it's OK now, if not let me know

* @param \Hybridauth\User\Profile $profile
* @return \App\Model\Customer\User\RegistrationData
*/
public function createFromSocialNetworkProfile(Profile $profile): RegistrationData
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this should be implemented in the framework package 😇

$registrationData->street = '';
$registrationData->city = '';
$registrationData->postcode = '';
$registrationData->country = $countries[0];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know this is an edge case but what if there is no enabled country on a domain?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

as we discussed, It's very little edge case and it will be solved in next follow-up task

$registrationData->city = '';
$registrationData->postcode = '';
$registrationData->country = $countries[0];
$registrationData->password = $this->hashGenerator->generateHashWithoutConfusingCharacters(CustomerUserPasswordFacade::MINIMUM_PASSWORD_LENGTH);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When the password policy is changed on a project (e.g. special characters are required), this implementation might be a problem, right? Even at this moment, it feels like it might lead to quite weak passwords 🤔 Let's discuss it, please

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed in 68ae66f

* @param \App\Model\Customer\User\CustomerUser $customerUser
* @return array{accessToken: string, refreshToken: string}
*/
public function loginAndReturnAccessAndRefreshToken(CustomerUser $customerUser): array
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it should not be in project-base 🙂 Moreover, I do not feel like putting this functionality into the existing LoginAsUserFacade - the class is used when you need to login administrator as a user. IMHO it is worth creating a separate class for your purpose. But we can discuss it if you want

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed in 313cf3c as we agreed

@@ -42,7 +42,7 @@ server {
fastcgi_param SCRIPT_FILENAME $realpath_root/resolveFriendlyUrl.php;
}

location ~ ^\/(admin|public|content(-test)?|ckeditor|build|bundles|graphql|_profiler|_wdt|file|redirect|elfinder|efconnect|personal-overview-export|codeception-acceptance-test|_error) {
location ~ ^\/(admin|public|content(-test)?|ckeditor|build|bundles|graphql|_profiler|_wdt|file|redirect|elfinder|efconnect|personal-overview-export|codeception-acceptance-test|_error|social-network) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

https://github.com/shopsys/deployment/blob/main/kubernetes/configmap/nginx.yaml needs to be edited in the same manner.

Then, you need to release a new version of the deployment package and require it in your composer.json
We can discuss this as well 😉

*/
public function loginAction(Request $request, string $type): Response
{
if (in_array($type, SocialNetworkConfigFactory::ALLOWED_PROVIDERS, true) === false) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are you checking types agains some hard defined array instead of checking if type is in social_network_login_config configuration?

I would like to see that if you want to add for example Apple ID, you can do that just by configuration (and probably you will have to add some images to SF) but thats all. I don't like the idea when you have configuration for some service and then you have same information somewhere in codebase

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hmm it true, it's not necessary check type, that library will throw exception if you send something not supported or disabled or..., deleted in 7f060b7

@sspooky13 sspooky13 force-pushed the pt-login-with-social-media branch 7 times, most recently from 3c8f547 to 7e6490e Compare May 28, 2024 08:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

6 participants