Based on recent testing SIF (sign-in-frequency) enforcement can be bypassed when refresh token is available for exchange¹ on third party² Web API clients.
I believe this to be undocumented by-design limitation of Azure AD Conditional Access and should be evaluated critically when using LOB (line-of-business) apps that can be considered your own/3rd party client apps.
What is the risk ?
– Any 3rd party azure AD OAuth2 web app (not spa) that relies on refresh tokens lifetime to align to restrictions configured in Sign-in-Frequency. If the back-end is evaluating the user session based on refresh token validity, then the session length can exceed that of which is configured in SIF.
¹ – Some clients require client_secret when exchanging refresh tokens, such clients typically live in the back-end
– It is worth mentioning, that even these clients, if they have proprietary endpoint to refresh refresh_tokens, can be abused if client is able to call the endpoint with any other session persistence mechanism that decouples the client from Authorization_code flow
² Third party clients mean non-Microsoft maintained OAuth2 applications.
- First party clients are MS native apps, that are registered already in the tenant, such as Azure CLI etc.
I am looking for peer review for this article – At this point I have confirmed these settings in one test environment, and would gladly accept information, which points out an misconfiguration (or misunderstanding) on my side.
I am also making this article public, because this is public information already (Github and MS forums), but I did not find a blog or docs article detailing these findings.
- Bypassing sign-in frequency requirements for Conditional Access on 3rd party clients
- 3rd party / custom Azure AD App – Web App (WEB API exchanges the Refresh Token)
- Redirect URI is type ’Web’
- 3rd party / custom Azure AD App – SPA web app (Client exchanges the Refresh Token)
- Redirect URI in is type ’Single-page application’
- 1st party app – Azure CLI
In my tests I request new access tokens with the refresh_token issued after authorization code flow. I will then try to use these refresh tokens in 1rd and 3rd party client, to see if refresh token is revoked after sign-in frequency time elapses
- I’ve also admin consented the API permissions to avoid triggering authorization_code flow (which we know works correctly)
✅ – Sign-in-frequency is enforced
❌ – Sign-in-frequency is not enforced
|first party client||✅||✅|
|third party client (Web as platform configured in app)||❌||✅|
|third party client (SPA as platform configured in app)||✅||✅|
These are tests on 3rd party client, done after the authorization code flow correctly prompts for reauthentication. (This is the point where I know, also refresh_tokens should not work)
- At this point I will only use refresh_token to bypass SIF enforcement
Single page app correctly revoke also refresh tokens
This article does not explore similar ramifications when the use case includes PRT (Primary Refresh Token)
When bypassing is possible
- User has access to refresh_token (Any library that stores the refresh_token on client side), or any implementation, where there is endpoint stores refresh-tokens, and issues those to clients with decoupled session management from Azure AD (User is not redirected to Azure AD for authorization_code)
results on Azure CLI
- Policy enforcement works
results on 3rd party client (Redirect URI type:web)
- Policy enforcement does not work
Log results show that enforcement would work, but reality is that this only applies to authorization code flow
- MFA is required except for trusted locations
This behavior is already publicly documented
There are examples where adjusting to ”all apps” should’ve worked, but I suspect the case is on Single-Page-App type redirect URI, which I’ve also confirmed to work.
- As you can see, in my case I am already using all apps policy for SIF (Sign-in-Frequency)
End of blog