Is it possible to use an KeyCloak AccessToken to get access to the Microsoft Graph?

Scenario:

I’ve configured an oidc identity provider to use Azure AD (TokenUrl = https://login.microsoftonline.com/{GUID}/oauth2/v2.0/token)

I can login correctly via KeyCloak to Azure AD, however when calling the Graph API (from C#) using the KeyCloak RawData Token : JwtSecurityToken.RawData an Exception is thrown:

{
  "error": {
    "code": "InvalidAuthenticationToken",
    "message": "Access token validation failure.",
    "innerError": {
      "date": "2021-01-16T08:26:44",
      "request-id": "00545057-cf1c-44cf-9c61-e5cde8a2a63b",
      "client-request-id": "00545057-cf1c-44cf-9c61-e5cde8a2a63b"
    }
  }
}

How to use the KeyCloak AccessToken? Or is there a way to get a valid Azure AD token via KeyCloak?

1 Like

Hi

I don’t know if you ve managed to do it yet.
There is a way using stored tokens.

I’ve not dine more than manual testing but it seems to be a stable feature.
I let you do the research because I cannot document just now (will try later tonight)

  • configure your identity provider to enable stored token
  • configure your keycloak users to receive the read-token rights in the broker client
  • once a user has received a keycloak access token, call the /auth/realm/yourrealm/identityproviser/alias/token (typing the endpoint from memory) to retrieve the Microsoft tokens

Hi Stef,

So here is a longer explanation and some reference articles:

When keycloak rely on an identity provider to authenticate the user, it can keep the user’s token for end user to retrieve at a later call: This is called stored token.

Some reference here Retrieving External IDP Tokens | keycloak-documentation

Practically, in the identity provider (ie Microsoft config in your case) you have a config screen and you must activate the two options in the red box:


Together these options will allow end user to request the tokens

Then you need to setup your users to be able to make that request in the context of the client app.
The user’s access token should match to a user that has the role read-token of the client broker (that is a pre-configured keycloak app). To do this you can either

Once the user is authenticated via keycloak to your external identity provider (Microsoft) you can use its access token to request the external idp tokens using the dedicated endpoint:

curl -H "Authorization: Bearer <user's access-token from keycloak> https://your-keycloak-server/auth/realms/your-realm-name/broker/provider_alias/token

You will have a json structure with the result that keycloak got when it called the token_endpoint of the external idp:

{
"access_token": "ey.....",
"id_token": "ey........",
"refresh_token": "optional refresh token ...",
etc
}

I hope that helps. Let us know the progress you’ve made so other can benefit :wink:

I’d like to share also, for those interested that I am working at a commercial solution (https://trusstee.io) that intends to facilitate the application integration with multiple social logins and in particular resolve that specific case of getting the remote access token of an identity provider that implement a particular set of APIs. Indeed, Microsoft has a tenant specific IDP that deliver access tokens giving access to Microsoft Graph API (for instance). I am looking for developers tester if interested.

3 Likes

Dear guenoledc, thanks for the detailed research and explanation.

However, I found out that even with this fix, my scenario will not work because I want to access the Graph Api not only for reading/writing the the current user, but also to other users.
So for that to work, I need an system-like App Registration which has all these permissions:

Indeed, the token you will receive is user specific. You let the user connect via keycloak to microsoft, the token you receive are theirs.

In your scenario, do you have the possibility to execute the action from the front end, I.e. you make the page script call the microsoft api with the specific user access token.

If you expect to be able to update user B details while being connected as user A on microsoft, surely you must have the right at microsoft api (some sort of user admin). In that case, your user A must pass the “User.ReadWrite.All” scope when connecting to receive that scope in the access token. This can be achieved in the keycloak configuration default scopes below. This should pass that scope list to microsoft when authenticating together with openid.

I quickly tested that and it seems to work (sends a valid oidc auth request).

But that means that all users using that identity provider must be allowed to be granted this scope. Not sure it is what you want.

Hello @guenoledc,

I was able to use your information to configure KeyCloak correctly in such a way that the Azure Token can be fetched and used for a connection to Microsoft Graph.

However not all functionality (e.g. Read All Users) is possible using the “User” token, so I’ve created/defined an additional client in my C# code which accesses the Microsoft Graph using a Application Registration which has permissions for “Read All Users”. So that works fine.

However, when I leave my Blazor apllication for some time running, and then try to access the Microsoft Graph using a user, I get an exception stating that the Access Token is expired.

Do you know how to solve this? For example is there a way to refresh that Azure token via KeyCloak ?

Hi Stef

If you are saying that your C# back end gets an exception of an expired token it is most probably because the access_token has a short life (that is normal) and that you should use the refresh_token provided at the first connection to get a new access_token (and probably a new refresh_token too) via the /token endpoint

When to do this in your back end can have 2 approaches
1- in the connection response you will get a expire_at or expire_in telling you when the access_token expires (in sec) and you can therefore trigger a timeout to refresh the token
2- you try to use the current access_token and in case of expired exception you perform the refresh

If you are unfamiliar with refreshing tokens look at the following doc Refreshing Access Tokens - OAuth 2.0 Simplified

I hope that helps. Please share your results

1.
The Azure Token which I can get using the KeyCloak Access Token looks like this:

{
  "access_token": "eyJ...",
  "expires_in": 3599,
  "refresh_expires_in": 0,
  "token_type": "Bearer",
  "id_token": "eyJ...",
  "not-before-policy": 0,
  "scope": "email openid profile User.Read",
  "accessTokenExpiration": 1614725332,
  "ext_expires_in": 3599
}

Note that there is no RefreshToken available.

2.
I also noticed that in my application, the KeyCloak Access Token does not seem to change after x-time (I still need to double check that tomorrow…)

So getting a (new) Azure Token based on that KeyCloak Access Token, will also be the same (old) value.

3.
I could try to catch the exception from Microsoft Graph:

Status Code: Unauthorized
Microsoft.Graph.ServiceException: Code: InvalidAuthenticationToken
Message: Access token has expired or is not yet valid.
Inner error:
	AdditionalData:
	date: 2021-03-02T21:43:04
	request-id: c8d09675-31e4-4502-bec7-a561c74be310
	client-request-id: c8d09675-31e4-4502-bec7-a561c74be310
ClientRequestId: c8d09675-31e4-4502-bec7-a561c74be310
   at Microsoft.Graph.HttpProvider.SendAsync(HttpRequestMessage request, HttpCompletionOption completionOption, CancellationToken cancellationToken)

And rethrow this an AuthenticationException, which will be perhaps be handled by the frontend, in such a way that a login is required from the user. But I still need to think on that if that’s possible / desired.

Hi @Stef ,

I don’t fully get the setup that you are describing.

You wrote:

Is this a client directly in microsoft config or in keycloak? Is it an end-user’s authentication or a service account authentication ? Could you clarify?

I understand that the stored tokens do not keep the refresh token for security reason. But you should be able to use the keycloak refres htoken to refresh the microsoft session (possibly, I have not tried this)

This a just App Registration in Azure, nothing to do with KeyCloak.

Hi @guenoledc I’m also trying to get an MS Graph API token. I did everything you mentioned above and I was able to get the microsoft user token.
The first thing I noticed is that the returned fields were different from yours:

{
“token_type”: “Bearer”,
“scope”: “Calendars.Read.Shared Calendars.ReadWrite.Shared email openid profile User.Read”,
“expires_in”: 3599,
“ext_expires_in”: 3599,
“access_token”: “eyJ0eXAiOiJKV1QiL…”
}

Secondly when I tried to exchange that token for a Graph API token using the OBO flow, I got the following error:

AADSTS50013: Assertion failed signature validation. [Reason - The provided signature value did not match the expected signature value

Looking further I noticed that the returned microsoft access token is regarded as v1 and is totally different from what I’d expect. Most notably the aud claim was not my ms user uid, but the (azure) application resource uid.

Any idea why this happens? I have the “accessTokenAcceptedVersion”: 2 property set in my Azure app’s manifest.

Thanks

Hi Stef,

Am also facing the same issue. Leaving our app for sometime and when we re-trigger, we were able to get the AD token using Keycloak Token, but it fails with the Graph API stating InvalidAuthenticationToken. Please see error message below

Error code: InvalidAuthenticationToken
Error message: Access token has expired or is not yet valid.

Have you found any solution to it as its.