joedawson's avatar

SSO Server with dedicated Socialite Provider

Hello all,

I'm so sorry for this super long thread, but I wanted to provide as much information as possible to keep the amount of back and forth to a minimum :)

I recently posted a thread asking how I should go about establishing some kind of connection between multiple Laravel applications - thankfully I was nudged in the direction of a dedicated SSO application.

I now, currently have three applications - one of which is my SSO application - the other two are SAAS apps. Here's a quick overview of each one.

  • Hub - The dedicated SSO application. This is where all of the users and their roles will be managed. I'm using the lucadegasperi/oauth2-server-laravel package to do this.
  • Grub - Very small ecommerce app we use to keep track of "snacks" in-house.
  • Safe - A password management app. Of which is currently still in development, so I won't really go into any details about this as it'll follow the same suit as the Grub app - using the dedicated Socialite driver.

I'm having a few problems at the moment, where the token is return null and therefore leading to further issues where it's unable to fetch the user. Here's what I currently have.

Hub

As mentioned above, I have the lucadegasperi/oauth2-server-laravel package installed, everything has been migrated, I believe I have everything configured correctly and I have chosen my grant - the Authorization code grant.

After following the implementation guide for the Auth code grant - my routes for the Hub app look a little something like this.

// Me
Route::get('me', ['as' => 'oauth.me', 'uses' => 'Auth\OAuthController@getMe']);

// OAuth & Home
Route::group(['middleware' => 'auth'], function() {

    Route::get('oauth/authorize', 'Auth\OAuthController@getAuthorize');
    Route::post('oauth/authorize', ['as' => 'oauth.authorize', 'uses' => 'Auth\OAuthController@postAuthorize']);
    Route::post('oauth/token', ['as' => 'oauth.token', 'uses' => 'Auth\OAuthController@postToken']);

    Route::get('/', ['as' => 'hub', 'uses' => 'HubController@hub']);

});

Which leads me to the OAuthController - of which looks like so:

class OAuthController extends Controller
{
    /**
     * OAuth Constructor
     */
    public function __construct()
    {
        $this->middleware('check-authorization-params', [
            'only' => ['getAuthorize', 'postAuthorize']
        ]);
    }

    /**
     * Display the Authorization Form
     * 
     * @return Response
     */
    public function getAuthorize()
    {
        $authParams = Authorizer::getAuthCodeRequestParams();

        $formParams = array_except($authParams,'client');

        $formParams['client_id'] = $authParams['client']->getId();

        $formParams['scope'] = implode(config('oauth2.scope_delimiter'), array_map(function ($scope) {
            return $scope->getId();
        }, $authParams['scopes']));

        return view('auth.authorize')->with(['params' => $formParams, 'client' => $authParams['client']]);
    }

    /**
     * Handle the Authorization
     * 
     * @param  Request $request
     * @return Response
     */
    public function postAuthorize(Request $request)
    {
        $params = Authorizer::getAuthCodeRequestParams();
        $params['user_id'] = auth()->id();
        $redirectUri = '/';

        $redirectUri = Authorizer::issueAuthCode('user', $params['user_id'], $params);

        return redirect()->to($redirectUri);
    }

    /**
     * Issue the Access Token
     * 
     * @param  Request $request
     * @return Resonse
     */
    public function postToken(Request $request)
    {
        return response()->json(Authorizer::issueAccessToken());
    }

    /**
     * Return the Authorized User
     * 
     * @return Response
     */
    public function getMe()
    {
        $user = auth()->user();

        return response()->json($user);
    }
}

I'm not so sure I have the getMe() method set up correctly - because this isn't mentioned anywhere in the docs. So I'm just guessing how to do it there.

I've then added a sample client to the oauth_clients table and set up the redirect on my oauth_client_endpoints table as seen below.

hub

Grub

I recently saw a reply from @martinbean's, of which he goes on to speak of setting up a dedicated Socialite provider - which I proceeded to do, you can view the source here

Then added an impact service to my services.php config file, which looks like so:

'impact' => [
    'url' => 'http://hub.dev',
    'client' => 'GrubID',
    'secret' => 'GrubSecret',
    'redirect' => 'http://grub.dev/test/callback'
]

Of course, all for testing purposes right now. I'll obviously create proper id's and secret's in the future.

I then set up some dummy routes to check everything is working...

Route::get('test', function() {

    return \Socialite::driver('impact')->redirect();

});

Route::get('test/callback', function(\Illuminate\Http\Request $request) {

    if($request->has('code'))
    {
        $user = \Socialite::driver('impact')->user();

        dd($user);
    }

});

I then go hit the http://grub.dev/test route, I'm redirect to http://hub.dev/oauth/authorize with all the parameters as expected - to unfortunately get the following error:

Missing argument 2 for array_get(), called in /home/vagrant/Code/grub/packages/Impact/Socialite/src/ImpactSocialite.php on line 70 and defined

Which if you was to view the Socialite provider's source, is in my mapUserToObject() method.

protected function mapUserToObject(array $user)
{
    return (new User)->setRaw($user)->map([
        'id' => array_get('id'),
        'name' => array_get($user, 'name'),
        'email' => array_get($user, 'email')
    ]);
}

But if I do a dd() on the $user, it returns an empty array - leading me to believe I'm not getting a user back from my SSO application? Why might this be?

I hope you can help!

0 likes
11 replies
d3xt3r's avatar

Will this help

/**
     * {@inheritdoc}
     */
    protected function mapUserToObject(array $user)
    {
        return (new User)->setRaw($user)->map([
            'id' => $user['id'], // or if you are using sso id array_get($user,'id')
            'name' => array_get($user, 'name'),
            'email' => array_get($user, 'email')
        ]);
    }
joedawson's avatar

Unfortunately not @premsaurav, when the mapUserToObject() gets called - the $user is an empty array, meaning it's not getting the user from my SSO application. But I'm not sure why :/

d3xt3r's avatar

@JoeDawson Can debug though.

/**
     * {@inheritdoc}
     */
    protected function getUserByToken($token)
    {
    dd($token); // Do you see a proper access token

        $userUrl = $this->url . '/me?access_token='.$token; // try this route in browser and see if you are getting a proper response object

        $response = $this->getHttpClient()->get($userUrl);

        return json_decode($response->getBody(), true);
    }
joedawson's avatar

@premsaurav unfortunately that is also null as I mentioned in my OP, my issue is more with the SSO server (Hub) rather than my Socialite provider right now...

d3xt3r's avatar

@JoeDawson

 public function getMe()
    {
        // $user = auth()->user(); // This will always be null when fetched from curl without cookies
    $user = User::find(Authorizer::getResourceOwnerId());
        return response()->json($user);
    }

and add this to the constructor

 $this->middleware('oauth', ['only' => ['getMe']]);
 $this->middleware('oauth-user', ['only' => ['getMe']]);
joedawson's avatar

@premsaurav thanks for your help so far. I've added that - still unfortunately getting an null token.

I get this error when I'm redirected to my callback

Client error: GET http://hub.dev/me?access_token= resulted in a 400 Bad Request response: {"error":"invalid_request","error_description":"The request is missing a required parameter, includes an invalid paramet (truncated...)

Which I assume confirms there's a a null token - got any ideas why?

d3xt3r's avatar
d3xt3r
Best Answer
Level 29

@JoeDawson Yes,

/ OAuth & Home
Route::group(['middleware' => 'auth'], function() {

    Route::get('oauth/authorize', 'Auth\OAuthController@getAuthorize');
    Route::post('oauth/authorize', ['as' => 'oauth.authorize', 'uses' => 'Auth\OAuthController@postAuthorize']);
    //Route::post('oauth/token', ['as' => 'oauth.token', 'uses' => 'Auth\OAuthController@postToken']);

    Route::get('/', ['as' => 'hub', 'uses' => 'HubController@hub']);

});

Route::post('oauth/token', ['as' => 'oauth.token', 'uses' => 'Auth\OAuthController@postToken']);

d3xt3r's avatar

@JoeDawson :) I was supposed to do this for one of my projects but was lagging behind. In order to debug yours, mine implementation is also done. cheers .. :)

1 like

Please or to participate in this conversation.