Power Platform OAuth Profiles and Connector Security
This chapter provides an exhaustive, production-grade guide to configuring, securing, deploying, and auditing OAuth connection profiles and custom connectors within the Microsoft Power Platform. It covers advanced security architectures, including workload identity federation (managed identities), On-Behalf-Of (OBO) pass-through authentication, and service principal client credentials, alongside enterprise governance and Application Lifecycle Management (ALM) automation.
1. Conceptual Foundation
To architect secure integrations in the Microsoft Power Platform, you must understand the security boundaries, identity delegation models, and data schemas that govern how cloud flows, canvas apps, and model-driven apps communicate with external APIs.
The Power Platform Connector Architecture
At its core, a connector is a wrapper around a RESTful API (described via an OpenAPI 2.0/Swagger definition) that allows the Power Platform runtime to communicate with an external service. The architecture is split into three distinct layers:
+-----------------------------------------------------------------+
| Power Platform Runtime |
| [Canvas App / Cloud Flow / Copilot Studio / Model-Driven App] |
+-----------------------------------------------------------------+
|
v
+-----------------------------------------------------------------+
| Connection Reference |
| - Solution-aware metadata pointer |
| - Binds logical flow action to physical connection |
+-----------------------------------------------------------------+
|
v
+-----------------------------------------------------------------+
| Connection |
| - Environment-specific instance containing credentials |
| - Managed by Azure API Connections service principal |
+-----------------------------------------------------------------+
|
v
+-----------------------------------------------------------------+
| Custom Connector |
| - OpenAPI 2.0 definition & security scheme |
| - Policy templates (routing, header injection, host override) |
+-----------------------------------------------------------------+
|
v
+-----------------------------------------------------------------+
| Target API |
| - Protected by Microsoft Entra ID, API Key, or Basic Auth |
+-----------------------------------------------------------------+
- Connection Reference (
connectionreference): A solution-aware metadata component that acts as a pointer. It binds a logical action in a flow or app to a physical connection. By referencing a connection reference instead of a hardcoded connection, solutions remain environment-agnostic. - Connection (
connection): An environment-specific instance of a connector containing the actual authentication credentials (e.g., OAuth refresh tokens, API keys, or basic auth credentials). Connections are stored securely in an encrypted infrastructure managed by the Azure API Connections service principal (fe053c5f-3692-4f14-aef2-ee34fc081cae). - Connector (
connector): The definition of the API's schema, endpoints, and security requirements. Custom connectors are stored as metadata within the Dataverse environment database.
OAuth 2.0 Identity Delegation Models
When integrating with APIs secured by Microsoft Entra ID (formerly Azure Active Directory), the Power Platform supports three primary identity delegation models:
1. User-Delegated Authentication (Authorization Code Flow with PKCE)
This is the default behavior for standard OAuth connectors.
- Mechanism: The maker or end-user interactively signs in via an Entra ID consent prompt. The Power Platform runtime receives an Authorization Code, exchanges it for an Access Token and a Refresh Token, and stores them securely in the Azure API Connections service.
- Security Context: The API calls execute under the security context of the individual user who established the connection.
- Lifecycle: If the user leaves the organization or their password is reset, the refresh token is invalidated, breaking the connection and halting any dependent automated processes.
2. On-Behalf-Of (OBO) Authentication (Token Exchange)
OBO is used to achieve seamless, pass-through user authentication without interactive prompts during runtime execution (commonly used in Copilot Studio and custom canvas app integrations).
- Mechanism: The client application (e.g., Power Apps) obtains an Entra ID token for the user. The Power Platform runtime intercepts this token and calls the Entra ID token endpoint using the
urn:ietf:params:oauth:grant-type:jwt-bearergrant type (OBO flow). It exchanges the user's initial token for a downstream access token specifically scoped for the target API. - Security Context: The backend API executes in the security context of the calling user, enabling row-level security (RLS) and granular audit logging directly in the target system.
- Prerequisite: The Azure API Connections service principal (
fe053c5f-3692-4f14-aef2-ee34fc081cae) must be pre-authorized in the target API's app registration.
3. Service Principal Authentication (Client Credentials & Workload Identity Federation)
This model is designed for non-interactive, system-to-system integrations, ensuring that mission-critical background processes are decoupled from individual user lifecycles.
- Client Secrets: The custom connector is configured with the Client ID and Client Secret of an Entra ID app registration. The runtime requests tokens using the
client_credentialsgrant type. Drawback: Requires secret rotation, management, and presents a risk of secret exposure in source control or solution files. - Managed Identity (Workload Identity Federation): The custom connector utilizes a system-assigned managed identity generated by the Power Platform runtime. This identity establishes a trust relationship with the target Entra ID app registration using Federated Credentials (Workload Identity Federation). The runtime requests tokens without passwords or secrets, using a signed client assertion (JWT) generated by the Power Platform's internal token service.
Dataverse Security and Connection Data Model
Custom connectors and connection references are represented as tables within the Dataverse database. Understanding this schema is critical for programmatic administration, auditing, and ALM:
- Connector Table (
connector):connectorid: Unique identifier (GUID).name: The logical name of the connector (e.g.,shared_customapi_5f06...).displayname: The user-friendly name.openapidefinition: The raw JSON string containing the OpenAPI 2.0 definition.connectortype: Set to1for custom connectors.capabilities: Array indicating supported features (e.g.,gateway,cloud).
- Connection Reference Table (
connectionreference):connectionreferenceid: Unique identifier (GUID).connectionreferencename: The logical schema name (prefixed by publisher prefix, e.g.,new_sharedcustomapi_5f06_7a8b).connectorid: Foreign key pointing to theconnectortable.connectionid: The physical connection GUID (populated dynamically per environment).
Data Loss Prevention (DLP) and Custom Connector Security Policies
Data Loss Prevention (DLP) policies restrict which connectors can share data. For custom connectors, DLP policies operate at two levels:
- Environment-Level DLP: Administrators can classify individual custom connectors by name into Business, Non-Business, or Blocked groups.
- Tenant-Level DLP (URL Pattern Matching): Because custom connectors are environment-specific, they do not appear in tenant-wide connector lists. Instead, tenant admins define an ordered list of Allow/Deny URL patterns (e.g.,
https://api.contoso.com/*). The Power Platform evaluates the target host of the custom connector against these patterns at runtime. If a custom connector's host matches a blocked pattern, execution is halted with a DLP violation error.
2. Architecture & Decision Matrix
When designing integrations, choosing the correct execution context and authentication mechanism is critical for security, performance, and licensing compliance.
Comparison of Integration Approaches
| Feature / Dimension | Custom Connector (User-Delegated OAuth) | Custom Connector (Service Principal / Managed Identity) | Custom Connector (On-Behalf-Of Flow) | Direct HTTP Action (Entra ID OAuth) | Dataverse Custom API / Plugin (Managed Identity) |
|---|---|---|---|---|---|
| Complexity | Low | Medium | High | Low | High |
| Scalability | High | High | High | Medium (No reuse) | High |
| Execution Context | Interactive User | System / Application | End-User (Pass-through) | Flow Owner | System / Managed Identity |
| Offline Support | No | Yes (Background flows) | No | Yes (Background flows) | Yes (Offline sync plugins) |
| Licensing | Premium Power Apps/Automate | Premium + Process/Per-Flow License | Premium Power Apps/Automate | Premium Power Apps/Automate | Dataverse / Dynamics 365 Base License |
| PL-400 Relevance | High (Core concept) | High (Advanced security) | High (Copilot/Sales integration) | Medium | High (Pro-dev extensibility) |
Architectural Decision Flowchart
[Start: Design Integration]
|
v
Is the target API internal or
external to Power Platform?
/ \
[Internal] [External]
| |
v v
Use Dataverse Web API Does the API require
or Custom Plugins. user-specific security?
/ \
[Yes] [No]
| |
v v
Is interactive login Use Service Principal
acceptable at runtime? or Managed Identity
/ \ for system-to-system
[Yes] [No] integration.
| |
v v
Use Standard OAuth Use On-Behalf-Of
(Auth Code Flow). (OBO) Token Exchange.
3. Step-by-Step Implementation Guide
This section details the configuration of a custom connector that authenticates to a secure backend Web API using Service Principal Workload Identity Federation (Managed Identity). This pattern eliminates the need to manage, store, or rotate client secrets.
Step 1: Register the Backend Web API in Microsoft Entra ID
First, register the API that the custom connector will call, and expose a delegated scope.
- Navigate to the Microsoft Entra admin center as an Application Developer or Global Administrator.
- Go to Identity > Applications > App registrations and select New registration.
- Configure the following:
- Name:
Contoso-Backend-API - Supported account types: Accounts in this organizational directory only (Single tenant)
- Redirect URI: Leave blank.
- Name:
- Select Register. Note the Application (client) ID and Directory (tenant) ID.
- Navigate to Expose an API:
- Select Set next to Application ID URI (accept the default
api://<client-id>). - Select Add a scope and configure:
- Scope name:
Data.Read - Who can consent: Admins and users
- Admin consent display name:
Read Contoso Data - Admin consent description:
Allows the custom connector to read backend data. - State: Enabled
- Scope name:
- Select Add scope.
- Select Set next to Application ID URI (accept the default
Step 2: Register the Custom Connector Client App in Entra ID
Next, register the application identity that the Power Platform connector runtime will assume.
- In App registrations, select New registration.
- Configure the following:
- Name:
Contoso-PowerPlatform-Connector-Client - Supported account types: Accounts in this organizational directory only (Single tenant)
- Redirect URI: Select Web and enter the global Power Platform redirect URL:
https://global.consent.azure-apim.net/redirect
- Name:
- Select Register. Note the Application (client) ID.
- Navigate to API permissions:
- Select Add a permission.
- Select My APIs and choose
Contoso-Backend-API. - Select Delegated permissions, check
Data.Read, and select Add permissions. - Select Grant admin consent for <Your Tenant Name> to bypass interactive consent.
Step 3: Create and Configure the Custom Connector
Now, define the custom connector in the Power Apps Maker Portal.
- Sign in to the Power Apps Maker Portal and select your development environment.
- Expand More on the left navigation pane, select Discover all, and click Custom connectors.
- Select New custom connector > Import an OpenAPI file (or Create from blank).
- On the 1. General tab:
- Host: Enter your API host (e.g.,
api.contoso.com). - Base URL:
/
- Host: Enter your API host (e.g.,
- On the 2. Security tab:
- Authentication type: Select OAuth 2.0.
- Identity Provider: Select Azure Active Directory.
- Client ID: Enter the Application ID of
Contoso-PowerPlatform-Connector-Client(from Step 2). - Client secret: Enter a temporary placeholder string (e.g.,
TEMP_SECRET). Note: This will be overridden by the Managed Identity configuration. - Login URL:
https://login.microsoftonline.com - Tenant ID: Enter your actual Directory (tenant) ID GUID. Do not use
common. - Resource URL: Enter the Application ID URI of the backend API (from Step 1, e.g.,
api://<Contoso-Backend-API-Client-ID>). - Enable Service Principal Support: Check this box.
- Service Principal Authentication Type: Select Use Managed Identity.
- Select Create connector in the top-right corner.
Step 4: Configure Workload Identity Federation (Federated Credentials)
Once saved, the Power Platform runtime generates a unique Managed Identity context for this connector. You must link this context to your Entra ID client app registration.
- After saving, scroll to the bottom of the Security tab of your custom connector.
- Locate the Managed Identity section and copy the generated Issuer and Subject strings.
- Example Issuer:
https://prod-sts.powerplatform.com/ - Example Subject:
connector:custom:environment:<env-guid>:connector:<connector-name>
- Example Issuer:
- Return to the Microsoft Entra admin center.
- Open the app registration for
Contoso-PowerPlatform-Connector-Client(from Step 2). - Navigate to Certificates & secrets and select the Federated credentials tab.
- Select Add credential and configure:
- Federated credential scenario: Select Other issuer.
- Issuer: Paste the copied Issuer string.
- Subject identifier: Paste the copied Subject string.
- Name:
PowerPlatform-Connector-Federation - Description:
Trust relationship for Power Platform custom connector managed identity.
- Select Add.
+-----------------------------------------------------------------------------------+
| Trust Relationship |
| |
| +----------------------------------+ +----------------------------+ |
| | Custom Connector | | Entra ID Client | |
| | (Power Platform) | | App Registration | |
| | | | | |
| | Managed Identity Context | | Federated Credentials | |
| | - Issuer: sts.powerplatform.com | ----------> | - Trust Issuer | |
| | - Subject: env:GUID:conn:NAME | | - Trust Subject | |
| +----------------------------------+ +----------------------------+ |
+-----------------------------------------------------------------------------------+
Step 5: Create a Connection and Test the Connector
- On the custom connector wizard, navigate to the 5. Test tab.
- Select New connection.
- Because Managed Identity is enabled, the connection is provisioned silently without prompting for a password or interactive login.
- Select an operation, enter any required parameters, and select Test operation. Verify that the response returns an HTTP
200 OK.
4. Complete Code Reference
OpenAPI 2.0 (Swagger) Definition with OAuth 2.0 Security
This is the complete, production-grade OpenAPI 2.0 JSON definition for the custom connector. It includes the securityDefinitions block mapping to Microsoft Entra ID and the custom Power Platform extensions for Service Principal support.
{
"swagger": "2.0",
"info": {
"title": "Contoso Enterprise API",
"description": "Secure integration with Contoso backend systems.",
"version": "1.0.0"
},
"host": "api.contoso.com",
"basePath": "/v1",
"schemes": [
"https"
],
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"paths": {
"/data/records": {
"get": {
"responses": {
"200": {
"description": "Successful retrieval of records",
"schema": {
"type": "array",
"items": {
"$ref": "#/definitions/RecordItem"
}
}
},
"401": {
"description": "Unauthorized - Invalid or expired token"
},
"403": {
"description": "Forbidden - Insufficient scopes"
}
},
"summary": "Get Records",
"description": "Retrieves secure records from the Contoso backend.",
"operationId": "GetRecords"
}
}
},
"definitions": {
"RecordItem": {
"type": "object",
"properties": {
"id": {
"type": "string",
"format": "uuid"
},
"name": {
"type": "string"
},
"value": {
"type": "number"
}
},
"required": [
"id",
"name"
]
}
},
"securityDefinitions": {
"AAD": {
"type": "oauth2",
"flow": "accessCode",
"authorizationUrl": "https://login.microsoftonline.com/common/oauth2/authorize",
"tokenUrl": "https://login.microsoftonline.com/common/oauth2/token",
"scopes": {
"api://contoso-backend-api-id/Data.Read": "Read Contoso Data"
},
"x-ms-connector-security": {
"preferredAuthType": "ManagedIdentity",
"supportedAuthTypes": [
"ManagedIdentity",
"Secret"
],
"managedIdentitySettings": {
"tenantId": "00000000-0000-0000-0000-000000000000",
"resource": "api://contoso-backend-api-id"
}
}
}
},
"security": [
{
"AAD": []
}
]
}
Backend ASP.NET Core Web API Controller
This C# controller secures the backend API, validates the incoming JWT token issued by Microsoft Entra ID, and extracts the calling identity.
// Filename: RecordsController.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
namespace Contoso.SecureApi.Controllers
{
[ApiController]
[Route("v1/data/[controller]")]
[Authorize] // Enforces JWT Bearer authentication
public class RecordsController : ControllerBase
{
private readonly ILogger<RecordsController> _logger;
public RecordsController(ILogger<RecordsController> logger)
{
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
}
/// <summary>
/// Retrieves secure records. Validates that the caller has the required scope.
/// </summary>
[HttpGet("records")]
[ProducesResponseType(typeof(IEnumerable<RecordItem>), 200)]
[ProducesResponseType(401)]
[ProducesResponseType(403)]
public IActionResult GetRecords()
{
// Extract and validate the scope claim (scp) from the JWT
var scopeClaim = User.FindFirst("http://schemas.microsoft.com/identity/claims/scope") ?? User.FindFirst("scp");
if (scopeClaim == null || !scopeClaim.Value.Split(' ').Contains("Data.Read"))
{
_logger.LogWarning("Forbidden: Request lacks the required 'Data.Read' scope.");
return Forbid("Insufficient permissions. Required scope: Data.Read");
}
// Extract the caller's identity (App ID for Service Principal, Object ID for User)
string callerIdentity = ExtractCallerIdentity(User);
_logger.LogInformation("Successfully authenticated request from: {CallerIdentity}", callerIdentity);
var records = new List<RecordItem>
{
new RecordItem { Id = Guid.NewGuid(), Name = "Secure Payload Alpha", Value = 99.45 },
new RecordItem { Id = Guid.NewGuid(), Name = "Secure Payload Beta", Value = 120.10 }
};
return Ok(records);
}
private string ExtractCallerIdentity(ClaimsPrincipal principal)
{
// Check for Application ID (appid) claim if called via Service Principal
var appIdClaim = principal.FindFirst("appid") ?? principal.FindFirst("azp");
if (appIdClaim != null)
{
return $"ServicePrincipal-AppID:{appIdClaim.Value}";
}
// Fallback to User Object ID (oid)
var oidClaim = principal.FindFirst("http://schemas.microsoft.com/identity/claims/objectidentifier") ?? principal.FindFirst("oid");
if (oidClaim != null)
{
return $"User-OID:{oidClaim.Value}";
}
return "Unknown-Identity";
}
}
public class RecordItem
{
public Guid Id { get; set; }
public string Name { get; set; }
public double Value { get; set; }
}
}
PowerShell Connection Auditing and DLP Management Script
This script uses the Microsoft.PowerApps.Administration.PowerShell module to audit all connections in a tenant, identify custom connectors that are not using per-connector redirect URIs, and configure tenant-level DLP URL patterns.
# Filename: AuditAndConfigureDlp.ps1
<#
.SYNOPSIS
Audits Power Platform connections and configures tenant-level DLP URL patterns.
.DESCRIPTION
This script performs three tasks:
1. Audits all connections in a target environment.
2. Identifies custom connectors using legacy global redirect URIs.
3. Configures tenant-level DLP URL patterns to restrict custom connector hosts.
#>
[CmdletBinding()]
param (
[Parameter(Mandatory = $true)]
[string]$TenantId,
[Parameter(Mandatory = $true)]
[string]$EnvironmentName,
[Parameter(Mandatory = $true)]
[string]$DlpPolicyName
)
# Ensure required modules are installed and imported
$requiredModules = @("Microsoft.PowerApps.Administration.PowerShell")
foreach ($module in $requiredModules) {
if (-not (Get-Module -ListAvailable -Name $module)) {
Write-Host "Installing required module: $module" -ForegroundColor Yellow
Install-Module -Name $module -Force -AllowClobber -Scope CurrentUser
}
Import-Module -Name $module
}
# Authenticate to Power Platform Admin Center
Write-Host "Authenticating to Power Platform..." -ForegroundColor Cyan
Add-PowerAppsAccount -Endpoint "prod"
# Task 1: Audit Connections in the Target Environment
Write-Host "Auditing connections in environment: $EnvironmentName" -ForegroundColor Cyan
$connections = Get-AdminPowerAppConnection -EnvironmentName $EnvironmentName
$auditReport = foreach ($conn in $connections) {
[PSCustomObject]@{
ConnectionId = $conn.ConnectionName
DisplayName = $conn.Internal.properties.displayName
ConnectorId = $conn.Internal.properties.apiId
CreatedBy = $conn.Internal.properties.createdBy.email
CreatedTime = $conn.Internal.properties.createdTime
Statuses = $conn.Internal.properties.statuses.status | Out-String
}
}
$auditReport | Format-Table -AutoSize
# Task 2: Identify Custom Connectors using Legacy Global Redirect URIs
Write-Host "Scanning for custom connectors with legacy redirect URIs..." -ForegroundColor Cyan
$customConnectors = Get-AdminPowerAppConnector -EnvironmentName $EnvironmentName
$legacyConnectors = foreach ($connector in $customConnectors) {
$redirectMode = $connector.Internal.properties.redirectMode
if ($redirectMode -ne "GlobalPerConnector") {
[PSCustomObject]@{
ConnectorId = $connector.ConnectorName
DisplayName = $connector.DisplayName
CreatedBy = $connector.Internal.properties.createdBy.email
RedirectMode = $redirectMode
ActionRequired = "MIGRATION_REQUIRED_TO_PER_CONNECTOR_REDIRECT"
}
}
}
if ($legacyConnectors) {
Write-Host "WARNING: Found custom connectors requiring migration!" -ForegroundColor Red
$legacyConnectors | Format-Table -AutoSize
} else {
Write-Host "All custom connectors are using secure per-connector redirect URIs." -ForegroundColor Green
}
# Task 3: Configure Tenant-Level DLP URL Patterns
Write-Host "Configuring DLP URL patterns for policy: $DlpPolicyName" -ForegroundColor Cyan
# Define the URL pattern rules
# Rule 1: Classify Contoso API as Business (Confidential)
# Rule 2: Classify Bing API as Non-Business (General)
# Rule 3: Block all other custom connector hosts (*)
$UrlPatterns = @{
rules = @(
@{
order = 1
customConnectorRuleClassification = "Confidential" # Business Group
pattern = "https://api.contoso.com/*"
},
@{
order = 2
customConnectorRuleClassification = "General" # Non-Business Group
pattern = "https://www.bing.com/*"
},
@{
order = 3
customConnectorRuleClassification = "Blocked" # Blocked Group
pattern = "*"
}
)
}
# Convert the hashtable to a JSON string
$jsonPatterns = $UrlPatterns | ConvertTo-Json -Depth 5
# Apply the URL patterns to the DLP Policy
try {
Write-Host "Applying URL patterns to DLP policy..." -ForegroundColor Yellow
New-PowerAppPolicyUrlPatterns -TenantId $TenantId -PolicyName $DlpPolicyName -NewUrlPatterns $jsonPatterns
Write-Host "DLP URL patterns successfully applied." -ForegroundColor Green
}
catch {
Write-Error "Failed to apply DLP URL patterns: $_"
}
5. Configuration & Environment Setup
Infrastructure Deployment (Azure Bicep)
This Bicep template deploys the backend Azure App Service (Web API) and configures Microsoft Entra ID authentication.
// Filename: deploy-backend.bicep
targetScope = 'resourceGroup'
param location string = resourceGroup().location
param appServicePlanName string = 'asp-contoso-secure-prod'
param webAppName string = 'wapp-contoso-secure-api-prod'
param entraTenantId string
param entraClientId string // Client ID of Contoso-Backend-API app registration
resource appServicePlan 'Microsoft.Web/serverfarms@2022-03-01' = {
name: appServicePlanName
location: location
sku: {
name: 'S1'
tier: 'Standard'
}
kind: 'linux'
properties: {
reserved: true
}
}
resource webApp 'Microsoft.Web/sites@2022-03-01' = {
name: webAppName
location: location
kind: 'app'
properties: {
serverFarmId: appServicePlan.id
siteConfig: {
linuxFxVersion: 'DOTNETCORE|6.0'
appSettings: [
{
name: 'AzureAd:TenantId'
value: entraTenantId
}
{
name: 'AzureAd:ClientId'
value: entraClientId
}
{
name: 'AzureAd:Instance'
value: 'https://login.microsoftonline.com/'
}
]
}
}
}
// Configure App Service Authentication (Easy Auth) to enforce Entra ID validation
resource authConfig 'Microsoft.Web/sites/config@2022-03-01' = {
parent: webApp
name: 'authsettingsV2'
properties: {
globalValidation: {
requireAuthentication: true
unauthenticatedClientAction: 'Return401'
}
identityProviders: {
azureActiveDirectory: {
enabled: true
registration: {
clientId: entraClientId
openIdIssuerInterval: '00:10:00'
clientIdSettingName: 'AzureAd:ClientId'
}
validation: {
allowedAudiences: [
'api://`${entraClientId}`'
]
}
}
}
platform: {
enabled: true
}
}
}
output webAppUrl string = 'https://`${webApp.properties.defaultHostName}`'
Custom Connector API Properties Configuration
When deploying custom connectors via solutions, the security metadata is stored in the apiProperties.json file. This file must be configured to support Managed Identity.
{
"properties": {
"connectionParameters": {
"token": {
"type": "oauthSetting",
"oAuthSettings": {
"identityProvider": "oauth2",
"clientId": "00000000-0000-0000-0000-000000000000",
"scopes": ["api://contoso-backend-api-id/Data.Read"],
"redirectMode": "GlobalPerConnector",
"customParameters": {
"loginUri": {
"value": "https://login.microsoftonline.com"
},
"tenantId": {
"value": "00000000-0000-0000-0000-000000000000"
},
"resourceUri": {
"value": "api://contoso-backend-api-id"
}
},
"properties": {
"IsServicePrincipalSupported": true,
"ServicePrincipalAuthType": "ManagedIdentity"
}
}
}
}
}
}
6. Security & Permission Matrix
To implement and run this architecture, you must grant specific permissions across Microsoft Entra ID, Azure Resources, and the Power Platform.
| Principal | Permission / Role | Scope | Reason |
|---|---|---|---|
| Power Platform Maker | Environment Maker | Power Platform Environment | Required to create, edit, and test custom connectors and connection references. |
| Power Platform Admin | Power Platform Administrator | Tenant / Environment | Required to configure DLP policies, URL patterns, and audit connection usage. |
| Entra ID Admin | Application Developer / Cloud Application Administrator | Microsoft Entra Tenant | Required to register applications, expose APIs, and configure Federated Credentials. |
| Connector Managed Identity | Federated Credential Trust | Entra ID Client App Registration | Establishes passwordless trust between the Power Platform runtime and Entra ID. |
| Azure API Connections SPN | Pre-authorized Client (fe053c5f-3692-4f14-aef2-ee34fc081cae) | Backend API App Registration | Allows the Power Platform runtime to request tokens on behalf of users (OBO flow). |
| Flow Execution Context | Process / Per-Flow License | Power Automate Flow | Required to run flows utilizing premium custom connectors under a Service Principal identity. |
7. Pipeline Execution Internals
When a cloud flow executes an action via a custom connector, the request passes through the Power Platform Connector Runtime. Understanding this pipeline is critical for debugging and performance tuning.
[Cloud Flow Action]
|
v
[Connection Reference Lookup]
|
v
[Connector Runtime Token Service]
- Checks cache for valid Access Token.
- If expired, uses Federated Credential (JWT assertion) to request new token from Entra ID.
|
v
[Token Injection]
- Injects Bearer token into HTTP Authorization header.
|
v
[DLP Policy Evaluation]
- Evaluates target host against tenant URL patterns.
|
v
[Outbound Sandbox Gateway]
- Routes request over HTTPS (Port 443).
- Enforces 110-second execution timeout.
|
v
[Target API Endpoint]
Token Lifecycle and Caching
- Token Request: When a flow action is triggered, the runtime looks up the associated connection.
- Cache Lookup: The runtime checks its internal, encrypted token cache for a valid Access Token.
- Token Refresh:
- If the token is expired or near expiration, the runtime initiates a refresh.
- For Managed Identity, the runtime generates a client assertion (a JWT signed by the Power Platform's private key). It sends this assertion to the Entra ID token endpoint (
/oauth2/v2.0/token) using theclient_credentialsgrant type and the federated credential trust. - Entra ID validates the assertion against the configured Federated Credential (Issuer and Subject) and returns a new Access Token.
- Token Injection: The runtime injects the retrieved token into the outbound HTTP request header:
Authorization: Bearer <token>.
Sandbox Mode and Callout Restrictions
All outbound calls from custom connectors are executed within a secure sandbox environment. The following restrictions apply:
- Protocol: Only secure protocols (
HTTPS) are permitted. PlainHTTPcalls are blocked. - Port: Outbound traffic is restricted to port
443. Custom ports are not supported. - Timeout: The maximum execution timeout for any outbound HTTP request is 110 seconds. If the backend API does not respond within this window, the runtime terminates the call and returns an HTTP
502 Bad Gatewayor504 Gateway Timeouterror. - Loop-Guard Limits: To prevent infinite loops (e.g., a flow triggering an API call that updates Dataverse, which triggers the same flow), the platform enforces a depth limit of 10 nested executions within a single transaction.
8. Error Handling & Retry Patterns
Robust integrations must handle transient network failures, rate limiting, and authentication expirations gracefully.
Common Failure Modes and Resolutions
| Error Code / Exception | Root Cause | Diagnostic Steps | Remediation |
|---|---|---|---|
AADSTS700213 | Federated credential mismatch. The Issuer or Subject string in Entra ID does not match the custom connector. | Compare the Issuer and Subject strings in the custom connector's Security tab with the Federated Credential in Entra ID. | Copy the exact strings from the custom connector and update the Federated Credential in the Entra ID app registration. |
AADSTS7000215 | Invalid client secret. The secret has expired or is incorrect. | Check the expiration date of the client secret in the Entra ID app registration. | Generate a new client secret in Entra ID and update the custom connector security settings. Note: Switch to Managed Identity to avoid this. |
401 Unauthorized | Invalid or expired token, or audience mismatch. | Decode the JWT token using jwt.ms and inspect the aud (audience) and exp (expiration) claims. | Ensure the Resource URL in the custom connector matches the Application ID URI of the backend API. |
403 Forbidden | Missing scopes or DLP policy block. | Check the scp claim in the decoded token. Check if the flow run history shows a DLP violation. | Add the required scope to the custom connector's security definition, or update the DLP policy to allow the custom connector host. |
429 Too Many Requests | Rate limiting / Throttling. | Inspect the response headers for Retry-After or rate limit indicators. | Implement exponential back-off retry policies in the calling flow or application. |
502 Bad Gateway | Backend API timeout or gateway failure. | Check the backend API logs to see if the request was received and how long it took to process. | Optimize backend API performance to respond within the 110-second sandbox timeout limit. |
Implementing Exponential Back-Off Retry Policy
In Power Automate, retry policies are configured directly on the action settings. However, when writing custom plugins or integrations that call the API, you must implement retry logic programmatically.
The following C# sample demonstrates how to implement an exponential back-off retry policy using the Polly NuGet package:
// Filename: SecureApiClient.cs
using System;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;
using Polly;
using Polly.Retry;
namespace Contoso.SecureApi.Client
{
public class SecureApiClient
{
private readonly HttpClient _httpClient;
private readonly AsyncRetryPolicy<HttpResponseMessage> _retryPolicy;
public SecureApiClient(HttpClient httpClient)
{
_httpClient = httpClient ?? throw new ArgumentNullException(nameof(httpClient));
// Define an exponential back-off policy for transient errors (5xx, 429)
_retryPolicy = Policy
.HandleResult<HttpResponseMessage>(r =>
r.StatusCode >= HttpStatusCode.InternalServerError ||
r.StatusCode == HttpStatusCode.TooManyRequests)
.WaitAndRetryAsync(
retryCount: 3,
sleepDurationProvider: retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)),
onRetry: (outcome, timespan, retryAttempt, context) =>
{
// Log retry attempt details
Console.WriteLine($"Transient error encountered: {outcome.Result.StatusCode}. Retrying attempt {retryAttempt} after {timespan.TotalSeconds} seconds.");
});
}
/// <summary>
/// Executes a secure GET request to the backend API with retry logic.
/// </summary>
public async Task<string> GetSecureDataAsync(string endpointUrl, string accessToken)
{
if (string.IsNullOrEmpty(endpointUrl)) throw new ArgumentNullException(nameof(endpointUrl));
if (string.IsNullOrEmpty(accessToken)) throw new ArgumentNullException(nameof(accessToken));
// Execute the call within the retry policy context
HttpResponseMessage response = await _retryPolicy.ExecuteAsync(async () =>
{
using (var request = new HttpRequestMessage(HttpMethod.Get, endpointUrl))
{
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
return await _httpClient.SendAsync(request);
}
});
// Enforce success status code after retries are exhausted
if (!response.IsSuccessStatusCode)
{
string errorContent = await response.Content.ReadAsStringAsync();
throw new HttpRequestException($"API call failed with status {response.StatusCode}. Details: {errorContent}");
}
return await response.Content.ReadAsStringAsync();
}
}
}
9. Performance Optimisation & Limits
To ensure enterprise-grade reliability, you must design integrations within the platform's performance boundaries.
Platform Limits
- Payload Size Limit: The maximum payload size for custom connector requests and responses is 100 MB.
- Execution Timeout: Outbound HTTP requests are strictly capped at 110 seconds.
- Throttling Limits: Custom connectors are subject to concurrency and request limits based on the license of the executing user or flow. By default, connections are throttled at 500 requests per connection per minute.
Optimization Strategies
- HTTP Compression: Ensure your backend API supports
gzipordeflatecompression. The custom connector runtime automatically includes theAccept-Encoding: gzip, deflateheader in outbound requests. - Pagination: Implement pagination for large datasets. Use the standard
nextLinkpattern in your API responses. The custom connector can be configured to automatically follow these links using thex-ms-pageableextension. - Batching: If your backend API supports batching (e.g., OData
$batch), design your custom connector to accept batch payloads to reduce the number of round-trips and avoid throttling. - Token Caching: Do not request a new token for every API call. The Power Platform runtime automatically caches tokens and handles refreshes. Ensure your backend API sets appropriate
Cache-Controlheaders if static data is returned.
10. ALM & Deployment Checklist
Moving custom connectors and connection references across environments (Development -> Test -> Production) requires strict adherence to ALM best practices.
Solution Packaging Rules
- Separate Solutions: Always package custom connectors in a separate solution from the connection references and flows that use them.
- Reason: During solution import, Dataverse registers the custom connector metadata first. If connection references or flows are in the same solution, they may attempt to bind to the connector before registration is complete, causing the import to fail.
- Managed Solutions: Always export solutions as Managed for downstream environments (Test, Production).
Connection Reference Mapping (Deployment Settings File)
To automate deployments via CI/CD pipelines, use a Deployment Settings File to map connection references to physical connections in the target environment.
Example deploymentSettings.json
{
"EnvironmentVariables": [
{
"SchemaName": "contoso_BackendApiUrl",
"Value": "https://api.contoso-prod.com"
}
],
"ConnectionReferences": [
{
"LogicalName": "contoso_shared_secure_api_reference",
"ConnectionId": "1a2b3c4d-5e6f-7a8b-9c0d-1e2f3a4b5c6d",
"ConnectorId": "/providers/Microsoft.PowerApps/apis/shared_contoso-20enterprise-20api"
}
]
}
Azure DevOps Pipeline YAML
This YAML snippet demonstrates how to automate the import of a managed solution containing custom connectors and connection references using the Power Platform Build Tools.
# Filename: deploy-solution-pipeline.yml
trigger:
- main
pool:
vmImage: 'windows-latest'
variables:
SolutionName: 'ContosoIntegrationSolution'
EnvironmentUrl: 'https://org-prod.crm.dynamics.com'
steps:
# Install Power Platform Build Tools
- task: microsoft-IsvExpTools.PowerPlatform-BuildTools.tool-installer.PowerPlatformToolInstaller@2
displayName: 'Power Platform Tool Installer'
# Import the Solution using the Deployment Settings File
- task: microsoft-IsvExpTools.PowerPlatform-BuildTools.import-solution.PowerPlatformImportSolution@2
displayName: 'Import Managed Solution to Production'
inputs:
authenticationType: 'PowerPlatformSPN'
PowerPlatformSPN: 'Production-Service-Connection' # Service Connection configured in Azure DevOps
SolutionInputFile: '$(Build.ArtifactStagingDirectory)/$(SolutionName)_managed.zip'
OverwriteUnmanagedCustomizations: true
PublishWorkflows: true
UseDeploymentSettingsFile: true
DeploymentSettingsFile: '$(Build.SourcesDirectory)/config/production/deploymentSettings.json'
AsyncOperation: true
MaxAsyncWaitTime: '60'
11. Common Pitfalls & Troubleshooting Guide
1. Using common Tenant ID with Managed Identity
- Symptom: Silent connection creation fails with an error indicating that the tenant could not be resolved.
- Root Cause: Managed Identity authentication requires a specific tenant GUID to generate the federated credential trust. Using the
commonendpoint is not supported. - Fix: Replace
commonwith your actual Entra ID Directory (tenant) ID GUID in the custom connector's security settings.
2. Legacy Global Redirect URI Enforcement
- Symptom: Existing custom connectors suddenly stop working for new connections, displaying redirect URI mismatch errors.
- Root Cause: Microsoft enforced per-connector redirect URIs. Legacy connectors using the global
https://global.consent.azure-apim.net/redirectwithout per-connector routing are blocked. - Fix: Edit the custom connector, navigate to the Security tab, check the Update to unique redirect URL box, save, and update the redirect URI in your Entra ID app registration with the newly generated unique URL.
3. Client Secret Rotation Failures
- Symptom: Flows fail with
401 UnauthorizedorInvalid Client Secreterrors after a secret is rotated in Entra ID. - Root Cause: The custom connector's security settings still reference the old, expired client secret.
- Fix: Manually update the client secret in the custom connector's security settings, or migrate the connector to use Managed Identity to eliminate secrets entirely.
4. DLP Policy Blocking Custom Connector
- Symptom: Flow runs fail at the custom connector action step with a
DlpPolicyViolationerror. - Root Cause: The custom connector host is not classified in the allowed groups of the environment's DLP policy.
- Fix: Add the custom connector by name to the Business or Non-Business group in the Power Platform Admin Center, or configure a tenant-level DLP URL pattern to allow the host.
5. Unmanaged Layers on Connection References
- Symptom: Changes to connection references in a managed solution do not take effect in the target environment.
- Root Cause: An unmanaged layer was created on the connection reference in the target environment (usually by manually editing the connection reference in production).
- Fix: Open the solution in the target environment, select the connection reference, view Solution layers, and delete the unmanaged layer.
6. Missing Pre-authorization for OBO Flow
- Symptom: On-Behalf-Of login fails with an error indicating that the user or application has not consented to the API.
- Root Cause: The Azure API Connections service principal (
fe053c5f-3692-4f14-aef2-ee34fc081cae) is not pre-authorized on the backend API's app registration. - Fix: In the backend API's app registration, navigate to Expose an API, add a client application with ID
fe053c5f-3692-4f14-aef2-ee34fc081cae, and check the exposed scope.
7. Sandbox Timeout (110 Seconds)
- Symptom: Outbound calls fail with HTTP
502 Bad Gatewayafter exactly 110 seconds. - Root Cause: The backend API took longer than 110 seconds to process the request, violating the sandbox execution limit.
- Fix: Optimize the backend API performance, implement asynchronous processing (polling pattern), or use pagination to reduce payload sizes.
8. Missing offline_access Scope
- Symptom: Connections work initially but expire after exactly one hour, requiring the maker to re-authenticate.
- Root Cause: The custom connector did not request the
offline_accessscope, preventing Entra ID from issuing a refresh token. - Fix: Add
offline_accessto the list of scopes in the custom connector's security definition.
9. Single-Tenant App Registration Restriction for Managed Identity
- Symptom: Managed Identity configuration fails to generate tokens, returning authentication errors.
- Root Cause: The Entra ID app registration is configured as multi-tenant. Power Platform Managed Identity for custom connectors requires a single-tenant application registration.
- Fix: Create a new single-tenant app registration in Entra ID and use its Client ID in the custom connector.
10. Solution Import Order Failure
- Symptom: Solution import fails with an error indicating that the custom connector cannot be found.
- Root Cause: The solution contains connection references or flows that depend on a custom connector that has not been imported or registered yet.
- Fix: Export and import the custom connector in a separate, prerequisite solution before importing the solution containing the dependent flows and connection references.
12. Exam Focus: Key Facts & Edge Cases
This section highlights critical, highly specific technical details that are frequently tested in the PL-400 exam.
- Per-Connector Redirect URIs: Since February 2024, all custom connectors using OAuth 2.0 must use unique, per-connector redirect URIs. Legacy global redirect URIs are deprecated and blocked for new connections.
- OpenAPI Version Support: The Power Platform custom connector engine strictly supports OpenAPI 2.0 (Swagger). OpenAPI 3.0 is not supported. If you have an OpenAPI 3.0 definition, you must convert it to 2.0 before importing.
- OpenAPI File Size Limit: The maximum file size for an OpenAPI definition imported into a custom connector is 1 MB.
- Service Principal Support: Custom connectors support Service Principal authentication via Client Credentials (using client secrets) or Managed Identity (using Workload Identity Federation).
- Managed Identity Requirements:
- Requires a single-tenant Entra ID app registration.
- Requires a specific Tenant ID GUID (using
commonis not supported). - Establishes trust via Federated Credentials using the Issuer and Subject strings generated by the custom connector.
- On-Behalf-Of (OBO) Flow:
- Used for seamless, pass-through user authentication.
- Requires the Azure API Connections service principal ID
fe053c5f-3692-4f14-aef2-ee34fc081caeto be pre-authorized in the target API's app registration.
- DLP URL Pattern Matching:
- Tenant-level DLP policies evaluate custom connectors using URL pattern matching (e.g.,
https://*.contoso.com/*). - The wildcard character
*is always the last entry in the pattern list. - The
Ignoreaction defers evaluation to other environment-level or tenant-level policies.
- Tenant-level DLP policies evaluate custom connectors using URL pattern matching (e.g.,
- Sandbox Execution Limits:
- Outbound HTTP requests are strictly limited to HTTPS on port 443.
- The maximum execution timeout is 110 seconds.
- The maximum payload size is 100 MB.
- PowerShell Cmdlets:
Get-AdminPowerAppConnection: Retrieves connections in an environment.Get-AdminPowerAppConnector: Retrieves custom connectors (does not list connectors packaged inside solutions).New-PowerAppPolicyUrlPatterns: Configures tenant-level DLP URL patterns.
- Dataverse Tables:
- Custom connectors are stored in the
connectortable. - Connection references are stored in the
connectionreferencetable.
- Custom connectors are stored in the