TheNetworg / oauth2-azure

Azure AD provider for the OAuth 2.0 Client.
https://packagist.org/packages/thenetworg/oauth2-azure
MIT License
231 stars 109 forks source link

Problem with sending a request to Graph through my API #105

Open QuentinDenisot opened 4 years ago

QuentinDenisot commented 4 years ago

Hi, I have a problem with this package, I am not sure to fully understand how it works. I am working on an old PHP application built as a monolith, but there is also an API on which the monolith connects to.

The application was built in a way that I have to put the code to login within the monolith. So far, it works, the user is redirected to the Microsoft OAuth interface, provides his creditentials then goes back to my application and I log him in. From the monolith I can call any route (/me, /me/calendars, /me/messages, etc.) without any problem.

However I will need to call these routes from the API, and that's where I am struggling. Given that you need a provider and a token to make a call, I decided to save the microsoft access token in the database (access_token, refresh_token and expires_in). Then I tried to do like the following example to re build the access token from what I stored in the database :

https://github.com/TheNetworg/oauth2-azure/issues/44#issuecomment-373051406

It worked on the monolith side, but not in the API unfortunately. This is how I tried to build the access token :

new AccessToken($accessTokenData, $this->provider);

I also tried to store the serialized whole accessToken object, but when I tried to unserialize it, PHP didn't recognize the class (which was loaded in my file with a 'use').

After that, I figured out that the provider (still on the API) didn't look valid. This is what I got when I tried to build the provider :

[
    [urlLogin] => https://login.microsoftonline.com/
    [pathAuthorize] => /oauth2/authorize
    [pathToken] => /oauth2/token
    [scope] => []
    [scopeSeparator] => 
    [tenant] => common
    [urlAPI] => https://graph.windows.net/
    [resource] => 
    [API_VERSION] => 1.6
    [authWithResource] => 1
]

Perhaps I should not have used the following code to build it :

$this->provider = new Azure([
    'clientId'          => $this->clientId,
    'clientSecret'  => $this->clientSecret,
    'redirectUri'    => $this->redirectURI
]);

Yet that's what I used on the monolith side and it had worked. The last thing I tried was to save the initial valid provider (from the monolith) in the database as a serialized object, but like the access token, I was unable to unserialize it, even though the class was loaded in my file.

I hope I've explained my problem clearly enough so that someone can point out my mistake if they find it, or just explain another way to make calls to Graph from another application (my API) without losing data from accessToken / provider.

hajekj commented 4 years ago

When protecting you API, you should follow the on_behlaf_of pattern: https://github.com/TheNetworg/oauth2-azure#protecting-your-api---experimental

QuentinDenisot commented 4 years ago

Thank you, I'll try that. I'll let you know if it solved my problem.

QuentinDenisot commented 4 years ago

I'm back with some news. I've managed to make most of the things work, but I still have one problem.

Actually, I can request /v1.0/me but any other route returns the following error :

Type: League\OAuth2\Client\Provider\Exception\IdentityProviderException
Code: 404
Message: Resource could not be discovered.
File: /vendor/thenetworg/oauth2-azure/src/Provider/Azure.php
Line: 315

That's what happens when I request /v1.0/me/messages or /v1.0/me/calendars for instance. By the way I also use the Microsoft Graph Explorer and these routes are working fine. I use the same account to log into Graph Explorer and my app, so I don't understand why it doesn't work.

Permissions on Azure Active Directory seem to be correct for retrieving the data I need :

Screenshot_4

On the other side when I check the AccessToken I use to make requests, there seem to be nothing missing :

"scope": "Calendars.ReadWrite email openid profile User.Read User.Read.All User.ReadBasic.All",

Everything also seem to be valid with the provider, correct permissions, and as you can see I've changed "urlAPI" and "resource" to "graph.microsoft.com" to reach the correct API :

{
    "urlLogin": "https://login.microsoftonline.com/",
    "pathAuthorize": "/oauth2/authorize",
    "pathToken": "/oauth2/token",
    "scope": [
        "Calendars.ReadWrite email openid profile User.Read User.Read.All User.ReadBasic.All"
    ],
    "scopeSeparator": " ",
    "tenant": "xxxxxxxx-xxxx-xxxxx-xxxx-xxxxxxxxxxxxx",
    "urlAPI": "https://graph.microsoft.com",
    "resource": "https://graph.microsoft.com/",
    "API_VERSION": "1.6",
    "authWithResource": true
}

However I've found out that the data returned from the call in my app is quite different from the data returned by Graph Explorer on the /v1.0/me route.

This is what I get from Graph Explorer :

{
    "@odata.context": "https://graph.microsoft.com/v1.0/$metadata#users/$entity",
    "displayName": "xxxxx xxxxx",
    "surname": "xxxxx",
    "givenName": "xxxxx",
    "id": "xxxxxxxxxxxxxxx",
    "userPrincipalName": "xxxxx.xxxxx@outlook.com",
    "businessPhones": [],
    "jobTitle": null,
    "mail": null,
    "mobilePhone": null,
    "officeLocation": null,
    "preferredLanguage": null
}

And this is from the request on my app :

{
    "@odata.context": "https://graph.microsoft.com/v1.0/$metadata#users/$entity",
    "businessPhones": [],
    "displayName": "xxxxx xxxxx",
    "givenName": "xxxxx",
    "jobTitle": null,
    "mail": null,
    "mobilePhone": null,
    "officeLocation": null,
    "preferredLanguage": "en",
    "surname": "xxxxx",
    "userPrincipalName": "xxxxx.xxxxx_outlook.com#EXT#@xxxxxxxxxxoutlook.onmicrosoft.com",
    "id": "xxxxxxx-xxxxx-xxxx-xxxx-xxxxxxxxxx"
}

The "@odata.context" is the same on both sides, but "id" is different. There is also something weird with the email inside "userPrincipalName" while it looks fine when I check the user on the Active Directory. I feel like the requests are not reaching the same endpoint.

I am pretty sure that it doesn't work properly because of a stupid mistake, hopefully someone will point out to me what I'm not getting.

hajekj commented 4 years ago

Right. The difference is that Microsoft Graph Explorer is using the V2 endpoint and you are using the library configured with V1. In order to get same results, please switch to V2 endpoint as per README. That should return consistent results then.

QuentinDenisot commented 4 years ago

Ok thank you, I'll try that.