Zapping Tokens: A Routine Refresh
Ok. Here’s a rather meaty write-up, so strap in. We’re in for a long one.
Use Case: You have a conditional access policy for MFA. Users are complaining because their token expires midday. Let’s zap those tokens and cause a reauth request off hours.
We are going to be leveraging the Microsoft Graph PowerShell module so before we get into the fun scripting stuff, we need to make a Registered App in Entra ID.
Prerequisites: We need to use PowerShell 7, and we need to ensure we have the Microsoft.Graph module installed.
TL;DR (just gimme the scripts!): Refresh-M365Tokens.ps1 and Encrypt-GraphAppReg.ps1
Install-Module Microsoft.Graph.Beta -Repository PSGallery -Force
Lets make a Registered App in Entra ID. Simply login to EntraID with an account that has at least the Application Developer permission assigned. Now navigate to the following in EntraID: Applications > App registrations and select “New registration at the top”
Now enter a superficial name that sums up your use, and be sure to select the “Accounts in this organizational directory only” radio button under the “Supported account types” selection.

Now that you have created the app, go to “Certificates & secrets” in the blade menu: create a new client secret, enter a description and set your expiration accordingly.
Now save a copy of your Secret ID somewhere safe. We will need it for this next step. If you leave this page without saving, it will obscure and you cannot retrieve it again.

Next, let’s encrypt our registered app’s secret ID by utilizing another of my scripts. This is a two part process. Check out the script here on GitHub: Encrypt-GraphAppReg.ps1
Run part one to create an encryption key. This will output to the path of your choosing.
$key = New-Object byte[] 32
[Security.Cryptography.RNGCryptoServiceProvider]::Create().GetBytes($key)
# Save the key to a file (replace path if you want)
$keyFilePath = "<path>\graphAes.key"
$key | Set-Content -Path $keyFilePath -Encoding Byte
Write-Host "AES key saved to $keyFilePath"
Follow up with part two, where we will be prompted for our Secret ID, which will be encrypted and written to a .txt file.
# Read the key you saved earlier
$keyFilePath = "<path>\graphAes.key"
$key = Get-Content -Path $keyFilePath -Encoding Byte
# Prompt for your client secret securely
$secureSecret = Read-Host "Enter your Azure AD App Client Secret" -AsSecureString ## when prompted enter in the Value info from the app registration
# Encrypt the secret using the AES key and convert to string
$encryptedSecret = $secureSecret | ConvertFrom-SecureString -Key $key
# Save the encrypted secret to a file (replace path if needed)
$secretFilePath = "<path>\graphSecret.txt"
Set-Content -Path $secretFilePath -Value $encryptedSecret
Write-Host "Encrypted client secret saved to $secretFilePath"

Now that we have our .key file and our .txt file saved to the path of our choosing, let’s take a deeper look at the main star of this write up: Refresh-M365Tokens.ps1
First, flip back to EntraID and get the App ID for your newly created registered app.

Additionally, ensure that the registered app has the following EntraID permissions:
Microsoft Graph:
Directory.Read.All
Directory.ReadWrite.All
User.Read.All
User.ReadWrite.All

Now, let’s dive into the script. First we’ll manage our encrypted secret ID by entering in the path where the files (.key and .txt) are located.
$keyPath = <#path to .key file #>
$secretPath = <# path to secret key file #>
$key = [System.IO.File]::ReadAllBytes($keyPath)
$encSecret = Get-Content -Path $secretPath -Raw
$secureSecret = $encSecret | ConvertTo-SecureString -Key $key
Next, lets define our tenant ID and registered app ID, and then auth to EntraID.
# Set AppId and TenantId
$appId = <# enter appID here in double quotes#>
$tenantId = <# enter tenantID here in double quotes#>
Write-Log -Message "Connecting to Microsoft Graph using ClientSecretCredential"
$clientSecretCredential = [PSCredential]::new($appId, $secureSecret)
# Connect with App-only authentication
Connect-MgGraph -TenantId $tenantId -ClientSecretCredential $clientSecretCredential
You can leverage the ability to exclude certain accounts, but this is optional. You’re going to want to enter the full UserPrincipalName in the exceptions, seperated by commas. Example: [email protected], [email protected]
# Define accounts to exclude
$ExcludeAccts = @( <#enter accounts here#> )
Now, let’s use our Get-MgUser cmdlet and cycle through each non-excluded user account. Then we’ll use Revoke-MgUserSignInSession to zap the token.
# Get all users, excluding the accounts listed above
$allUsers = Get-MgUser -All | Where-Object { $ExcludeAccts -notcontains $_.UserPrincipalName }
foreach ($user in $allUsers) {
Write-Log -Message "Revoking sessions for $($user.UserPrincipalName) (ID: $($user.Id))"
try {
Revoke-MgUserSignInSession -UserId $user.Id
Write-Log -Message "Revoked sessions successfully for $($user.UserPrincipalName)"
}
catch {
Write-Log -Message "Failed to revoke sessions for $($user.UserPrincipalName): $_" -Level "Error"
}
}
Running this process will skim through your list of EntraID users, expire their active tokens, thus enforcing a sign-in request on all platforms (mobile, web, local client, i.e. anything accessing the tenant). I like to setup a scheduled task via Task Scheduler to run this on routine.
As always, remember:
This project is provided “as is” without any warranty of any kind, express or implied. Use it at your own risk. The authors and contributors are not responsible for any damage, data loss, or other issues that may arise from using this software. You are solely responsible for any actions taken based on this code.