0
votes

I'm trying to convert some existing Exchange Online EWS scripts to use Oauth. I'm able to request an access token, but when I try to work with a mailbox it errors with the following. I appreciate your help!

x-ms-diagnostics: 2000003;reason="The audience claim value is invalid for current resource. Audience claim is 'https://graph.microsoft.com', request url is 'https://outlook.office365.com/EWS/ Exchange.asmx' and resource type is 'Exchange'.";error_category="invalid_resource"

Here's the code:

## Request an access token

# Define AppId, secret and scope, your tenant name and endpoint URL
$AppId = 'APP-ID HERE'
$AppSecret = 'SECRET HERE'
$Scope = "https://graph.microsoft.com/.default"
$TenantName = "OurDomain.onmicrosoft.com"
$Url = "https://login.microsoftonline.com/$TenantName/oauth2/v2.0/token"

# Add System.Web for urlencode
Add-Type -AssemblyName System.Web

# Create body
$Body = @{
    client_id = $AppId
    client_secret = $AppSecret
    scope = $Scope
    grant_type = 'client_credentials'
}

# Splat the parameters for Invoke-Restmethod for cleaner code
$PostSplat = @{
    ContentType = 'application/x-www-form-urlencoded'
    Method = 'POST'

    # Create string by joining bodylist with '&'
    Body = $Body
    Uri = $Url
}

# Request the token!
$Request = Invoke-RestMethod @PostSplat

#######################

# Import "Microsoft Exchange Web Services Managed API 2.2"
Import-Module -Name "C:\Program Files\Microsoft\Exchange\Web Services\2.2\Microsoft.Exchange.WebServices.dll"

## Create the Exchange Service object with Oauth creds
$Service = New-Object Microsoft.Exchange.WebServices.Data.ExchangeService -ArgumentList Exchange2013_SP1
$service.Url= new-object Uri("https://outlook.office365.com/EWS/Exchange.asmx")
$Service.TraceEnabled = $true
$Service.Credentials = New-Object Microsoft.Exchange.WebServices.Data.OAuthCredentials($Request.access_token)

#####################

$Email = "[email protected]"

# Set the WellKnownFolder
$FolderId = [Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::Inbox

# Bind to WellKnownFolder Notes
$folder = [Microsoft.Exchange.WebServices.Data.Folder]::Bind($Service, $folderId)

Write-Host "$($Email): $($folderName):  " -NoNewline 
$folder.archivetag.RetentionId.Guid     
1
John, Thanks for your reply. This is my first time working with Graph, and I'm having a lot of trouble. I probably need to do more thorough reading, but I thought changing the authentication would be relatively easy. It turns out to be more complicated for me. I"m not clear on how to adjust my script above to "Make sure that you obtain a refresh token when you request your Graph token (by including the offline_access scope in your auth/token requests). Then use that refresh token to obtain a second token with the proper audience.". Are there clear examples using powershell anywhere? - mbromb
You are using client_credentials which means no refresh token as you just reissue the access token when required. Instead look at scopes in your call: docs.microsoft.com/en-us/azure/active-directory/develop/… - John Hanley
Also, look at directly authorizing with Office 365. docs.microsoft.com/en-us/outlook/rest/get-started - John Hanley

1 Answers

0
votes

In your script you will need to change the scope original scope from

$Scope = "https://graph.microsoft.com/.default"

to

$Scope = "https://outlook.office365.com/.default"

The rest of your code isn't using the Graph so you don't need to get an access token for something your not using. Also because your using an App Secret you would be generating an App Only token which means you won't have a refresh token anyway. The two things that is missing from your EWS code is you need to use EWS Impersonation and you should also always set the X-AnchorMailbox header eg you would have

$service.HttpHeaders.Add("X-AnchorMailbox", "[email protected]")
$service.ImpersonatedUserId = new-object Microsoft.Exchange.WebServices.Data.ImpersonatedUserId([Microsoft.Exchange.WebServices.Data.ConnectingIdType]::SmtpAddress, "[email protected]")