Force Teams to Sign Out

Token Resistance

I ran into an interesting scenario yesterday during a tenant migration where users from tenant A were successfully migrated to Tenant B, but their accounts remained logged into Teams – even changing the user account names to their onmicrosoft.com domain and removing their Teams license wouldn’t force them to log out… talk about a token that won’t quit!

The goal here was that the users would log out of Teams – and since their old UPNs were changed behind the scenes – when they tried to log back in using their regular username and password, they’d be passed into the new tenant instead of remaining in the old one. However, this was not happening, and users were remaining signed in to the old tenant – very frustrating!

Initiate Sign-out (the Ctrl-Alt-Del of the cloud)

Now, you should normally be able to force this logout by going to the admin portal, clicking on the user account, then the OneDrive tab, and using the Initiate sign-out option:

In fact, this was a step that was already tried by the team working on the migration (and it didn’t work), before the question even made it across my inbox to see if there was a way I could help force the logout and get this migration wrapped up successfully. This was indeed puzzling, as my expectation was that initiating a one-time sign-out like this would indeed work across all sessions, the way it promised, so I needed to do some testing, and some research!

First off, it turns out that the sign-out option does work – it just takes a while. In my initial testing, web sessions, Office clients, mobile clients, etc. will log out within 5-15 minutes, as promised. However, the Teams client holds on to that token a lot longer after it’s been invalidated and will not sign out until after an hour. Now, that’s not horrible, but it’s still not going to give me the controlled experience that I was looking for, so I needed to dive a little deeper – especially as I wanted PowerShell options… no way would I click through hundreds of users to make this happen!

Force Kick – the Way of the Shell

To do this through PowerShell, you first need to have the Azure AD Preview module installed – if you don’t have it on your system already, open an admin shell and install it by using the Install-Module AzureADPreview command. After that, you can go ahead and connect to Azure AD using Connect-AzureAD, and logging in as normal.

First thing to do is find your user, and see when their current access token is valid from, like so:

Get-AzureADUser -SearchString "Lester Tester" | Select *token*

This will return the RefreshTokensValidFromDateTime, which essentially tells you that any token issued AFTER this date/time is valid – note that the time is in UTC, so you’ll need to adjust for your own time zone.

So far this is just informational, but still useful – especially if you want to find any users you might have missed, or who still have old tokens sticking around. Now, to revoke this access token, simply use the following command:

[powershell]
Get-AzureADUser -SearchString "Lester Tester" | Revoke-AzureADUserAllRefreshToken
[/powershell]

This command won’t return anything in the Shell, but if you run the Get-AzureADUser command from above one more time, you should see that your refresh token validation date has been set to the current date and time (again, don’t forget you need to convert from UTC to your own time zone – very annoying!!).

Now that we have the PowerShell basics down, we can easily take this command and scale it out to as many users as we want by filtering on an available attribute – let’s take for instance, the Test family in my tenant:

[powershell]
Get-AzureADUser | ? {$_.DisplayName -match "Test"} | Select DisplayName,RefreshTokensValidFromDateTime
[/powershell]

Once you have your filter working the way you want, just go ahead and re-run that command and pipe it out to the Revoke command:

[powershell]
Get-AzureADUser | ? {$_.DisplayName -match "Test"} | Revoke-AzureADUserAllRefreshToken
[/powershell]

If you run your Get command again, you can see that all our users in scope have had their old tokens invalidated:

Now we have a process set that we can scale out and revoke the tokens for hundreds or even thousands of users if we need to. However, that still didn’t solve our timing problem, so I logged in to the Azure AD portal to see if there was somewhere else that I could make this sign-out happen when I wanted it to, and not after an hour (or whenever Teams felt like letting go of its token). Unfortunately, there is not any other magic kill switch in the Azure AD portal, but I did discover something interesting – both running the initiate sign-out option, and revoking the access token through PowerShell do the exact same thing, but with different actors initiating the change:

Sadly, this doesn’t really get us any closer to the final answer – it simply tells me that either method is essentially doing the same thing behind the scenes, and you should have the exact same experience in both cases. Now let’s see what we can do about that delay!

Tweaking that Token

I knew that some time back, Microsoft had introduced the option to configure token lifetimes, so I wanted to see if there was a way I could shorten that process, and get these Teams users logged out when I wanted, not lingering around on the old tenant the way they were. If you check the link above, you can find a lot of interesting info about this whole process, but here’s a handy reference to the defaults, and what they can be changed to:

Seeing that I could get my tokens down as low as 10 minutes was starting to sound a lot more interesting to me, so I wanted to build this out and do some more testing. Here’s what I did:

Start by checking to see which policies you have in place by using Get-AzureADPolicy. If your tenant is like mine, it probably looks like this:

By default, there are no Token Lifetime Policies in place, so we’re going to go ahead and create one.

[powershell]
New-AzureADPolicy -Definition @(‘{"TokenLifetimePolicy":{"Version":1,"AccessTokenLifetime":"00:10:00","MaxInactiveTime":"00:10:00"}}’) -DisplayName "OrganizationDefaultPolicyScenario" -IsOrganizationDefault $true -Type "TokenLifetimePolicy"
[/powershell]

Voila:

Make note of your policy ID so that you can use it to target this policy if you want to update it (using Set-AzureADPolicy) or delete it (using Remove-AzureADPolicy). Now that our Access Token Lifetime and Max Inactive Time were both set to 10 minutes, I tested again revoking an access token with a user that was signed into Outlook on the Web, Teams in a different browser, the Teams desktop client, and Teams on a mobile device.

Here’s how they behaved:

  1. Outlook on the Web: logged out immediately
  2. Teams Web & Mobile: logged out about 2 minutes later
  3. Teams Desktop: logged out in less than 5 minutes

Sweet, sweet victory!

Final thoughts

After all this fun, here’s a couple of things to keep in mind:

  • You can revert your settings to default by either deleting the policy you created, or by using the following PowerShell command:

[powershell]
New-AzureADPolicy -Definition @(‘{"TokenLifetimePolicy":{"Version":1,"MaxInactiveTime":"14.00:00:00","MaxAgeSingleFactor":"90.00:00:00","MaxAgeMultiFactor":"90.00:00:00","MaxAgeSessionSingleFactor":"until-revoked","MaxAgeSessionMultiFactor":"until-revoked"}}’) -DisplayName "OrganizationDefaultPolicyScenario" -IsOrganizationDefault $true -Type "TokenLifetimePolicy"
[/powershell]

If for some reason you need to keep your existing policy in place, just target it using the Set-AzureADPolicy command, and update the pieces you need to change – worst-case scenario, delete the policy and create it fresh using the command above. (more info here)

  • I’m not 100% sure of the impact of running your long term with your access tokens refreshing this quickly – I assume that if Microsoft is allowing you to change the token lifetimes down to 10 minutes, that it’s acceptable to do so. I did run for a couple of hours like this without any adverse side effects, but that’s in my test tenant, not a production environment. If I needed to fulfill the scenario that started this whole thing, I’d probably change my token lifetime policy a day or two before my migration cutover so that all the users would be revoked immediately upon cutover. After that, I’d personally set it back to defaults unless I had a compelling reason to keep the lifetime shorter.
  • Also, I wouldn’t waste too much time with this, as they’ve already announced that they’re deprecating this feature and instead moving it to a conditional access policy. No clue what that looks like yet but know that it’s coming.
  • All in all, an intriguing problem, and a fun trip to the solution – hope this helps someone else! Feel free to leave a comment below if you have any questions, or to let me know if any of this was helpful. 😀

     

    Leave a Reply

    Fill in your details below or click an icon to log in:

    WordPress.com Logo

    You are commenting using your WordPress.com account. Log Out /  Change )

    Google photo

    You are commenting using your Google account. Log Out /  Change )

    Twitter picture

    You are commenting using your Twitter account. Log Out /  Change )

    Facebook photo

    You are commenting using your Facebook account. Log Out /  Change )

    Connecting to %s

    This site uses Akismet to reduce spam. Learn how your comment data is processed.