How to Renew OAuth Tokens in Workspace ONE UEM

You may have come here from my other blog “How to Use REST API in Workspace ONE UEM (with PowerShell)”. In it, I mention that OAuth tokens are only valid for one hour. Most of the time, this is not a problem as the majority of API calls definitely do not take longer than one hour. As you start to work in larger environments or with larger data sets, you can start to hit this one-hour limit. In this blog, I’ll detail how to renew your OAuth tokens if you get close to the one-hour expiration time.

Overall Structure

Before we get into the detail, let’s just review the overall structure of how this will work so that you will have a good understanding.

First, we’ll need to create two new functions. This is so that we can easily make a call to get a new OAuth token and subsequent REST API header (which includes the token) from anywhere in the script. They will be called Get-WorkspaceOneOAuth and Get-Header.

Second, we’ll need a timer and a simple check to see how long the timer is elapsed. The timer part is inside of the Get-Header function and the time check is in the actual function where you are making API calls. Once the timer gets to 55 min or so, it will request a new header and start the timer over.

And third, I am also adding the ability to use both version 1 and version 2 of the API.

Function Get-WorkspaceOneOAuth

This function contains the access token URL, the Client ID and Secret of your OAuth account, and the API call to get an access token for this session. This access token is the one that only lasts for 60 min. This part is essentially the same as what is detailed in the other blog except that the function returns the actual access token. Here is the code:

Function Get-WorkspaceOneOAuth {
Write-Host "Getting OAuth Token..."
$client_id = "enter_your_id_here"
$client_secret = "enter_your_secret_here"
$access_token_url = "https://uat.uemauth.vmwservices.com/connect/token"
$body = @{
	grant_type    = "client_credentials"
	client_id     = $client_id
	client_secret = $client_secret
}
 
try {

        $response = Invoke-WebRequest -Method Post -Uri $access_token_url -Body $body -UseBasicParsing
        $response = $response | ConvertFrom-Json
        $oauth_token = [string]$($response.access_token)

    }
    catch {
        $ErrorMessage = $PSItem | ConvertFrom-Json
        Write-Host "Failed to create OAuth Token with following ErrorCode: $($ErrorMessage.errorCode) - $($ErrorMessage.message)" -ForegroundColor Red
    }

    Return $oauth_token

}

Next, we’ll create the Get-Header function.

Function Get-Header

This function builds the complete header including the OAuth access token. It calls the Get-WorkspaceOneOAuth function in order to get it. It also starts the timer when the OAuth access token is requested so that we can track how long its been and request a new one once it’s getting close to expiring.

Additionally, it supports both versions 1 and 2 of the API. You can add more to the switch statement if you have a need to use version 3 or 4 of any API. Because both the version and OAuth access token live in the header itself, we need separate timers. I have chosen to make these global variables so that they are accessible anywhere in the script regardless of the function.

function Get-Header
{
  [CmdletBinding()]
  Param(
      [Parameter(Mandatory = $True)]
      [string]$version
  )   
  $oauthToken = Get-WorkspaceOneOAuth
  $header = @{
      "Authorization" = "Bearer " + $oauthToken;
      "Accept" = "application/json;version=$version";
      "Content-Type" = "application/json"
  }
  switch ($version) {
      '1' { 
            $global:OAuthTimer1 = [System.Diagnostics.Stopwatch]::StartNew()
         }
      '2' {  
           $global:OAuthTimer2 = [System.Diagnostics.Stopwatch]::StartNew()
         }
      Default {}
  }

    Write-Host "Oauth Token: $oauthToken "
    return $header
}

So how do you use this? It’s very simple. Just create a variable and then call the Get-Header function with the version as the parameter, like this:

$global:header_v1 = Get-Header -version 1
$global:header_v2 = Get-Header -version 2

Then after you have done this, make your API call:

Invoke-RestMethod -Uri "$server/api/mdm/devices/search?" -Method Get -Headers $global:header_v1

Checking the OAuth Timer and Renewing the Token

Now we just need a little bit of code in order to check the OAuth timer to see if we need to request a new token. You’ll need one for the specific version of the API you are using. This also does a check to see if the timer was never started due to the header never being previously requested.

Version 1:

if(($Global:OAuthtimer1.Elapsed.TotalMinutes -gt 55) -or ($null -eq $Global:OAuthtimer1)){
    Write-Host "OAuth token expiring within 5 min or doesn't exist, renewing..."
    $global:header_v1 = Get-Header -version 1
}

Version 2:

if(($Global:OAuthtimer2.Elapsed.TotalMinutes -gt 55) -or ($null -eq $Global:OAuthtimer2)){
    Write-Host "OAuth token expiring within 5 min or doesn't exist, renewing..."
    $global:header_v2 = Get-Header -version 2
}

Put this little code snippet around each “Get-Header” request you make and you’ll be in good shape and won’t have to worry about it expiring on you.

Real World Example

So let’s put this into a real-world example. Say you have queried your UEM console for devices and must check the profile statuses on each device to see if one or more are successfully installed. This requires that you must make a bunch of individual API calls, one for each device. If you have 10,000 devices, that is going to take a really long time and will most certainly run longer than one hour. How do we tackle this?

Create a function called Get-ProfileStatus and configure it to have “deviceID” as a parameter. Then when you loop through each device you can query the profile status of each while ensuring your OAuth token doesn’t expire.

Function Get-ProfileStatus {
    param (
        $DeviceID
    )
    Write-Host "Checking profiles on device: $deviceID"
    $Uri = "$server/API/mdm/devices/$DeviceID/profiles"
    if(($Global:OAuthtimer1.Elapsed.TotalMinutes -gt 55) -or ($null -eq $Global:OAuthtimer1)){
        Write-Host "OAuth token expiring within 5 min or doesn't exist, renewing..."
        $global:header_v1 = Get-Header -version 1
    }
    $Profiles = (Invoke-RestMethod -Method GET -Uri $Uri -ContentType "application/json" -Header $global:header_v1).DeviceProfiles | Select Name, @{Name = 'ProfileId'; Expression = { $_.id.value } }, Status
    $Profiles
}

The link to the same code outlined in this blog is located here.

Conclusion

In this blog, you learned how to properly keep alive your OAuth token in case your script runs longer than one hour. I do highly recommend that you optimize your script as much as possible so that you never have to worry about this, but in some situations, you will have long-running scripts that necessitate adding this in. Good luck scripting!

Share on:

2 thoughts on “How to Renew OAuth Tokens in Workspace ONE UEM”

  1. What is the reason to separate timers to versions as oauth_token does not need a version, and renewing oauth_token is common for both version.

    Reply
    • Originally I did this because the API version is in the actual header along with the OAuth token and it was just easier to manage the timer of each header. But now that I think about it, you could create two headers each time you need to request one token and just populate the same token into each header.

      Reply

Leave a Comment