Update docs on OnBehalfOf authentication flow
Updates guidance on on-behalf-of flow.
Previous description insinuated that an access token for Microsoft Graph could be re-used as an assertion, however this leads to an invalid_grant error when the same token is used as the assertion in the on-behalf-of flow.
On-behalf-of flow expects the initial access token issued to have an audience (aud) claim of the intermediary API then the intermediary can exchange the assertion for a Microsoft Graph access token.
This PR updates the description to reflect this better & links to various resources.
Azure Identity docs are not very clear about setting this up.
closes https://github.com/microsoftgraph/msgraph-sdk-php/issues/1472 closes https://github.com/microsoftgraph/msgraph-sdk-php/issues/1607
Microsoft Reviewers: Open in CodeFlow
@Ndiritu, thanks for the update. I have tested the solution and was able to obtain the access_token. However, this token expires after about an hour. I am developing an app that needs to call the API daily without requiring the tenant to authorize again. The solution described here works perfectly for this use case: https://github.com/microsoftgraph/msgraph-sdk-php/issues/1472#issuecomment-2151714684. I'm not sure how to achieve the same behavior using the examples provided. To be honest, with the many options Microsoft offers for authorization, it’s difficult to determine which one is the best fit for my use case. I would really appreciate any guidance :)
@daverdalas when using the assertion to initialize the GraphServiceClient, you can add the .offline_access scope. After making a successful request, the Graph access token is cached. There are samples on how you can initialize the cache so that you can retrieve the tokens from the in-memory cache & store them for future - sample. Ensure you store the refresh_token as well.
For future requests, you can re-use the cached token by passing them to the GraphServiceClient again like this.
If the token has already expired at this point, the refresh token will be used by the SDK to renew the access token without requiring the user to log in.
@Ndiritu Thanks for the quick reply :) I was able to get the api up and running with the help of more or less code like this:
$authorizationCodeContext = new AuthorizationCodeContext(
tenantId: $tenantId,
clientId: $clientId,
clientSecret: $clientSecret,
authCode: $authCode,
redirectUri: $redirectUri
);
$tokenProvider = new GraphPhpLeagueAccessTokenProvider(
tokenRequestContext: $authorizationCodeContext,
scopes: $scopes,
);
$tokenProvider->getAuthorizationTokenAsync(NationalCloud::GLOBAL)
->wait();
$accessToken = $tokenProvider->getAccessTokenCache()
->getAccessToken($authorizationCodeContext->getCacheKey());
$tokenRequestContext = new OnBehalfOfContext(
tenantId: $tenantId,
clientId: $clientId,
clientSecret: $clientSecret,
assertion: $accessToken,
);
$cache = new InMemoryAccessTokenCache(
tokenRequestContext: $tokenRequestContext,
accessToken: new AccessToken(
[
'access_token' => $accessToken,
'refresh_token' => $refreshToken,
// 'expires' => 1 -> Removed, no matter what value I put here the token is not refreshed and I get a message that it has expired.
]
)
);
$graphServiceClient = GraphServiceClient::createWithAuthenticationProvider(
GraphPhpLeagueAuthenticationProvider::createWithAccessTokenProvider(
GraphPhpLeagueAccessTokenProvider::createWithCache(
accessTokenCache: $cache,
tokenRequestContext: $tokenRequestContext,
scopes: $scopes,
)
)
);
Too bad there is no easy way to initialize GraphServiceClient using only refresh token value :( This way we need to store both refresh token and access token in the database.