I implemented this tutorial in our azure environment and everything worked as expected, no problems there. However, after adding MS Graph permissions to the app permissions of the API app, i ran into some token expiration issues that i failed to solve.
The additional steps i took are:
User.Read scope (Delegated Permission)Calling that Azure Function with the access token obtained by login into the web app works, but only if the access token in the X-MS-TOKEN-AAD-ACCESS-TOKEN header set by the app service authentication of the API app is not yet expired (which can easily be checked by the X-MS-TOKEN-AAD-EXPIRES-ON header). With the expired access token in the API app, the graph token binding fails with AADSTS70002: Error validating credentials. AADSTS50013: Assertion is not within its valid time range.
I tried to refresh the token by calling /.auth/refresh with bearer authentication but that gives me 401 Unauthorized response. I stumbled upon this StackOverflow issue which states that refreshing with bearer authentication is not supported. I tried refreshing the token via browser which worked out fine and for the next function call, I got a valid graph access token back (until the new token expired again after an hour).
So the question is, how do i refresh the access token from a service-side call?
Looking forward to a response on this.
âš Do not edit this section. It is required for docs.microsoft.com âžź GitHub issue linking.
@kmees
Thanks for the feedback! We are currently investigating and will update you shortly.
@kmees you need to call /.auth/refresh _before_ the token expires. Just call it inside an authenticated session and the access token will be refreshed.
@cephalin thanks for the response, but this isn't possible as i described earlier. I am authenticated against my WEB app, and i need to refresh the tokens in my API app, but calling /.auth/refresh (on the API app) with bearer authentication is apparently not supported (see StackOverflow thread above).
Also, to prevent further misunderstanding, I'm not talking about the access token for my API app, but the the access token "inside" the API app that is required to fetch a graph token. This is the code inside my azure function secured by API app, called by Web app with bearer authentication:
var tokenAttribute = new TokenAttribute()
{
Identity = TokenIdentityMode.UserFromRequest,
IdentityProvider = identityProvider,
Resource = "https://graph.microsoft.com"
};
// this call fails with
// AADSTS70002: Error validating credentials. AADSTS50013: Assertion is not within its valid time range.
var token = await binder.BindAsync<string>(tokenAttribute);
You shouldn't be calling /.auth/refresh on the API app. The auth token is generated on the _WEB_ app so you need to call /.auth/refresh on the WEB app.
As the thread pointed out, if you need an alternative to the bearer token you can just use the AppServiceAuthSession cookie from your client request to /.auth/refresh.
Sorry, but you're still not getting the issue here. Refreshing the auth token from the web app doesn't help. Let me try to explain it one more time.

[FunctionName("ImperativeGraphTokenBinding")]
public static async Task<IActionResult> Run([HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = null)] HttpRequest req, IBinder binder, TraceWriter log)
{
var tokenAttribute = new TokenAttribute()
{
Identity = TokenIdentityMode.UserFromRequest,
IdentityProvider = "AAD",
Resource = "https://graph.microsoft.com"
};
try
{
var token = await binder.BindAsync<string>(tokenAttribute);
return new OkObjectResult(new
{
token = token,
headers = req.Headers.Where(header => header.Key.StartsWith("X-MS"))
});
}
catch (Exception ex)
{
log.Error(ex.Message, ex, "ImperativeGraphTokenBinding");
return new OkObjectResult(new
{
error = ex.Message,
headers = req.Headers
});
}
}
access_token obtained from the https://<web-app>/.auth/me endpointGET /api/ImperativeGraphTokenBinding HTTP/1.1
Host: meetsweet-dnext-functions.azurewebsites.net
Content-Type: application/json
Authorization: Bearer eyJ0eXAiOi....
{
"token": "eyJ0eXAiOiJKV1QiLC...",
"headers": [
{
"key": "X-MS-TOKEN-AAD-ACCESS-TOKEN",
"value": [ "eyJ0eXAiOi..."]
},
{
"key": "X-MS-TOKEN-AAD-EXPIRES-ON",
"value": [
"2018-07-09T14:16:33.0000000Z"
]
},
// ... other headers
}
This works, as long as the access token "inside" the API app (from the X-MS-TOKEN-AAD-ACCESS-TOKEN header) is not expired (see X-MS-TOKEN-AAD-EXPIRES-ON header). After the expiration, this call fails with the AADSTS70002: Error validating credentials. AADSTS50013: Assertion is not within its valid time range. error mentioned earlier.
Now, calling https://<web-app>/.auth/refresh and using the refreshed access token does not change anything about the X-MS-TOKEN-AAD-ACCESS-TOKEN inside the API app. I have to call https://<api-app>/.auth/refresh but this is not possible with bearer authentication. But since I'm also not directly authenticated I don't have a valid AppServiceAuthSession cookie for the API app to use (only for the WEB app, but they are for difference app service authentications).
Is this scenario not possible with App Service Authentication (EasyAuth)?
Or is my architecture wrong and i need to add the MS Graph Permissions to my Web App instead and send and extra graph access token to my API? That feels wrong somehow...
I could also manually refresh the token inside my function by calling
POST /1da0e38d-c9e1-4df7-9751-9b16a6e46de5/oauth2/token HTTP/1.1
Host: login.microsoftonline.com
Content-Type: application/x-www-form-urlencoded
Cache-Control: no-cache
Postman-Token: ad6b59b1-a2b6-fd24-9be2-713141ba01b0
grant_type=refresh_token&resource=https%3A%2F%2Fgraph.microsoft.com&...
but that doesn't store the refreshed token in the EasyAuth token cache and I'd have to do that on each request, so that can't be the solution either.
@kmees I apologize I didn't read your original question very carefully.
@mattchenderson Can you assist?
@mattchenderson @BryanTrach-MSFT any updates on this?
Hi folks,
What happens if you have multiple APIs with different AD Applications? I mean, I have two questions here:
Thanks!
Having same problem, any updates?
Any update on this?
I have the same error. Any updates?
Hi all - sincere apologies about not seeing this sooner. @ConnorMcMahon is going to spend some time investigating.
Just to make sure we have the context captured properly, we have a user-facing web app, which in turn calls an API backend, which in turn calls the MSFT graph. We expect a different AAD registration at each phase:
Because this seemed to be related to adding the graph permission, our first guess is that the user hasn't authored consent to the Microsoft Graph. If you're just refreshing the existing token, there isn't an opportunity for that consent to be captured, as the user isn't involved. You would have to have the user re-login with an explicit consent prompt, which can be achieved through a /.auth/login/aad?prompt=consent call. However, we wouldn't expect that to result in the expiration error. It is possible that a cached, expired value is being used as a fallback from that permission error, but I'm guessing. Connor would have to weigh in there.
One other piece of information that would be good to have is the logs from the actual Authentication / Authorization layer itself, particularly if there's an error message. To get these logs:
Please keep in mind that the verbose log could include a token, etc., so please sanitize before sharing (as you've done with earlier examples).
Any updates?
Seeing the same issue here, my SPFX app that's connecting with Aadhttpclient works fine for the first 60 minutes, then I get the Assertion is not within its valid time range error.
Calling a different function in the same app that doesn't extract the auth token doesn't have the error, and I get X-MS-TOKEN-AAD-ACCESS-TOKEN headers through.
If I GET the function in the browser, that seems to cause a token refresh, and my calls then work again.
I am having a similar problem with this. I have an azure function app with AAD auth (anonymous) and an Auth Token input binding from the graph and use it to server content to an SPA. I've used ADAL.js and MSAL.js to authenticate against our AAD and get accessTokens, but after the hour period the function app fails with AADSTS500133 assertion is not within its valid time range. If I navigate to any of the function app URLS, or any .auth/ endpoint, the access token is renewed (silently) the function app will work again.
I am desperately looking for a solution to this, I don't care if it outright prompts the user for their password every time, I just want a way to refresh this token without having to navigate to the function app directly from a browser. Refresh token calls come back 401, xhrs return null json responses, I can sign out and back in to my session and get new tokens with ADAL or MSAL but I can't even manage to get a prompt to authenticate to the function app once the assertion expires.
I would be more concerned about my code, but I can start a new browser session or open an incognito window and everything hits exactly as expected...for one hour. I am completely open to any suggestions or ideas because I've tried just about everything else I can think to try.
We stopped using the token binding and handle it ourselves. Our functions are run infrequently enough that it's not an issue for us to do it as needed. The basic code we use is this:
```string tenantId = ClaimsPrincipal.Current.FindFirst("http://schemas.microsoft.com/identity/claims/tenantid")?.Value;
string authority = $"https://login.microsoftonline.com/{tenantId}";
// convert the ID token to a graph token
ClientCredential clientCred = new ClientCredential(clientId, clientSecret);
UserAssertion userAssertion = new UserAssertion(token);
var authContext = new AuthenticationContext(authority);
Uri hubUri = new Uri(url);
AuthenticationResult authResult = await authContext.AcquireTokenAsync("https://" + hubUri.Host, clientCred, userAssertion);
var spAccessToken = authResult.AccessToken;```
@ng-marcus Is it possible for you to post some more code or link to a blog post?
@ng-marcus Is it possible for you to post some more code or link to a blog post?
Anders, yes I'll write up how we have this working for us to see if that helps. Give me a day and I should have it written.
I'll post the code we use.
First of all, we use the following:
```
using Microsoft.IdentityModel.Clients.ActiveDirectory;
using System.Security.Claims;
Then we get the token, it's either an ID token if testing direct from the browser, or an access token if called from our Spfx app:
```
string token = null;
if (null != req?.Headers?.Authorization?.Parameter)
{
log.Info("Using Authorization token");
token = req?.Headers?.Authorization.Parameter;
}
else
{
log.Info("using X-MS-TOKEN-AAD-ID-TOKEN");
IEnumerable
var tokenId = string.Empty;
if (req.Headers.TryGetValues("X-MS-TOKEN-AAD-ID-TOKEN", out headerValues))
{
token = headerValues.FirstOrDefault();
}
}
log.Info("Token: " + token);
And finally we exchange the token for the API we need:
```
string ClientId = Environment.GetEnvironmentVariable("CLIENT_ID");
string ClientSecret = Environment.GetEnvironmentVariable("CLIENT_SECRET");
string msGraphResourceUrl = "https://graph.microsoft.com";
//Get the tenant id from the current claims
string tenantId = ClaimsPrincipal.Current.FindFirst("http://schemas.microsoft.com/identity/claims/tenantid")?.Value;
string authority = $"https://login.microsoftonline.com/{tenantId}";
//Exchange the SPFx access token with another Access token containing the delegated scope for Microsoft Graph
ClientCredential clientCred = new ClientCredential(ClientId, ClientSecret);
UserAssertion userAssertion = new UserAssertion(token);
//For production, use a Token Cache like Redis https://blogs.msdn.microsoft.com/mrochon/2016/09/19/using-redis-as-adal-token-cache/
var authContext = new AuthenticationContext(authority);
AuthenticationResult authResult = await authContext.AcquireTokenAsync(msGraphResourceUrl, clientCred, userAssertion);
var graphAccessToken = authResult.AccessToken;
//log.Info(graphAccessToken);
HttpClient client = new HttpClient();
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", graphAccessToken);
I hope that helps, let me know if you have any questions. This is running on v1 function apps.
Hey @ng-marcus Thank you. We finish our POC yesterday and end up with pattern.
I'm getting the same problem in a Function App v2 with JavaScript and a very simple O365 on-behalf-of scenario (functions are secured with AAD). The same symptoms: everything works until, after some time, the token endpoint returns: AADSTS500133: Assertion is not within its valid time range. The "/.auth/refresh" endpoint does nothing to update tokens that are in the app headers.
This is the example code that I'm using:
const superagent = require('superagent');
module.exports = async function (context, req) {
var result = {};
var graphTokenParameters = {
grant_type: 'urn:ietf:params:oauth:grant-type:jwt-bearer',
client_id: "<clientid>",
client_secret: "<clientsecret>",
assertion: req.headers['x-ms-token-aad-id-token'],
requested_token_use: 'on_behalf_of',
scope: "https://graph.microsoft.com/user.read"
};
try {
var tokenResponse = await superagent
.post('https://login.microsoftonline.com/common/oauth2/v2.0/token')
.type('form')
.send(graphTokenParameters);
var meResponse = await superagent
.get('https://graph.microsoft.com/v1.0/me/')
.set({ "Authorization": 'Bearer ' + tokenResponse.body.access_token });
result.status = "ok";
result.result = meResponse.body;
} catch (e) {
result.status = "nok";
result.result = JSON.stringify(e);
}
context.res = {
body: result
};
context.done();
};
Running into the same assertion issue. Anyone figure this out, aside from the workaround posted by marcus (above)? @ConnorMcMahon
Noting I am receiving the same issue. Azure Function App v2, Token resource as parameter on function, and function configured for Azure AD auth. Access the endpoint, works for 1 hour, then fails with AADSTS500133.
@ng-marcus I have tried what you have recommended to call Graph API and Get the tokens. But it did not work.
Any other way?
doesn't this has to do with easyauth itself? I see that the X-MS-TOKEN-AAD-ID-TOKEN itself is never refreshed. This id_token is needed by the token binding to get a valid graph token
https://github.com/Azure/azure-functions-microsoftgraph-extension/blob/master/src/TokenBinding/TokenBaseAttribute.cs#L55
and
https://github.com/Azure/azure-functions-microsoftgraph-extension/blob/master/src/TokenBinding/TokenConverter.cs#L56
I think I am running into the same problem.
I am running an AAD protected Azure Function. I serve static content using proxies and use the same Function App as my API backend.
So, from the static code (front-end) I am able to issue a get request to .auth/refresh; however, since users never leave the function app domain, they can be logged in for some time. At some point, a fresh is necessary.
What I would like to do is detect this issue in my backend C# code to refresh before the access_token expires. Unfortunately, I am unable to call .auth/refresh from C# code. I can call .auth/me using the id_token, but not .auth/refresh.
Can you tell me specifically what the .auth/refresh requires for headers and body. For the headers and body, if a token is required, state exactly what token from either the .auth/me endpoint or the X-MS-TOKEN-AAD[...] header is required.
Edit:
So I figured this one out. A get request with the Session Cookie will work. I am using an azure function so I had to pull the HttpRequestWrapper property from the request.
A little documentation would be helpful here.
Functions related documentation on this subject is potentially required. @ggailey777 @craigshoemaker FYI
Hi @jchilde, can you share some code example ? Thanks
I think I am running into the same problem.
I am running an AAD protected Azure Function. I serve static content using proxies and use the same Function App as my API backend.
So, from the static code (front-end) I am able to issue a get request to .auth/refresh; however, since users never leave the function app domain, they can be logged in for some time. At some point, a fresh is necessary.
What I would like to do is detect this issue in my backend C# code to refresh before the access_token expires. Unfortunately, I am unable to call .auth/refresh from C# code. I can call .auth/me using the id_token, but not .auth/refresh.
Can you tell me specifically what the .auth/refresh requires for headers and body. For the headers and body, if a token is required, state exactly what token from either the .auth/me endpoint or the X-MS-TOKEN-AAD[...] header is required.
Edit:
So I figured this one out. A get request with the Session Cookie will work. I am using an azure function so I had to pull the HttpRequestWrapper property from the request.
A little documentation would be helpful here.
Hi jchilde
How exactly did you solve this?
I too am getting this issue.