Free Support Forum - aspose.com

OAuth2 support for O365 with Application permissions (C# .NET)

Hello,

I would like to access an Office 365 mailbox using OAuth Application permissions.
If I’m not mistaken, the code sample in OAuth2 support for O365 uses OAuth Delegated permissions and not Application permissions.

When we use the following code to implement an ITokenProvider:

internal class AzureTokenProvider : ITokenProvider
{
    private readonly string _tenantId;
    private readonly string _clientId;
    private readonly string _clientSecret;
    private readonly string[] _scopes;

    private readonly object _tokenSyncObj = new object();
    private OAuthToken _token;

    /// <summary>
    /// Initializes a new instance of the <see cref="AzureTokenProvider"/> class
    /// </summary>
    public AzureTokenProvider(string tenant, string clientId, string clientSecret, string[] scopes)
    {
        _tenantId = tenant;
        _clientId = clientId;
        _clientSecret = clientSecret;
        _scopes = scopes;
    }

    /// <summary>
    /// Gets OAuth access token. 
    /// </summary>
    /// <param name="ignoreExistingToken">
    /// If ignoreExistingToken is true, requests new token from a server. Otherwise behaviour is depended on whether token exists or not.
    /// If token exists and its expiration date is not expired returns current token, otherwise requests new token from a server.
    /// </param>
    /// <returns>Returns oAuth access token</returns>
    public virtual OAuthToken GetAccessToken(bool ignoreExistingToken)
    {
        lock (_tokenSyncObj)
        {
            if (_token != null && !this._token.Expired && !ignoreExistingToken)
                return _token;

            _token = GetNewAccessToken();
            return _token;
        }
    }

    /// <summary>
    /// Gets oAuth access token.
    /// If token exists and its expiration date is not expired returns current token, otherwise requests new token from a server.
    /// </summary>
    /// <returns>Returns oAuth access token</returns>
    public OAuthToken GetAccessToken()
    {
        return GetAccessToken(false);
    }

    private OAuthToken GetNewAccessToken()
    {
        // https://docs.microsoft.com/en-us/exchange/client-developer/exchange-web-services/how-to-authenticate-an-ews-application-by-using-oauth
        var app = ConfidentialClientApplicationBuilder
            .Create(_clientId)
            .WithAuthority(AzureCloudInstance.AzurePublic, _tenantId)
            .WithClientSecret(_clientSecret)
            .Build();

        //Make the token request
        var authResultTask = app.AcquireTokenForClient(_scopes).ExecuteAsync();
        var authResult = authResultTask.Result;

        return new OAuthToken(authResult.AccessToken, TokenType.AccessToken, authResult.ExpiresOn.LocalDateTime);
    }

    /// <summary>
    /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
    /// </summary>
    public virtual void Dispose()
    {
    }
}

And use the following code to connect:

        var ewsScopes = new string[] { "https://outlook.office.com/.default" };
        var azureTokenProvider = new AzureTokenProvider(_mailboxSettings.AzureTenantId,
            _mailboxSettings.AzureClientId,
            _mailboxSettings.AzureClientSecret,
            ewsScopes);

        NetworkCredential credentials = new OAuthNetworkCredential(_mailboxSettings.Username, azureTokenProvider);
        _ewsClient = EWSClient.GetEWSClient(_mailboxSettings.Host, credentials);

we always get the following exception:
System.Web.Services.Protocols.SoapException
HResult=0x80131501
Message=ExchangeImpersonation SOAP header must be present for this type of OAuth token.
Source=System.Web.Services
StackTrace:
at System.Web.Services.Protocols.SoapHttpClientProtocol.ReadResponse(SoapClientMessage message, WebResponse response, Stream responseStream, Boolean asyncCall)
at System.Web.Services.Protocols.SoapHttpClientProtocol.Invoke(String methodName, Object[] parameters)
at #=zXtdwOglEJf$aKzfPTIvDlrbACe_cFhBgycEaMNg8xwNKGTyUxA==.GetFolder(GetFolderType GetFolder1)
at Aspose.Email.Clients.Exchange.WebService.EWSClient.GetEWSClient(String mailboxUri, ICredentials credentials, WebProxy proxy)
at Aspose.Email.Clients.Exchange.WebService.EWSClient.GetEWSClient(String mailboxUri, ICredentials credentials)
at GenericDocumentImport.Modules.Email.ExchangeProtocolApi.CheckClient()

We are using Aspose.Email version 20.02

@Luk_De_Reu,

I suggest you to please visit the following thread for your convenience as it has solution for addressing the similar OAut2 authentication issues. Also, please try using latest Aspose.Email for .NET 20.5 as there was a bug related to EWSClient in OAuth authentication which has been fixed.

We have upgraded to Aspose.Email for .NET 20.5, but the issue remains.

I have read the thread that you refer to, but this is not applicable because:

  1. We don’t have a problem obtaining an OAuth token, I have tested that AzureTokenProvider.GetToken() indeed manages to obtain a token for the requested scope. We get an exception from EWSClient.GetEWSClient() after it has called GetToken() on the AzureTokenProvider we have provided. The message has something to do with the SOAP call to EWS, not with the authentication. Cfr. the exception message is: ExchangeImpersonation SOAP header must be present for this type of OAuth token.
  2. In our scenario, we are using EWSClient and not IMAPClient.

I can share a code sample which connects to a Office 365 demo tenant if this would assist you in troubleshooting.

@Luk_De_Reu,

That would be good if you provide us a working example reproducing issue along with all necessary details.

The code sample relies on an Office 365 tenant, can I send you the Azure connection data by mail instead of posting them on this forum?
Code sample in attachment.
ConnectToEWSWithAppPermissions.zip (4.3 KB)

@Luk_De_Reu,

You can please share the connection data privately with us using Message option. For this, you will have to press on my Name Icon and use Message option to send the private message.

image.png (9.0 KB)

@Luk_De_Reu,

Thank you for sharing the information. A ticket with ID EMAILNET-39868 has been created in our issue tracking system to further investigate the issue. We will share the notification with you as soon as the issue will be investigated.

@Luk_De_Reu,

Thank you for sharing the information privately. As requested in private message, can you please also share the screen shots of application permissions as well.

@Luk_De_Reu,

I have received application permissions in a private message via your colleague @pdhert. I have associated information in our issue tracking system and will get back to you with feedback as soon as the issue will be fixed.

@Luk_De_Reu, have you been able to resolve this issue? I am having the exact same SOAP exception…

@marieke.saeij,

I regret to share that at present there are no updates available. We will share updates with you as soon as the issue will be addressed.