SocialiteProviders / Providers

A Collection of Providers for Laravel Socialite
https://socialiteproviders.com
MIT License
504 stars 440 forks source link

400 Bad Request with graph setTenantId() #337

Closed warlord0 closed 5 years ago

warlord0 commented 5 years ago

Laravel v5.8, "socialiteproviders/microsoft-graph": "^2.1"

If I setup my key as multitenant I can get authorised. But when I change it to single tenant and try to add in the tenant Id as I don't want every man and his dog logging in, I get the following error in Laravel.

 GuzzleHttp \ Exception \ ClientException (400)
Client error: `POST https://login.microsoftonline.com/common/oauth2/v2.0/token` resulted in a `400 Bad Request` response: {"error":"invalid_request","error_description":"AADSTS50194: Application 'dc1efa87-385e-4d90-83a4-b1567350c196'(Laravel- (truncated...) 

Which seems kind of strange. I was expecting the part of the url /common/ to be my tenant Id. In my .env I have the following

GRAPH_KEY=dc1efa87-385e-4d90-83a4-b1567350c196
GRAPH_SECRET=[protectingtheinnocent]
GRAPH_TENANT_ID=[protectingtheinnocent]
GRAPH_REDIRECT_URI=http://localhost:8000/login/microsoft/callback

Which in turn I'm using this in my config/services.php:

    'graph' => [
        'client_id' => env('GRAPH_KEY'),
        'client_secret' => env('GRAPH_SECRET'),
        'tenant_id' => env('GRAPH_TENANT_ID'),
        'redirect' => env('GRAPH_REDIRECT_URL'),
    ],

These are my routes:

Route::get('login/microsoft', 'Auth\LoginController@redirectToProvider');
Route::get('login/microsoft/callback', 'Auth\LoginController@handleProviderCallback');

My LoginController.php:

    /**
     * Redirect the user to the o365 authentication page.
     *
     * @return \Illuminate\Http\Response
     */
    public function redirectToProvider()
    {
        return Socialite::with('graph')
            ->setTenantId(env('GRAPH_TENANT_ID'))
            ->redirect();
    }

    /**
     * Obtain the user information from o365.
     *
     * @return \Illuminate\Http\Response
     */
    public function handleProviderCallback()
    {
        $user = Socialite::driver('graph')->user();

        echo 'Looks like we got there as ' . $user->email;
        // $user->token;
    }

I've also left out the setTenantId() and get the error:

AADSTS50194: Application 'dc1efa87-385e-4d90-83a4-b1567350c196'(Laravel-O365 Test) is not configured as a multi-tenant application. Usage of the /common endpoint is not supported for such applications created after '10/15/2018'. Use a tenant-specific endpoint or configure the application to be multi-tenant.

Without using setTenantId() I thought it would use the config variable? Either way it doesn't work if I specify the tenant Id as setTenantId('tenant-id') or rely upon the tenant_id variable.

Can you point me in the right direction?

Many thanks.

warlord0 commented 5 years ago

Hmm, after some poking around in the Provider.php I dropped in some dd()'s

    protected function getAuthUrl($state)
    {
        dd($this->buildAuthUrlFromBase(
            sprintf(
                'https://login.microsoftonline.com/%s/oauth2/v2.0/authorize',
                $this->getTenantId()
            ),
            $state
        ));

results in the corrent Url:

https://login.microsoftonline.com/[my-tenant-id]/oauth2/v2.0/authorize?client_id=[my-client-id]&scope=User.Read&response_type=code&state=[some-state]
    protected function getTokenUrl()
    {
        dd(sprintf(
            'https://login.microsoftonline.com/%s/oauth2/v2.0/token',
            $this->getTenantId()
        ));

Results in the incorrect url. So is it not able to access the variables in $this->getTenantId() or is it too early?

https://login.microsoftonline.com/common/oauth2/v2.0/token
warlord0 commented 5 years ago

I decided to try another browser, in case I'd got some session data or cookie issue. It looks like I am successfully logging in as I get the MS modal dialog asking if I want to remain logged in. If I say no it continues and then I get the 400 error.

So I'm suspecting that after I login it tries to get the token and then that's where the getTokenUrl() is falling over.

I edited Provider.php and hard coded my tenant Id into getTokenUrl(). This worked and successfully finished the logon process. So now I don't know where to look.

    protected function getTokenUrl()
    {
        return sprintf(
            'https://login.microsoftonline.com/%s/oauth2/v2.0/token',
            'my-tenant-id'
            // $this->getTenantId()
        );
    }
harrygulliford commented 5 years ago

I stumbled upon this issue as well when implementing the provider. You also need to set your tenant id in the handleProviderCallback() function too.

public function handleProviderCallback()
{
    $user = Socialite::driver('graph')
        ->setTenantId(config('services.graph.tenant_id'))
        ->user();

    // ...
}

I'd also reccomend swapping out the env() calls to config('services.graph.tenant_id'), which will mean your code won't break if the config is cached.

There's an open PR #332 waiting to be merged which will automatically inject the tenant_id value from app/services.php without messing around with ->setTenantId().

warlord0 commented 5 years ago

->setTenantId(config('services.graph.tenant_id'))

Many, many thanks. Seems obvious when you see it :) So often one line of code is all it is.