|
| 1 | +--- |
| 2 | +layout: post |
| 3 | +title: "Enabling SSO for Custom Connectors in Copilot Studio" |
| 4 | +date: 2025-11-15 |
| 5 | +categories: [copilot-studio, connectors, authentication] |
| 6 | +tags: [sso, custom-connectors, entra-id, authentication, api] |
| 7 | +description: How to configure custom connectors with Entra ID SSO to enable seamless user authentication and consent flow. |
| 8 | +#image: /assets/posts/custom-connector-sso/sso-flow.png |
| 9 | +author: adilei |
| 10 | +mermaid: true |
| 11 | +published: false |
| 12 | +--- |
| 13 | + |
| 14 | +When building agents in Copilot Studio, you often need to call downstream custom APIs with the user's credentials. Until recently, most builders relied on [manual authentication](https://learn.microsoft.com/en-us/microsoft-copilot-studio/configuration-end-user-authentication#authenticate-manually) to populate `System.User.AccessToken` and pass it as a bearer token to their APIs. |
| 15 | + |
| 16 | +But here's a little-known capability* that makes a difference: **Custom connectors support the same SSO/consent experience as first-party connectors (as long as they support Entra authentication)**. |
| 17 | + |
| 18 | +*At least it wasn't known to me, so I assumed no one else knew about it either. Sorry. |
| 19 | + |
| 20 | +## The Problem with Manual Authentication |
| 21 | + |
| 22 | +While manual authentication works, it has significant drawbacks: |
| 23 | + |
| 24 | +1. **Complex Setup**: requires configuring two app registrations per agent (at least per out best practices) |
| 25 | +2. **No Tenant Graph Grounding**: Manual authentication isn't compatible with [Tenant Graph Grounding](https://learn.microsoft.com/en-us/microsoft-copilot-studio/knowledge-copilot-studio#tenant-graph-grounding), which provides higher quality responses grounded in SharePoint and Graph Connectors |
| 26 | +3. **Single Resource Limitation**: You configure a single OAuth resource for manual auth, which means you cannot obtain both a Graph token and a token for your custom API simultaneously. |
| 27 | + |
| 28 | +## How Connector SSO Works in Copilot Studio |
| 29 | + |
| 30 | +Before diving into custom connectors, let's understand how Copilot Studio handles authentication for **first-party connectors** that support Entra ID. |
| 31 | + |
| 32 | +### The Consent Card Experience |
| 33 | + |
| 34 | +When a signed-in user is conversing with an agent and the agent invokes a connector configured with [end-user authentication](https://learn.microsoft.com/en-us/microsoft-copilot-studio/configure-enduser-authentication), Copilot Studio presents a **consent card** asking the user to grant permission for the agent to create a connection on their behalf. |
| 35 | + |
| 36 | +{: .shadow w="700" h="400"} |
| 37 | +_The seamless consent card experience_ |
| 38 | + |
| 39 | +This is purely for privacy considerations. When users click `Allow` Copilot Studio handles all the OAuth token exchange automatically behind the scenes, obtaining tokens on behalf of the user (OBO flow). |
| 40 | + |
| 41 | +## This Also Works for Custom Connectors! |
| 42 | + |
| 43 | +Custom connectors configured with Entra authentication can leverage the exact same SSO/consent experience as first-party connectors, but there are specific configuration requirements: |
| 44 | + |
| 45 | +1. A **service app registration** representing your custom API |
| 46 | +2. A **connector app registration**, representing your, well custom connector |
| 47 | +3. Grant permissions to the connector app to obtain an OBO token with the service app as the resource |
| 48 | +4. Configure authentication for your custom connector |
| 49 | + |
| 50 | +This may seem like a lot of steps, but **it's done once per connector, and then any agent can use that connector** with the same seamless SSO experience. |
| 51 | + |
| 52 | +When a signed-in user invokes your custom connector for the first time, they see the consent prompt showing your custom API's scopes. Once granted, subsequent calls work automatically with the user's credentials—just like Office 365 Users, SharePoint, or any other first-party connector using Entra auth. |
| 53 | + |
| 54 | +{: .shadow w="700" h="400"} |
| 55 | +_The seamless consent card experience, this time for a custom connector_ |
| 56 | + |
| 57 | +{: .shadow w="700" h="400"} |
| 58 | +_The agent responds based on connector output_ |
| 59 | + |
| 60 | +## The Better Way: Custom Connector SSO |
| 61 | + |
| 62 | +Just like first-party connectors that support Entra ID authentication, custom connectors can trigger the elegant [consent card experience](/posts/connector-consent-card-obo/) instead of the universally disliked connection manager. |
| 63 | + |
| 64 | +{: .shadow w="700" h="400"} |
| 65 | +_The seamless consent experience - now available for custom connectors_ |
| 66 | + |
| 67 | +When a signed-in user is conversing with an agent and invokes a connector configured with Entra auth, they see a consent prompt asking permission for Copilot Studio to sign in on their behalf. This is purely for privacy considerations—**users aren't expected to actually sign in manually**. |
| 68 | + |
| 69 | +## Configuration Overview |
| 70 | + |
| 71 | +Setting up custom connector SSO requires configuring two app registrations and a custom connector. Let's walk through each step in detail. |
| 72 | + |
| 73 | +### Understanding the Authentication Flow |
| 74 | + |
| 75 | +Before diving into configuration, let's visualize how the authentication flow works: |
| 76 | + |
| 77 | +```mermaid |
| 78 | +sequenceDiagram |
| 79 | + participant User |
| 80 | + participant Agent as Copilot Studio Agent |
| 81 | + participant ConnFW as Connector Framework |
| 82 | + participant EntraID as Microsoft Entra ID |
| 83 | + participant API as Your Custom API |
| 84 | +
|
| 85 | + User->>Agent: Starts conversation |
| 86 | + Agent->>Agent: Determines tool/connector needed |
| 87 | + Agent->>User: Shows consent card |
| 88 | + Note over User,Agent: "Allow connector to access <br/>data on your behalf?" |
| 89 | + |
| 90 | + User->>Agent: Clicks "Allow" |
| 91 | + ConnFW->>EntraID: Request token (OBO flow) |
| 92 | + Note over ConnFW,EntraID: Using Connector App credentials<br/>and user's auth context |
| 93 | + |
| 94 | + EntraID-->>ConnFW: Returns access token |
| 95 | + Note over ConnFW: Token audience: api://service-app-id<br/>Token scope: access_as_user |
| 96 | + |
| 97 | + Agent->>ConnFW: Invokes action |
| 98 | + ConnFW->>API: Calls API with Bearer token |
| 99 | + Note over ConnFW,API: Authorization: Bearer eyJ0eXAi... |
| 100 | + |
| 101 | + API->>API: Validates token |
| 102 | + Note over API: Checks audience, issuer,<br/>signature, expiration |
| 103 | + |
| 104 | + API-->>ConnFW: Returns data |
| 105 | + ConnFW-->>Agent: Returns response |
| 106 | + Agent->>User: Displays result |
| 107 | + |
| 108 | + Note over User,API: Subsequent calls use cached token<br/>No additional consent required |
| 109 | +``` |
| 110 | + |
| 111 | +**Key Points:** |
| 112 | +- **One-time consent**: Users consent once per connector |
| 113 | +- **OBO (On-Behalf-Of) flow**: Connector Framework requests tokens using the user's context |
| 114 | +- **Token audience**: Points to your Service App (`api://service-app-id`) |
| 115 | +- **Automatic refresh**: Connector Framework handles token renewal |
| 116 | +- **User context preserved**: API receives user's identity and claims |
| 117 | + |
| 118 | +### Step 1: Create the Service App Registration (Your Custom API) |
| 119 | + |
| 120 | +The service app registration represents your custom API. This is the OAuth resource that will be protected and accessed by the connector. |
| 121 | + |
| 122 | +#### 1.1 Create the App Registration |
| 123 | + |
| 124 | +1. Navigate to [Azure Portal](https://portal.azure.com) → **Microsoft Entra ID** → **App registrations** |
| 125 | +2. Click **New registration** |
| 126 | +3. Configure basic settings: |
| 127 | + - **Name**: `Service App 3p` (or your API name) |
| 128 | + - **Supported account types**: **Accounts in this organizational directory only (Single tenant)** |
| 129 | + - **Redirect URI**: Leave blank (not needed for API) |
| 130 | +4. Click **Register** |
| 131 | + |
| 132 | +#### 1.2 Note the Application ID URI |
| 133 | + |
| 134 | +1. After creation, go to **Overview** |
| 135 | +2. Note the **Application (client) ID** - you'll need this later |
| 136 | +3. The Application ID URI will be in format: `api://<client-id>` |
| 137 | + |
| 138 | +#### 1.3 Expose an API |
| 139 | + |
| 140 | +1. In the left menu, click **Expose an API** |
| 141 | +2. Next to **Application ID URI**, click **Add** |
| 142 | +3. Accept the default `api://<client-id>` or customize it (e.g., `api://contoso.com/myapi`) |
| 143 | +4. Click **Save** |
| 144 | + |
| 145 | +{: .shadow w="700" h="400"} |
| 146 | +_Setting the Application ID URI_ |
| 147 | + |
| 148 | +#### 1.4 Add a Custom Scope |
| 149 | + |
| 150 | +1. Still in **Expose an API**, click **Add a scope** |
| 151 | +2. Configure the scope: |
| 152 | + - **Scope name**: `access_as_user` |
| 153 | + - **Who can consent**: **Admins and users** |
| 154 | + - **Admin consent display name**: `Access as user` |
| 155 | + - **Admin consent description**: `Access the API as the signed-in user` |
| 156 | + - **User consent display name**: `Access as user` |
| 157 | + - **User consent description**: `Allow the app to access the API on your behalf` |
| 158 | + - **State**: **Enabled** |
| 159 | +3. Click **Add scope** |
| 160 | + |
| 161 | +{: .shadow w="700" h="400"} |
| 162 | +_Defining a custom scope for your API_ |
| 163 | + |
| 164 | +The full scope identifier will be: `api://<client-id>/access_as_user` |
| 165 | + |
| 166 | +#### 1.5 Configure API Permissions (Optional) |
| 167 | + |
| 168 | +Your service app registration typically **doesn't need any API permissions** itself, unless your API needs to call other APIs like Microsoft Graph. |
| 169 | + |
| 170 | +If your API needs to call Microsoft Graph: |
| 171 | + |
| 172 | +1. Go to **API permissions** |
| 173 | +2. Click **Add a permission** → **Microsoft Graph** → **Delegated permissions** |
| 174 | +3. Add only what you need (e.g., `User.Read` if you need basic profile info) |
| 175 | +4. Click **Add permissions** |
| 176 | +5. Click **Grant admin consent for [Your Tenant]** if required |
| 177 | + |
| 178 | +{: .shadow w="700" h="400"} |
| 179 | +_Most service apps only need User.Read, if anything_ |
| 180 | + |
| 181 | +**Important**: Keep permissions minimal. The service app represents your API, not the connector consuming it. |
| 182 | + |
| 183 | +#### 1.6 Record Key Values |
| 184 | + |
| 185 | +Save these values for later steps: |
| 186 | +- **Application (client) ID**: `b52b5b7a-e895-4600-b567-2c4cbe27d2e7` (example) |
| 187 | +- **Application ID URI**: `api://b52b5b7a-e895-4600-b567-2c4cbe27d2e7` |
| 188 | +- **Scope**: `api://b52b5b7a-e895-4600-b567-2c4cbe27d2e7/access_as_user` |
| 189 | + |
| 190 | +### Step 2: Create the Connector App Registration |
| 191 | + |
| 192 | +[Placeholder: Connector app registration configuration] |
| 193 | + |
| 194 | +### Step 3: Configure the Custom Connector |
| 195 | + |
| 196 | +[Placeholder: Custom connector configuration] |
| 197 | + |
| 198 | +## Best Practices |
| 199 | + |
| 200 | +[Placeholder: Security and implementation best practices] |
| 201 | + |
| 202 | +- Least privilege scopes |
| 203 | +- Token caching |
| 204 | +- Error handling |
| 205 | +- Testing strategies |
| 206 | + |
| 207 | +## Comparison: Manual Auth vs. Custom Connector SSO |
| 208 | + |
| 209 | +| Feature | Manual Auth | Custom Connector SSO | |
| 210 | +| ---------------------- | ----------------------- | ------------------------ | |
| 211 | +| Setup Complexity | High | Medium | |
| 212 | +| Tenant Graph Grounding | ❌ No | ✅ Yes | |
| 213 | +| User Experience | Poor (explicit sign-in) | Excellent (consent card) | |
| 214 | +| Token Management | Manual | Automatic | |
| 215 | +| MCP Server Support | ❌ No | ✅ Yes | |
| 216 | + |
| 217 | +## Real-World Scenarios |
| 218 | + |
| 219 | +Custom connector SSO is perfect for: |
| 220 | + |
| 221 | +- **Enterprise API Integration**: Call internal APIs with user context |
| 222 | +- **Multi-tenant Applications**: Maintain user identity across services |
| 223 | +- **Compliance Requirements**: Ensure proper user authorization |
| 224 | +- **MCP Servers**: Connect to Model Context Protocol servers with Entra auth |
| 225 | + |
| 226 | +## Troubleshooting |
| 227 | + |
| 228 | +[Placeholder: Common issues and solutions] |
| 229 | + |
| 230 | +- Consent not appearing |
| 231 | +- Token validation failures |
| 232 | +- Scope mismatches |
| 233 | + |
| 234 | +## Further Reading |
| 235 | + |
| 236 | +- [Configure End-User Authentication](https://learn.microsoft.com/en-us/microsoft-copilot-studio/configure-enduser-authentication) |
| 237 | +- [Intercepting Connector Consent Cards](/posts/connector-consent-card-obo/) |
| 238 | +- [Tenant Graph Grounding](https://learn.microsoft.com/en-us/microsoft-copilot-studio/knowledge-copilot-studio#tenant-graph-grounding) |
| 239 | + |
| 240 | +--- |
| 241 | + |
| 242 | +*Special thanks to the Copilot Studio engineering team for clarifying this capability!* |
0 commit comments