Viewed   48 times

I'm using ScnSocialAuth ZF2 module to enable social media authentication in my project.

As it uses ZfcUser as one of its dependencies, two DB tables are created/used by default:

CREATE TABLE `user`
(
    `user_id`       INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
    `username`      VARCHAR(255) DEFAULT NULL UNIQUE,
    `email`         VARCHAR(255) DEFAULT NULL UNIQUE,
    `display_name`  VARCHAR(50) DEFAULT NULL,
    `password`      VARCHAR(128) NOT NULL,
    `state`         SMALLINT UNSIGNED
) ENGINE=InnoDB CHARSET="utf8";

and

CREATE TABLE IF NOT EXISTS `user_provider` (
  `user_id` INT NOT NULL,
  `provider_id` varchar(50) NOT NULL,
  `provider` varchar(255) NOT NULL,
  PRIMARY KEY (`user_id`,`provider_id`),
  UNIQUE KEY (`provider_id`,`provider`),
  FOREIGN KEY (`user_id`) REFERENCES `user` (`user_id`)
) ENGINE=InnoDB;

I'd like to add more fields to both tables.

The first step was to alter these tables in my DB server.

Next, I've override User and UserProvider entities/classes (adding properties and their getters and setters) and set up the config files (zfcuser.global.php and scn-social-auth.global.php) so they make use of these new entities. Up to here everything runs smoothly.

Now I want to get user's profile data and save them into these tables at auth time (even using this data to populate a new user record or to update an existing one).

Looking at the ScnSocialAuthAuthenticationAdapterHybridAuth::authenticate() method I've realized that, when the user is authenticated, only the following fields are set:

user.email
user.password
user.display_name
user_provider.user_id
user_provider.provider_id
user_provider.provider

leaving my added fields (for example: gender, birth_day, region, etc) blank.

Another thing I've noticed, by checking how ScnSocialAuthAuthenticationAdapterHybridAuth class works, is that the entities are hydrated in a "hardcoded" way. For example:

protected function facebookToLocalUser($userProfile)
    {
        ...

        $localUser = $this->instantiateLocalUser();
        $localUser->setEmail($userProfile->emailVerified)
            ->setDisplayName($userProfile->displayName)
            ->setPassword(__FUNCTION__);
        $result = $this->insert($localUser, 'facebook', $userProfile);

        return $localUser;
    }

Can anyone explain which is the best way to extend/override the authenticate() method so I could set these new fields with the values from user's profile? Of course, without modifying the original method.

Thanks

 Answers

5

The solution was given in this issue, opened in github. https://github.com/SocalNick/ScnSocialAuth/issues/202

Thursday, December 8, 2022
 
jong
 
1

To be honest, I don't think it is a good idea to block every page for a non-authenticated user. How would you access the login page?

That said, you must know the page being accessed, to make a whitelist of pages accessible for anonymous visitors. To start, I'd suggest to include the login page. You can check pages the easiest by using their route. So check the current matched route against the whitelist. If blocked, act upon. Otherwise, do nothing.

An example would be inside a Module.php from a module, for example your application:

namespace Application;

use ZendMvcMvcEvent;
use ZendMvcRouterRouteMatch;

class Module
{
    protected $whitelist = array('zfcuser/login');

    public function onBootstrap($e)
    {
        $app = $e->getApplication();
        $em  = $app->getEventManager();
        $sm  = $app->getServiceManager();

        $list = $this->whitelist;
        $auth = $sm->get('zfcuser_auth_service');

        $em->attach(MvcEvent::EVENT_ROUTE, function($e) use ($list, $auth) {
            $match = $e->getRouteMatch();

            // No route match, this is a 404
            if (!$match instanceof RouteMatch) {
                return;
            }

            // Route is whitelisted
            $name = $match->getMatchedRouteName();
            if (in_array($name, $list)) {
                return;
            }

            // User is authenticated
            if ($auth->hasIdentity()) {
                return;
            }

            // Redirect to the user login page, as an example
            $router   = $e->getRouter();
            $url      = $router->assemble(array(), array(
                'name' => 'zfcuser/login'
            ));

            $response = $e->getResponse();
            $response->getHeaders()->addHeaderLine('Location', $url);
            $response->setStatusCode(302);

            return $response;
        }, -100);
    }
}
Friday, September 9, 2022
 
supdun
 
5

The problem has been solved thanks to @samsonasik comment to another question.

public function checkAuth(MvcEvent $e){
    # ... get auth service 
    if (!$auth->hasIdentity()) {
        $e->getRouteMatch()
                     ->setParam('controller', 'zfcuser')
                     ->setParam('action', 'login');
    }
    return;
}

So I have changed route without external redirection.

Monday, December 12, 2022
 
2

You have to measure the children before trying to get its height and width. And in onLayout, you are decide the position of the child. After measuring, you will have the height and width of all children, then with the help of it, you can position the children. You can set its left, right, top, bottom points based on it height and width. For example, if you want to position the child at (0,0), then in onLayout

child.layout(0,0,child.getMeasuredWidth(), child.getMeasuredHeight());

If right and bottom points are mentioned wrong, the the view may cut/enlarge/shrink.

Thursday, December 22, 2022
 
3

To get the behavior you want you need to wait for the process to finish before you exit Main(). To be able to tell when your process is done you need to return a Task instead of a void from your function, you should never return void from a async function unless you are working with events.

A re-written version of your program that works correctly would be

class Program
{
    static void Main(string[] args)
    {
        Debug.WriteLine("Calling DoDownload");
        var downloadTask = DoDownloadAsync();
        Debug.WriteLine("DoDownload done");
        downloadTask.Wait(); //Waits for the background task to complete before finishing. 
    }

    private static async Task DoDownloadAsync()
    {
        WebClient w = new WebClient();

        string txt = await w.DownloadStringTaskAsync("http://www.google.com/");
        Debug.WriteLine(txt);
    }
}

Because you can not await in Main() I had to do the Wait() function instead. If this was a application that had a SynchronizationContext I would do await downloadTask; instead and make the function this was being called from async.

Tuesday, December 20, 2022
Only authorized users can answer the search term. Please sign in first, or register a free account.
Not the answer you're looking for? Browse other questions tagged :