How does one monitor the access token for expiry and refresh it in an Office-Addin Single Sign on Solution?
MSAL Single Sign On in an Office-Add in is a complicated beast. The access token is obtained by calling:
OfficeRuntime.auth.getAccessToken()
The access token is then swapped for an access token for my API using the Microsoft Identity On-Behalf-Of-Flow. All good.
But access tokens expire. The OfficeRunetime.auth does not appear to have anything that manages the expiry and refresh of access tokens. Unlike its counterpart MsalService (which runs in a normal Javascript/Angular web app - not an Office-Addin) and manages the tokens - and can not be used in an Office-Addin.
Do I simply continue to call OfficeRuntime.auth.getAccessToken() in an HTTP Interceptor every time prior to an API call? I understand that getAccessToken() does first consult the cache for an AccessToken, do I infer then that it is managing the expiry of that token and going back to identity server to get a new one?
Or am I completely on the wrong track?
After a lot of reading and prototyping, I'll answer my own question.
Firstly, I am a beginner when it comes to MSAL, tokens, claims, scopes etc and all matters authentication, and I've painfully waddled my way through it all. I welcome all feedback to this answer, and will update the answer if necessary
Here is what I have observed: But firstly, in conclusion to my question
OfficeRuntime.auth.getAccessToken() manages and refreshes the token when it expires (for MSExcel).
Background
Firstly, I am using MSExcel. The Microsoft Documentation seems to group Excel/Word/PowerPoint together and deals with Outlook differently. I am grateful for @Eugene Astafiev answer to this question from the Outlook perspective, but I found the opposite to be true for Excel
The office Add-in OfficeRuntime.auth.getAccessToken() returns, what Microsoft docs calls, a 'bootstrap' token being an access token that also contains an identity token. The bootstrap token can be used in the On-Behalf-Of Flow (in which it is called the 'assertion' token) to swap it for other access tokens for different scopes. In my app, I swap if for an access token for my API, lets call it the APIAccessToken
Both the bootstrap token and the identity token within it expire at the same time, after about 87 minutes. The expiry time is recorded on the Identity Tokens 'exp' claim. I don't know if this is configurable somewhere, but the key is that both expire together.
The APIAccessToken (the one returned from the OBO flow) also expires at the same time as the bootstrap token (well almost, it seems to expire randomly up to 10 mins after its official expiry time). Every time I call the OBO flow, the generated APIAccesstoken returned is different- but only in the later half of it. Although they are different, and without any regard to what time they were generated, they all still expire at the same time as the bootstrap token (assertion token) that was used to be swapped for them, and they all remain valid, and useable until the expiry time (even though they are different)
It is not possible to get the APIAccessToken in any other way in the Excel Addin than using the OBO Flow. This is because of the challenges with IFrames and login redirects in Office Excel. Excel provides the getAccessToken() and a dialog box that runs in its own instance.(see MS Excel docs on Single Sign In for Office-Addin) The access token is only useful for OBO flow
The fallback authentication strategy outlined in the MS Docs https://learn.microsoft.com/en-us/office/dev/add-ins/develop/sso-in-office-add-ins is absolutely necessary, as you will always fallback when the user has not granted consent for your SPA and your API to access their profile. However, as currently documented it is not sufficient. It does not get consent for MSOffice15, and this leads to ongoing login problems. I have raised a Stack Overflow question on this here Office-Addin Single Sign In, How to manually add MSOffice15 consent and Error 13005 and currently have implemented a work around (I'll post it in response at that link). The fallback must manage three consents. MSOffice15, Your SPA and Your API.
Refreshing The Token 1. Every call to OfficeRuntime.auth.getAccessToken() returns the same bootstrap token with the same expiry time until it expires. When it expires I confirm that this function then calls the Identity Server Endpoint and obtains a new bootstrap token, with a new Identity token, whose expiry time is again ~ 87mins. This makes sense, since the Microsoft Documentation states that it is stored in Cache, and is retrieved by OfficeRuntime.auth.getAccessToken(). This makes this function call light-weight and can be invoked often.
MySolution I derived a new class (inherited) from the official MSALInterceptor class. On every call to a protected resource I call the OfficeRuntime.auth.getAccessToken() If the token's expiry date has changed then I know that a new bootstrap token has been issued. I then call some middleware code to get a new APIAccessToken
I hope this saves someone some time! And leaving on a great note, I have finally got the entire Single Sign On login solution working for an Office-Addin SPA (angular) that manages all consents (office, SPA and my API). Took over 8 weeks work. Now its Friday 3pm - and I'm sneaking past the boss and going home :-)