Understanding Azure AD Authentication: Interactive vs. Non-Interactive Token Acquisition

vijay balebail
5 min readJan 18, 2024

--

In the realm of Azure Active Directory (Azure AD), obtaining authentication tokens is a pivotal aspect of securing access to resources. Two primary methods — Interactive and Non-Interactive — facilitate this process. In this blog post, we delve into the distinctions between these methods and shed light on when Non-Interactive methods prove particularly advantageous.

Interactive Token Acquisition: A User-Centric Approach

The Interactive method involves user interaction, typically in the form of a login prompt. When an application initiates the authentication process, users are redirected to a new browser tab and prompted to provide their credentials interactively. This scenario is common in applications with a user interface, such as web applications or those executed on a device where user interaction is feasible.

Pros:

  1. User-Friendly: Allows for a seamless user experience and a browser redirection to login into Azure AD.
  2. Multi-Factor Authentication (MFA): Supports MFA for enhanced security. -> Leverages Azure AD MFA for user login

Cons:

  1. User Presence Required: Implies that user presence is necessary for token acquisition.
  2. Automated Scenarios: Can only run when a browser can run on that server and not possible to integrate into any automated process.

Non-Interactive Token Acquisition: Automation and Service Principals

In contrast, Non-Interactive methods called “Client Credential flow” are designed for scenarios where user interaction is impractical or unnecessary. This method is often employed in automated processes, background tasks, or services that don’t have a user interface. Service principals, representing applications rather than users, play a central role in Non-Interactive token acquisition.

Pros:

  1. Automation-Friendly: Suited for scenarios where user interaction is not possible or desired.
  2. Background Tasks: Ideal for tasks running in the background, devoid of user intervention.
  3. The server need not have a browser as connect to applications

Cons:

  1. Multi-Factor Authentication Challenges: Might face challenges when dealing with MFA due to the absence of a user.

When Non-Interactive is Useful: Scenarios and Considerations

  1. Automation and Scripting: Non-Interactive methods are indispensable when scripting or automating tasks that require access to Azure AD-secured resources. This is prevalent in scenarios such as scheduled jobs or data synchronization processes.
  2. Service-to-Service Communication: Applications communicating with each other, especially in a microservices architecture, benefit from Non-Interactive methods. Service principals authenticate the applications without user involvement.
  3. Backend Services: Backend services, devoid of user interfaces, often rely on Non-Interactive methods for acquiring tokens securely.
  4. Resource Access without User Presence: In scenarios where resource access is required without the need for direct user interaction, such as background data processing or API access.

Microsoft Authentication Library (MSAL)

Microsoft provides us with libraries to interact with Azure AD and get the token. Below are some sample program that connect to Azure using MSAL to get tokens. These tokens, once obtained, can be securely stored in a directory and configure Oracle Database connection strings. This opens up connection to Oracle databases, whether residing in the cloud or on-premises, with enhanced authentication and security. This demonstration assumes you have the necessary prerequisites and have registered your application within Azure as described in Part 1.

Python Example:

Interactive Token Acquisition:

# pip install msal
from msal import PublicClientApplication
import sys

# You can hard-code the registered app's client ID and tenant ID here,
# or you can provide them as command-line arguments to this script.
client_id = 'f080bd12-c7d2-4acb-b8ab-5c8fa2cf5c40'
tenant_id = '5b743bc7-c1e2-4d46-b4b5-a32eddac0286'


# Enter the scope of DB registar scope url.
scopes = [ 'https://oracledevelopment.onmicrosoft.com/aa04583a-fa70-4bbd-8f62-ec4ec80213df/oracle_ADB_access' ]

# Check for too few or too many command-line arguments.
if (len(sys.argv) > 1) and (len(sys.argv) != 3):
print("Usage: get-tokens.py <client ID> <tenant ID>")
exit(1)

# If the registered app's client ID and tenant ID are provided as
# command-line variables, set them here.
if len(sys.argv) > 1:
client_id = sys.argv[1]
tenant_id = sys.argv[2]

app = PublicClientApplication(
client_id = client_id,
authority = "https://login.microsoftonline.com/" + tenant_id
)

acquire_tokens_result = app.acquire_token_interactive(
scopes = scopes
)

if 'error' in acquire_tokens_result:
print("Error: " + acquire_tokens_result['error'])
print("Description: " + acquire_tokens_result['error_description'])
else:
print("Access token:\n")
print(acquire_tokens_result['access_token'])
print("\nRefresh token:\n")
print(acquire_tokens_result['refresh_token'])

Non-Interactive Token Acquisition:

from msal import ConfidentialClientApplication
import sys


# You can hard-code the registered app's client ID and tenant ID here,
# or you can provide them as command-line arguments to this script.
client_id = 'f080bd12-c7d2-4acb-b8ab-5c8fa2cf5c40'
tenant_id = '5b743bc7-c1e2-4d46-b4b5-a32eddac0286'

# Enter the scope of DB registar scope url.
scopes = [ 'https://oracledevelopment.onmicrosoft.com/aa04583a-fa70-4bbd-8f62-ec4ec80213df/.default' ]


#in non-interative, you provide the secret of the client app in Azure AD.
client_secret = 'fJ-8Q~hQmDSfhCXsHYqX0WR~CdLoeL5pWiCKXcKe'

# User ConfidentialClientApplication for non-interactive calls.
app = ConfidentialClientApplication(
client_id,
authority=f"https://login.microsoftonline.com/{tenant_id}",
client_credential=client_secret
)

result = app.acquire_token_for_client(scopes=scopes)

if 'access_token' in result:
print("Access token:\n")
print(result['access_token'])
print("\nRefresh token:\n")
print(result.get('refresh_token', 'Refresh token not provided in client credentials flow'))
else:
print("Failed to acquire token:", result.get('error_description') or result.get('error'))

Java Example:

Non-Interactive Token Acquisition:

import com.microsoft.aad.msal4j.*;
import com.microsoft.aad.msal4j.*;

import java.util.Collections;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;

public class GetToken {

public static void main(String[] args) {
// Your Azure AD app's information
String clientId = "f080bd12-c7d2-4acb-b8ab-5c8fa2cf5c40";
String tenantId = "5b743bc7-c1e2-4d46-b4b5-a32eddac0286";
String clientSecret = "fJ-8Q~hQmDSfhCXsHYqX0WR~CdLoeL5pWiCKXcKe";
String authority = "https://login.microsoftonline.com/" + tenantId;

// Define the scope for which you want to acquire a token
String scope = "https://oracledevelopment.onmicrosoft.com/aa04583a-fa70-4bbd-8f62-ec4ec80213df/.default";

// Build the confidential client application

ConfidentialClientApplication app;
try {
app = ConfidentialClientApplication.builder(clientId, ClientCredentialFactory.createFromSecret(clientSecret))
.authority(authority)
.build();
} catch (Exception e) {
System.out.println("Invalid authority URL");
return;
}


// Acquire a token using the client credentials flow
acquireToken(app, scope);
}

private static void acquireToken(ConfidentialClientApplication app, String scope) {
Set<String> scopes = Collections.singleton(scope);
ClientCredentialParameters parameters = ClientCredentialParameters.builder(scopes).build();

CompletableFuture<IAuthenticationResult> future = app.acquireToken(parameters);
try {
IAuthenticationResult result = future.get();
System.out.println("Access token:\n");
//System.out.println(result.accessToken());
String accessToken = result.accessToken();
if (accessToken != null && !accessToken.isEmpty()) {
System.out.println("Access token:\n");
System.out.println(accessToken);
} else {
System.out.println("Access token is null or empty.");
}
System.out.println("\nRefresh token:\n");
// System.out.println(result.refreshToken() != null ? result.refreshToken() : "Refresh token not provided in client credentials flow");
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
}
}

--

--