Authenticating to Microsoft (Office) 365 with OAuth as an Unattended Application


Requirements: IPWorks, Cloud Keys, Cloud Mail, or Cloud Storage

The Microsoft identity platform supports authentication with OAuth either as a signed-in user (i.e., "delegated" mode) or as an unattended service application (i.e., "app-only" mode). This guide covers the client credential grant flow for OAuth, which is used to authenticate applications to the Microsoft Graph API and the Office 365 Exchange service when a signed-in user is not present.

Contents

Overview and Background Information

Microsoft recommends that the client credential grant flow be used only by applications that run without a user present, such as web apps, web API apps, and daemon or service apps. This is because the client credential grant flow requires application-level permissions (scopes), which in turn require a client secret or client certificate to be configured. In addition, Microsoft requires additional steps when the application is first configured to work with Office 365 Exchange services such as IMAP, POP3, and SMTP.

Like other OAuth flows, the client credential flow consists of two phases.

"Image shows a flow diagram for client credentials in Microsoft identity platform"

In the first phase, the application must obtain consent for the specified scopes in order to access protected resources, such as a mailbox used to send emails. In the client credential grant flow, this consent is obtained from the administrator of the tenant rather than from the owner of the protected resource. This step occurs once, while the application is being set up in its working environment. The process of obtaining consent from the administrator is described in the Administration section below.

The second phase is authentication with the token server, which does not require any user interaction. This is one of the main advantages of the client credential grant flow. To authenticate, the application provides its client ID along with a credential, such as a client secret or certificate. Obtaining these credentials and making the authentication request are described in the sections below.

Application Registration

The first step in configuring an application for the client credential grant flow is to register it with Azure. For detailed instructions on registering an application, please see our Setting up the Azure Portal for OAuth article.

Multitenant vs. Single Tenant

For the client credential grant flow, Microsoft requires that the application be either single tenant or multitenant. Personal Microsoft accounts cannot be used, since they have no administrator to grant consent. When deciding which account types to allow, consider who will have access to the application. When an administrator grants consent to the scopes, the consent applies to the entire tenant. Therefore, if administrators in multiple tenants grant consent, the application can use the same credentials to access the protected resources of all of those tenants. More information on the account types can be found in the Supported Account Types section of the setup article.

Platforms and Redirect URIs

The client credential grant type does not require any platform configurations to be added. However, to generate an admin consent URL using the OAuth component (described below), a redirect URI must be added to the Web platform type. This redirect URI is typically in the format http://localhost:PORT. Note that Microsoft does not currently perform port matching for localhost redirect URIs, so the port in the registered redirect URI does not need to match the port in the request's redirect_uri parameter. More information on platforms and redirect URIs can be found in the Platforms and Redirect URIs section of the setup article.

Application Scopes

When adding scopes for the application, select "Application permissions" scopes instead of "Delegated permissions" scopes. Application permissions allow the application to act on its own, rather than on behalf of a signed-in user, and typically grant broader access to resources across the organization. Microsoft recommends requesting only the permissions that are necessary for the application's functionality, which reduces the potential impact if the application or its credentials are compromised. More information on permissions can be found in the Application vs. Delegated Permissions section of the setup article.

The specific API and permissions to configure in Azure depend on the service being used:

  1. Projects that use the Office365 component in Cloud Mail (or other components that use Microsoft Graph) require application permissions under the "Microsoft Graph" API. This API provides access to a wide range of Office 365 services. "Image shows permission options for Microsoft Graph API. Options are delegated permissions or application permissions"
  2. Projects that use a legacy mail protocol such as IMAP, POP3, or SMTP require application permissions under the "Office 365 Exchange Online" API. This API provides access to Exchange Online services, including email functionality through the legacy protocols. "Image shows permission options for Office 365 Exchange Online. Options are delegated permissions or application permissions"

Selecting the correct API and application permissions ensures that the application has the access it needs to the desired resources.

Credentials

When using the client credential grant flow, Microsoft requires credentials to be configured for the application. Two types of credentials are supported: client secrets and certificates. A client secret is a key that is generated in Azure and stored securely by the application, which provides it along with its client ID when requesting an access token.

"Image shows a credentials window with 'Certificates & Secrets' selected on the left hand side and the 'Client secrets' tab selected. 'new client secret' is selected on the right side"

"Image shows details added for creating a client secret with a secret description of 'Demo Secret' and an expiration of the recommended 6 months."

While client secrets are easier to set up and maintain, they are considered less secure than certificates. Certificates offer a higher level of security, but require more setup and maintenance. Certificates use a public-private key pair, where the public key is registered with Azure and the private key is stored securely by the application.

"Image shows a credentials window with 'Certificates & Secrets' selected on the left hand side and the 'Certificates' tab selected. 'Upload Certificate' is selected on the right side"

"Image shows details added for uploading a certificate with a file of "localhost_pub.cer" and a description of "Demo public certificate"."

The choice between client secrets and certificates should be based on the application's security requirements and the ability to manage the credentials over time. More information on the credential types can be found in the Credentials section of the setup article.

Administration

All application permissions require an Azure administrator to grant consent for the scopes requested by the application. There are two ways for an administrator to provide consent. If the administrator is in the same tenant as the application registration, they can grant consent directly from the Azure Portal. Otherwise, an admin consent URL must be generated, which can be done using the OAuth component.

Azure Portal

If the administrator is in the same tenant as the application registration, they can grant consent directly from the Azure Portal. To do so, navigate to the "API permissions" tab of the application registration, and then click the "Grant admin consent for Directory" button. After consent is granted, the status of the permissions will change accordingly.

"Image shows the Azure Portal window at the 'Configured Permissions' page with the delegated permissions granted"

If the administrator is in a different tenant, an admin consent URL can be generated using the OAuth component. This URL allows the administrator to go through the authorization steps and grant consent for the requested scopes. The OAuth component supports this process with the ogtAdminConsent grant type. A redirect URI must be configured, typically a localhost URI, and only the client ID needs to be set.

There are two ways to handle the admin consent flow:

  1. Direct: If the administrator is on the same machine as the application, call the GetAuthorization method with the ocpApplication client profile. The component launches a browser and starts an embedded web server so that the administrator can grant consent and the component can parse the response. On success, the response contains the tenant ID; on failure, it contains an error code and description.
  2. Indirect: If the administrator cannot access the machine, the GetAuthorizationURL method can be used to generate only the admin consent URL. The administrator can then use this URL on any machine to grant consent for the application. The redirected response is not captured in this case, but it does not contain any critical information.

Example: Direct

OAuth oauth = new OAuth();
oauth.ClientProfile = OAuthClientProfiles.ocpApplication;
oauth.GrantType = OAuthGrantTypes.ogtAdminConsent;
oauth.ClientId = "CLIENT_ID";
oauth.WebServerPort = 7777;
oauth.ReturnURL = "http://localhost:7777";
oauth.GetAuthorization();
// Check the result of the admin consent request.
string error = oauth.Config("Microsoft365AdminConsentError");
string errorDesc = oauth.Config("Microsoft365AdminConsentErrorDesc");
string tenantID = oauth.Config("Microsoft365AdminConsentTenant");

Example: Indirect

OAuth oauth = new OAuth();
oauth.ClientProfile = OAuthClientProfiles.ocpApplication;
oauth.GrantType = OAuthGrantTypes.ogtAdminConsent;
oauth.ClientId = "CLIENT_ID";
oauth.WebServerPort = 7777;
oauth.ReturnURL = "http://localhost:7777";
// Provide this URL to the administrator.
string authURL = oauth.GetAuthorizationURL();

Office 365 Exchange

For applications using legacy mail protocols (IMAP, POP3, or SMTP), additional steps are required to link the mailboxes to the application. Once the administrator has granted consent to the application's API permissions, the application is added to the "Enterprise applications" node in the tenant. However, the application must also be registered with Office 365 Exchange.

An Azure Portal administrator needs to perform the following steps:

  1. Navigate to the "Enterprise applications" node in the tenant of the mailboxes the application needs to access.
  2. Select the application, and note the Application ID and Object ID shown in this node (not in the "App registrations" node).

"Image shows the Azure Portal with the Demo Application selected and the 'Overview' tab selected. Underneath the 'Properties' tab the 'Application ID' and the 'Object ID' are highlighted."

Next, the Exchange administrator must use PowerShell to connect to Exchange Online, register a service principal for the application, and grant the service principal permissions to each mailbox the application will access. The Add-MailboxPermission cmdlet must be run for each mailbox, including shared mailboxes. The service principal ID passed to Add-MailboxPermission can be retrieved with the Get-ServicePrincipal cmdlet after the service principal is created.

Install-Module -Name ExchangeOnlineManagement
Import-Module ExchangeOnlineManagement
Connect-ExchangeOnline -Organization TENANT_ID
New-ServicePrincipal -AppId APPLICATION_ID -ObjectId OBJECT_ID
Get-ServicePrincipal | Format-List
Add-MailboxPermission -Identity EMAIL -User SERVICE_PRINCIPAL_ID -AccessRights FullAccess

After these steps are complete, Office 365 Exchange recognizes the application, and the client credential grant flow can be used to access the mailboxes.

Component Setup

Below are examples of using the client credential grant type, depending on the API and component being used. The first example uses the built-in OAuth support of the components in Cloud Mail, Cloud Storage, and Cloud Keys. The second example uses the OAuth component to obtain an access token, which is then supplied to the IMAP component for authentication. The third example shows how to authenticate with a certificate instead of a client secret.

Example: Microsoft Graph

When using application permissions with Microsoft Graph, there is no signed-in user, so the Office365 component's UserId property must be set to the ID or user principal name (typically the email address) of the mailbox to access.

Office365 office365 = new Office365();
office365.OAuth.ClientProfile = CloudOAuthClientProfiles.cocpApplication;
office365.OAuth.GrantType = OAuthSettingsGrantTypes.cogtClientCredentials;
office365.OAuth.ClientId = "CLIENT_ID";
office365.OAuth.ClientSecret = "CLIENT_SECRET";
office365.OAuth.ServerTokenURL = "https://login.microsoftonline.com/TENANT_ID/oauth2/v2.0/token";
office365.OAuth.AuthorizationScope = "https://graph.microsoft.com/.default";
office365.Authorize();
// The mailbox to send from.
office365.UserId = "sender@example.com";
office365.MessageSubject = "I saw a Tree";
office365.MessageBodyContentType = "text";
office365.MessageBodyContent = "It was in my back yard initially. It was still there when I last checked.";
office365.MessageTo = "recipient@example.com";
office365.SendMail(true);

For more information about the built-in OAuth support, including the equivalent settings for the OneDrive and SharePoint components in Cloud Storage, please see our OAuth Authorization in /n software Cloud Products article.

Example: Legacy Mail API

Note that the scope for the legacy mail protocols is different than the scope used with Microsoft Graph.

OAuth oauth = new OAuth();
oauth.ClientProfile = OAuthClientProfiles.ocpApplication;
oauth.GrantType = OAuthGrantTypes.ogtClientCredentials;
oauth.ClientId = "CLIENT_ID";
oauth.ClientSecret = "CLIENT_SECRET";
oauth.ServerTokenURL = "https://login.microsoftonline.com/TENANT_ID/oauth2/v2.0/token";
oauth.AuthorizationScope = "https://outlook.office365.com/.default";
string authorization = oauth.GetAuthorization();
// Use the access token to authenticate to the IMAP server.
IMAP imap = new IMAP();
imap.AuthMechanism = IMAPAuthMechanisms.amXOAUTH2;
imap.Config("AuthorizationIdentity=" + authorization);
imap.MailServer = "outlook.office365.com";
imap.User = "user@example.com";
imap.SSLStartMode = IMAPSSLStartModes.sslImplicit;
imap.MailPort = 993;
imap.Connect();

Example: Certificate Credentials

To authenticate with a certificate instead of a client secret, set the OAuth component's ClientProfile property to ocpJWT. The component then signs a JSON Web Token (JWT) with the certificate's private key and sends it to the token server as a client assertion. The JWTServiceProvider configuration setting must be set to 1 (Microsoft), and the client ID is used as both the issuer and the subject of the JWT.

OAuth oauth = new OAuth();
oauth.ClientProfile = OAuthClientProfiles.ocpJWT;
oauth.ClientId = "CLIENT_ID";
oauth.ServerTokenURL = "https://login.microsoftonline.com/TENANT_ID/oauth2/v2.0/token";
oauth.AuthorizationScope = "https://graph.microsoft.com/.default";
oauth.Config("JWTServiceProvider=1");
oauth.Config("JWTIssuer=CLIENT_ID");
oauth.Config("JWTSubject=CLIENT_ID");
oauth.Config("JWTAudience=https://login.microsoftonline.com/TENANT_ID/oauth2/v2.0/token");
// Load the certificate (with its private key) from a PFX file.
oauth.Config("JWTCertStoreType=2");
oauth.Config("JWTCertStore=C:\\certs\\app.pfx");
oauth.Config("JWTCertStorePassword=PFX_PASSWORD");
oauth.Config("JWTCertSubject=*");
oauth.Config("JWTSignatureAlgorithm=RSA-SHA256");
string authorization = oauth.GetAuthorization();

The resulting authorization string can be assigned to the Authorization property of a component, or used with the IMAP component as shown in the previous example (with the appropriate scope).

Summary

When evaluating the client credential grant type, carefully assess the security and accessibility requirements of the application. Although this grant type minimizes user interaction, it places increased responsibility on the administrators of the cloud environment. Microsoft advises using other grant types whenever feasible, since delegated permissions offer additional safeguards if the application is compromised. For more information about the differences between delegated and application permissions, please see the Application vs. Delegated Permissions section of the setup article.

We appreciate your feedback. If you have any questions, comments, or suggestions about this article please contact our support team at support@nsoftware.com.