Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update docs on OnBehalfOf authentication flow #1617

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

Ndiritu
Copy link
Contributor

@Ndiritu Ndiritu commented Nov 15, 2024

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 #1472
closes #1607

Microsoft Reviewers: Open in CodeFlow

@Ndiritu Ndiritu requested a review from a team as a code owner November 15, 2024 10:25
@Ndiritu Ndiritu marked this pull request as draft November 15, 2024 10:41
@daverdalas
Copy link

@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: #1472 (comment). 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 :)

@Ndiritu
Copy link
Contributor Author

Ndiritu commented Jan 20, 2025

@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.

@daverdalas
Copy link

daverdalas commented Jan 21, 2025

@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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
2 participants