12Knocksinna / Office365itpros

Office 365 for IT Pros PowerShell examples
MIT License
1.28k stars 567 forks source link

(401) Unauthorized - TeamsGroupsActivityReportV5 #50

Closed antondreyer closed 2 years ago

antondreyer commented 2 years ago

Good day, and thanks for the script. I think I am running into an issue where the Token isn't refreshing a second time - it's on a tenant with 50k+ teams

After about an hour I can see it saying it refreshed the token, and then after another hour it starts returning "The remote server returned an error: (401) Unauthorized."

By limiting the number of groups it loops through to 10 I did get a successful run, so i don't think its graph permissions

Does the script handle multiple refreshes of the token in all the loops that take a long time?

12Knocksinna commented 2 years ago

There's a loop in the script to get a refreshed token after 59 minutes.

Check if token is older than 50 minutes and request a refresh token

    $TimeRightNow = (Get-date)
    if($TimeRightNow  -ge $TokenExpiredDate){
        $body = @{
            client_id     = $AppId
            scope         = "https://graph.microsoft.com/.default"
            client_secret = $AppSecret
            grant_type    = "client_credentials"
        }

        $Params = @{
            'Uri' = "https://login.microsoftonline.com/$TenantId/oauth2/v2.0/token"
            'Method' = 'Post'
            'Body' = $Body
            'ContentType' = 'application/x-www-form-urlencoded'
        }

    # Get OAuth 2.0 Token
    try{
        $Refreshtoken = Invoke-RestMethod @Params
    }
    catch{
        Write-Host "An error occurred:"
        Write-Host $_ -ForegroundColor Red
        Write-ErrorLog 'An error occurred: {Error}' -PropertyValues $_
    }
    # Unpack Access Token

    if ($null -ne $Refreshtoken) {
        $Token = $Refreshtoken.access_token
        Write-Host "Token Refreshed at $TimeRightNow" -ForegroundColor Red
    }
    else {Write-Host "Not refreshed, Token is empty" -ForegroundColor Red}
    $TokenExpiredDate = (Get-date).AddMinutes($TimeToRefreshToken) 
    }
#### END of Check if token is older than 50 minutes and request a refresh token #######

Looking at the code, it seems like it works for once through the refresh loop but maybe fails after that. Perhaps if you chance the unpack access token code to that shown below it will work for multiple loops.

    # Unpack Access Token

    if ($null -ne $Refreshtoken) {
        $Token = $Refreshtoken.access_token
        Write-Host "Token Refreshed at $TimeRightNow" -ForegroundColor Red
        $TokenCreationDate = (Get-Date)
        $TokenExpiredDate = (Get-Date).AddMinutes($TimeToRefreshToken) 
antondreyer commented 2 years ago

Unfortunately still the same result

# Unpack Access Token

if ($null -ne $Refreshtoken) {

should $Refreshtoken not perhaps be set to null somewhere so it can evaluate true on the next run?

Also not sure if "$TokenCreationDate = (Get-Date)" serves any purpose? Doesnt seem to be used anywhere

12Knocksinna commented 2 years ago

It's no excuse, but another contributor added that piece of code. I will get to testing and refining it some time. Finding 40K teams to test it against might be a challenge...

12Knocksinna commented 2 years ago

I did some testing...

I moved the code to get an access token into a function

`function GetAccessToken {

function to return an Oauth access token

Define the values applicable for the application used to connect to the Graph

$AppId = "828e1143-88e3-492b-bf82-24c4a47ada63" $TenantId = "b662313f-14fc-43a2-9a7a-d2e27f4f3478" $AppSecret = 'sK78Q~EYQVuAU5EURRtTZhl4uH-e-WAinDEfqaMW'

Construct URI and body needed for authentication

$uri = "https://login.microsoftonline.com/$tenantId/oauth2/v2.0/token" $body = @{ client_id = $AppId scope = "https://graph.microsoft.com/.default" client_secret = $AppSecret grant_type = "client_credentials" }

Get OAuth 2.0 Token

$tokenRequest = Invoke-WebRequest -Method Post -Uri $uri -ContentType "application/x-www-form-urlencoded" -Body $body -UseBasicParsing

Unpack Access Token

$Token = ($tokenRequest.Content | ConvertFrom-Json).access_token

Return $Token } `

and then replaced all the code that's currently in place to check for token renewal with:

Check if token needs to be refreshed. If it does, request a new token

    $TimeNow = (Get-Date)
    if($TimeNow -ge $TokenExpiredDate){
       $Token = GetAccessToken
       $TokenExpiredDate = (Get-date).AddMinutes($TimeToRefreshToken) 
       Write-Host "Requested new access token - expiration at" $TokenExpiredDate 
    }

#### END of Check if token is older than 50 minutes and request a refresh token #######

I changed the token renewal check to 3 minutes and the script ran as expected with renewal happening on schedule. You can apply the change to your script and try it out or wait until I get done with another change that I want to make (sometime soon).

12Knocksinna commented 2 years ago

I've updated the script to V5.5. This version includes the changes to improve token handling.