How To Control App Token Lifetimes in SharePoint 2013
Today's post is the first selection from the little twitter contest I announced on the Share-n-Dipity blog a few days ago: https://blogs.technet.com/b/speschka/archive/2013/09/04/use-social-tools-to-tell-me-what-you-want-to-see-here-next.aspx. Shariq wanted to know more about the lifetime for high trust app tokens, as he tweeted here:
@speschka - I would be keen to understand more around caching tokens in High Trust Apps. Not much details available. Thanks!
— Shariq Siddiqui (@shsiddiq) September 4, 2013
So we'll look at where you can control that, but we'll also see that at the end of the day it really doesn't matter. The keys to controlling the token lifetime for apps in a high trust app is in TokenHelper. If you look at the IssueToken method you'll see that there are one or two JWT tokens created - one if you are using an app only token, and a second if you are using tokens for the app and the current user. In both cases it creates a JsonWebSecurityToken, and the fourth parameter used to create that token defines how long the token is valid for. For the app token, TokenHelper uses a constant called TokenLifetimeMinutes that is set to a value of 1,000,000 by default. Definitely not a value you should need to worry about in any scenario, unless of course you wanted to limit it (which is really the more interesting scenario). For the user token, it is hard-coded by default to use a lifetime of 10 minutes. So if you drill into the IssueToken method you can find those values and change them to whatever you want. So why doesn't it really matter?
Well, if you dig further into the code, you see that TokenHelper includes a delegate that's invoked in the GetClientContextWithAccessToken method. It's whole role in life is to add a bearer token to any request that comes from your app into SharePoint. You can see it here:
clientContext.ExecutingWebRequest +=
delegate(object oSender, WebRequestEventArgs webRequestEventArgs)
{
webRequestEventArgs.WebRequestExecutor.RequestHeaders["Authorization"] =
"Bearer " + accessToken;
};
If you set a breakpoint on that line of code and then step through a request from your app, you'll see that none of the token "setup" functions - i.e. those that create the ClientContext - actually result in an HTTP call. It's only when you call the ExecuteQuery method on the ClientContext object that a request goes over the wire, and then the delegate fires and the token is added to the request. So getting back to really the intent of Shariq's original question, while you CAN cache the access token you get in a high trust app, there's really not a good reason to do so (at least that I can think of so far), because it doesn't generate a call to SharePoint anyways. Just for proving this out I wrote a little sample app where I played with setting the token lifetime for the user to 5 minutes, but I cached the access token for 1 year. Sure enough, after 5 minutes had passed all of my app calls started failing with 401 - unauthorized errors, which is exactly what you would expect to see. I've attached a zip file to this posting with that sample application.
Now...where caching app tokens DOES help is when you are using low trust apps. In that case you absolutely do save one or more roundtrips to ACS to get an access token if you are caching them. If you have lots of users and/or lots of requests this can really add up. I won't bother going into the details of how to cache tokens for low trust apps here, because I've already posted about that in the series on app security. For more details, see part 4 of that series at https://blogs.technet.com/b/speschka/archive/2013/07/30/security-in-sharepoint-apps-part-4.aspx.
Hopefully that explains the concepts in a little more detail and will be useful to you in your application security designs. That was a great question Shariq, and thanks for responding to the twitter 'test. Feel free to continue to pile your requests in there.
Comments
Anonymous
January 01, 2003
Perfect !! Thanks for the great explanation.Anonymous
January 01, 2003
thanksAnonymous
January 01, 2003
The comment has been removedAnonymous
January 01, 2003
thanks for sharing.Anonymous
September 18, 2014
The comment has been removed