Inside SharePoint 2013 OAuth Context Tokens

This post will show you how to inspect the SharePoint 2013 context token to better understand how OAuth is used in SharePoint 2013 apps.

First, Some Context

In order to use a context token with SharePoint 2013 apps, you will need to create a provider-hosted app that uses a client ID and a client secret.  This requires that the target SharePoint farm has a trust configured to Azure Access Control Services, or ACS.  Office365 automatically configures this trust for you, so if you create your app using Office365 then this works just fine.  If you create your app using your own SharePoint farm, then you will need to configure low-trust apps for on-premises deployments in order to establish a trust with Azure ACS for your SharePoint farm.

When you use a client ID and a client secret to build your app, you are using Azure ACS as the authentication server.  The OAuth flow looks like this:

image

This is what I jokingly refer to as “the scary slide”.  It’s not really that scary once you understand it.

  1. User makes a request to SharePoint
  2. SharePoint requests the context token from Azure ACS
  3. The signed context token is returned
  4. The HTML markup for the SharePoint page is sent to the client
  5. If the SharePoint page contents contain a client app part, then a request is sent to the remote web server for the IFrame’s contents.  If the user simply clicked on the app to launch it, then a page in SharePoint called appredirect.aspx is used to redirect to the app’s start URL.  In either case, a context token is sent along with that page request in a form parameter called SPAppToken. 
  6. The context token is a JWT token.  The JWT token is a set of base64 encoded claims.  One claim is named “refreshtoken” that has a base64 encoded value.  One of the claims is “appctx” which contains a child object, one of the properties is SecurityTokenServiceUri with a value https://accounts.accesscontrol.windows.net/tokens/OAuth/2.  Your app uses the TokenHelper.cs class to extract the refreshtoken and then send it to the SecurityTokenServiceUri to request an access token.
  7. The access token is retrieved from ACS.
  8. The access token is passed to the SharePoint site in the HTTP header Authorization that has a value beginning with “Bearer” and has your access token.  This token is passed in the HTTP header to the CSOM endpoint _vti_bin/client.svc/ProcessQuery when you use the CSOM.
  9. If the app is authorized, SharePoint returns the data requested.
  10. Finally, the app server returns the requested HTML markup as a response. 

 

The really important thing to understand is what’s passed to your app, the context token.  The context token contains a few key pieces of information necessary for the rest of the plumbing to function.

 

What’s In Your Wallet?

Now that we’ve walked through the steps of the OAuth dance, let’s take a look at what’s in that context token that is critical to the whole process.  When you use Visual Studio to create a provider-hosted app, the boiler plate code looks like this:

 protected void Page_Load(object sender, EventArgs e)
{
    // The following code gets the client context and Title property by using TokenHelper.
    // To access other properties, you may need to request permissions on the host web.

    var contextToken = TokenHelper.GetContextTokenFromRequest(Page.Request);
    Response.Write(contextToken + "<br/>");
    var hostWeb = Page.Request["SPHostUrl"];

    using (var clientContext = TokenHelper.GetClientContextWithContextToken(hostWeb, contextToken, Request.Url.Authority))
    {
        clientContext.Load(clientContext.Web, web => web.Title);
        clientContext.ExecuteQuery();
        Response.Write(clientContext.Web.Title);
    }
}

Reading that code, you can see that the context token is passed to your app, but it is a base64 encoded string that is unintelligible.  I added a Response.Write statement to write the context token to the page, but you can also see it in Fiddler.  In the following screen shot, I highlighted the SPAppToken HTTP form variable where the context token is passed.

image

Once you have that context token, you have the base64 encoded value of the context token, which is a JWT token.  The JWT token format is pretty straightforward, and the TokenHelper.cs class in Visual Studio simply parses that JWT token for you.  However, for the curious who want to see what it looks like, you can use a tool like https://openidtest.uninett.no/jwt to inspect the JWT token.  Copy the context token string into the “Encoded JWT” text box and the client secret into the “Secret” text box, and then press decode. 

image

What is returned is the following JSON object, which is a JWT token that contains a set of claims.

 

 {
    "aud": "4c2df2aa-3d14-4d84-8a79-5a75135e98d0/localhost:44346@d341a536-1d82-4267-87e6-e2dfff4fa325",
    "iss": "00000001-0000-0000-c000-000000000000@d341a536-1d82-4267-87e6-e2dfff4fa325",
    "nbf": 1365177964,
    "exp": 1365221164,
    "appctxsender": "00000003-0000-0ff1-ce00-000000000000@d341a536-1d82-4267-87e6-e2dfff4fa325",
    "appctx": "{\"CacheKey\":\"em1/saZohTOS4nOUZHXMb8QJgyNbkEO86TSe5j9WYmo=\",
\"SecurityTokenServiceUri\":\"https://accounts.accesscontrol.windows.net/tokens/OAuth/2\"}",
    "refreshtoken": "IAAAANc8bAVMWZceOsdfgsdfggbfm7oU_aM7D2qofUpQstMsdfgsdfgfYS0OtbZ-
eY9UQGvlYSl5kpPi913G1AwIVBMxoCux8-bhcCCiaGVo-vuFzrXetdhRGPftQdHh-
1rS5cvDuuQ_bw_mjySIyuHNGSavEs8HUgHY9BOVc3pTGZtZ_nS-
1NbDLYObjnznasdfasdfasdfQreLAeeOpVRY1PGsdfgsdfgOITA3BKhjJFz_40YJMubdHmY2OTS
nqwNnUe-rBBCtfvKt4xFWvdRzTzwfW",

    "isbrowserhostedapp": "true"
}

You can now see that the context token contains the refresh token as a base64 encoded value.

What are the claims in the context token?

The following shows the properties for the context token.

aud Short for “audience”, means the principal the token is intended for. The format is <client ID>/<target URL authority>@<target realm>. Based on this information, you can determine the client ID for your app and the realm.  In an on-premise environment, there is typically just one realm, and its identifier matches your farm ID.  For Office 365, this is your tenant ID.
iss Short for “issuer”, this is the principal that issued the token, in the form of <principal ID>@<realm> .  The principal ID value 00000001-0000-0000-c000-000000000000 is ACS. 
nbf Short for “not before”, this is the number of seconds after January 1, 1970 (part of the JWT specification) that the token starts being valid. 
exp Short for “expires”, represents the number of seconds after January 1, 1970 that the token stops being valid.
appctxsender The sender of the token in the form <sender ID>@<realm>.  The value 00000003-0000-0ff1-ce00-000000000000 is the identifier for SharePoint.  For trivia:
ACS 00000001-0000-0000-c000-000000000000
Exchange 00000002-0000-0ff1-ce00-000000000000
SharePoint 00000003-0000-0ff1-ce00-000000000000
Lync 00000004-0000-0ff1-ce00-000000000000
Workflow 00000005-0000-0000-c000-000000000000
The realm will be the tenant ID for Office 365, or the farm ID for your on-premise deployment.
appctx Contains two properties, CacheKey and SecurityTokenServiceUri. CacheKey: UserNameId + "," + UserNameIdIssuer + "," + ApplicationId + "," + Realm This is provided so that you can cache the value in a cookie or in session to identify that the user has already authenticated. SecurityTokenServiceUri: The URL for Azure ACS where the token is to be validated.  The URL is https://accounts.accesscontrol.windows.net/tokens/OAuth/2
refreshtoken The contents of the refresh token that are sent to Azure ACS.
isbrowserhostedapp Indicates if the request initiated from a user interacting with the browser and not an app event receiver

 

During Ignite training, I tell the attendees that the most important thing you can understand when building apps for SharePoint is OAuth.  You need to understand the OAuth dance, the reason for the context token, and it is invaluable to understand what the TokenHelper.cs is doing for you when it does things like GetClientContextFromContextToken.  This post goes into some of the details on why this is such an important piece to understand when troubleshooting apps.  You can also see why it is mandatory for apps to SSL due to the nature of information being sent in HTTP. 

You can find more information about the context token, calculating nbf and exp claim values, and links to additional articles at https://msdn.microsoft.com/en-us/library/office/apps/fp179932.aspx

For More Information

configure low-trust apps for on-premises deployments

JWT token format

Tips and FAQs: OAuth and remote apps for SharePoint 2013

Comments

  • Anonymous
    April 05, 2013
    Good Post.

  • Anonymous
    April 11, 2013
    Nice Post. I did a blog post recently explaining how we can use just HTTP requests to get the accesstoken, which explains some of the internal flow of TokenHelper.Here is the post : jomit.blogspot.com/.../authentication-and-authorization-with.html

  • Anonymous
    October 14, 2014
    I used the VS model that mounts to interact with the app, but when the user gets some minutes without interacting with the buttons, the context is null and the error page. The user is then required to start the app again. Know what can be?

  • Anonymous
    December 07, 2014
    The comment has been removed

  • Anonymous
    January 28, 2015
    This all worked fine in development but when I published, my web site no longer receives the context token.  What setting did I miss?  

  • Anonymous
    January 29, 2015
    I had to use Fiddler to figure out what was going wrong.  I had to adjust the settings in my client ID setup in the seller dashboard to match what was expected as far as the domain and URL.

  • Anonymous
    March 07, 2015
    The question is why? Why is Microsoft making it so hard to communicate with SharePoint server. JSOM?CSOM has FormDigest. why can't you just make the same set of calls? To make it worse they never explained in their documentation or sample what is going on behind the scene like you so clearly explained. Thanks for sharing.

  • Anonymous
    March 08, 2015
    @Kenny - it's not at all harder, it's actually quite easier than it was in the SOAP web service days as the tooling hides all of this complexity while OAuth opens a ton of new scenarios that weren't possible previously.  As for the FormDigest... that assumes that the CSOM call is rendered in the same page as SharePoint.  If that is the case, then none of this applies as that is not an app, that's just JavaScript in a page, and you don't need any OAuth for that. This scenario is for when your server-side code is running on a completely different server, domain, and possibly even platform than SharePoint.  I can now easily write apps using Java, Node.js, and PHP that communicate with SharePoint... this was difficult using SOAP.

  • Anonymous
    April 24, 2015
    Hi Kirk,Nice post! BTW, I'm having the same issue as Dave.. When I'm running the webapp locally everything is right, but when I upload it to Azure, SPAppToken is null and it can't authenticate...Any thoughts on this?Many thanks.

  • Anonymous
    April 25, 2015
    @fretwio and @Dave -Assuming you are using AppRegNew.aspx, the domain and callback URL are required when registering the app.  If you use them locally, then you would have registered http://localhost.  When publishing to another site (such as Azure Web App) you have to register a new entry with the new domain and callback URL.  These are not editable when using AppRegNew.aspx.If you are using the store registration, same thing... you need to update the domain and callback URL.  

  • Anonymous
    June 16, 2015
    I am a little baffled about that. Every decent developer must be switching back and forth between testing locally and deploying remotely constantly. Is the intention that they should edit their Web.config to change the client ID each time such a switch is made, or is there a less dumb way to go about it?

  • Anonymous
    September 15, 2015
    I'm trying to get the access_token, but i got this error {"error":"invalid_request","error_description":"ACS90004: The request contains 1 tokens separated by u0027=u0027 instead of a single key value pair.rnTrace ID: 1772198e-ddb6-4dbc-82cf-d0a487b887adrnCorrelation ID: 9a2b81d4-13ce-4a9d-9 bff-8cb7ae41e447rnTimestamp: 2015-09-15 16:57:50Z","error_codes":[90004],"time stamp":"2015-09-15 16:57:50Z","trace_id":"1772198e-ddb6-4dbc-82cf-d0a487b887ad", "correlation_id":"9a2b81d4-13ce-4a9d-9bff-8cb7ae41e447"} Any guess? Thanks!

  • Anonymous
    September 15, 2015
    @Mauricio - Sorry, no clue.  I would suggest either creating a support ticket or posting your question to a community forum such as stackoverflow.com/.../sharepoint.  

  • Anonymous
    September 17, 2015
    @kirk I've already found the error, i guess it was because i was using only ' instead of ", now i got a new error "{"error":"invalid_request","error_description":"ACS90019: Unable to determine the tenant identifier from the request.\r\nTrace ID: 5c538fef- b2f4-458f-b245-d8ee008ea68c\r\nCorrelation ID: 52aa08b0-3bc6-4850-85ef-c1d7d3f3fc91\r\nTimestamp: 2015-09-17 17:25:10Z","error_codes":[90019], "timestamp":"2015-09-17 17:25:10Z","trace_id":"5c538fef-b2f4-458f-b245-d8ee008ea68c","correlation_id":"52aa08b0-3bc6-4850-85ef-c1d7d3f3fc91 "}" I'm trying to read this link, but 404 msdn.microsoft.com/.../fp179932.aspx Thanks @Kirk

  • Anonymous
    September 17, 2015
    @kirk i found a solution, here is the gist gist.github.com/.../ce4c4af9eb845735a825

  • Anonymous
    November 12, 2015
    Is there any way to get information about the user from the token? I have a provider hosted app that is not accessing any Sharepoint resources. The hope was to launch through Sharepoint and accpet authentication provided by Sharepoint but provide my own authorization

  • Anonymous
    November 12, 2015
    Sorry, I should have provided more information. The provider hosted app is written in Java. So, I do not have access to the TokenHelper or some of the other convenient .NET classes.

  • Anonymous
    November 12, 2015
    The comment has been removed