Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions src/PPDS.Auth/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Fixed

- **ServiceClient org metadata not populated** - Credential providers now use `ConnectionOptions` constructor instead of token provider constructor, which triggers org metadata discovery. This populates `ConnectedOrgFriendlyName`, `ConnectedOrgUniqueName`, and `ConnectedOrgId` properties. ([#86](https://github.com/joshsmithxrm/ppds-sdk/issues/86))

### Added

- **Integration tests for credential providers** - Live tests for `ClientSecretCredentialProvider`, `CertificateFileCredentialProvider`, and `GitHubFederatedCredentialProvider` ([#55](https://github.com/joshsmithxrm/ppds-sdk/issues/55))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using Azure.Core;
using Azure.Identity;
using Microsoft.PowerPlatform.Dataverse.Client;
using Microsoft.PowerPlatform.Dataverse.Client.Model;
using PPDS.Auth.Cloud;
using PPDS.Auth.Profiles;

Expand Down Expand Up @@ -76,12 +77,14 @@ public async Task<ServiceClient> CreateServiceClientAsync(

EnsureCredentialInitialized();

var token = await GetTokenAsync(environmentUrl, cancellationToken).ConfigureAwait(false);

var client = new ServiceClient(
new Uri(environmentUrl),
_ => Task.FromResult(token),
useUniqueInstance: true);
// Create ServiceClient using ConnectionOptions to ensure org metadata discovery.
// The provider function acquires tokens on demand and refreshes when needed.
var options = new ConnectionOptions
{
ServiceUri = new Uri(environmentUrl),
AccessTokenProviderFunctionAsync = _ => GetTokenAsync(environmentUrl, CancellationToken.None)
};
var client = new ServiceClient(options);

if (!client.IsReady)
{
Expand Down
18 changes: 11 additions & 7 deletions src/PPDS.Auth/Credentials/DeviceCodeCredentialProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using Microsoft.Identity.Client;
using Microsoft.Identity.Client.Extensions.Msal;
using Microsoft.PowerPlatform.Dataverse.Client;
using Microsoft.PowerPlatform.Dataverse.Client.Model;
using PPDS.Auth.Cloud;
using PPDS.Auth.Profiles;

Expand Down Expand Up @@ -107,14 +108,17 @@ public async Task<ServiceClient> CreateServiceClientAsync(
// Ensure MSAL client is initialized
await EnsureMsalClientInitializedAsync().ConfigureAwait(false);

// Get token
var token = await GetTokenAsync(environmentUrl, forceInteractive, cancellationToken).ConfigureAwait(false);
// Get token and prime the cache (may prompt user for device code auth)
await GetTokenAsync(environmentUrl, forceInteractive, cancellationToken).ConfigureAwait(false);

// Create ServiceClient with token provider
var client = new ServiceClient(
new Uri(environmentUrl),
_ => Task.FromResult(token),
useUniqueInstance: true);
// Create ServiceClient using ConnectionOptions to ensure org metadata discovery.
// The provider function uses cached tokens and refreshes silently when needed.
var options = new ConnectionOptions
{
ServiceUri = new Uri(environmentUrl),
AccessTokenProviderFunctionAsync = _ => GetTokenAsync(environmentUrl, forceInteractive: false, CancellationToken.None)
};
var client = new ServiceClient(options);

if (!client.IsReady)
{
Expand Down
15 changes: 9 additions & 6 deletions src/PPDS.Auth/Credentials/GitHubFederatedCredentialProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using Azure.Core;
using Azure.Identity;
using Microsoft.PowerPlatform.Dataverse.Client;
using Microsoft.PowerPlatform.Dataverse.Client.Model;
using PPDS.Auth.Cloud;
using PPDS.Auth.Profiles;

Expand Down Expand Up @@ -76,12 +77,14 @@ public async Task<ServiceClient> CreateServiceClientAsync(

EnsureCredentialInitialized();

var token = await GetTokenAsync(environmentUrl, cancellationToken).ConfigureAwait(false);

var client = new ServiceClient(
new Uri(environmentUrl),
_ => Task.FromResult(token),
useUniqueInstance: true);
// Create ServiceClient using ConnectionOptions to ensure org metadata discovery.
// The provider function acquires tokens on demand and refreshes when needed.
var options = new ConnectionOptions
{
ServiceUri = new Uri(environmentUrl),
AccessTokenProviderFunctionAsync = _ => GetTokenAsync(environmentUrl, CancellationToken.None)
};
var client = new ServiceClient(options);

if (!client.IsReady)
{
Expand Down
18 changes: 11 additions & 7 deletions src/PPDS.Auth/Credentials/InteractiveBrowserCredentialProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
using Microsoft.Identity.Client;
using Microsoft.Identity.Client.Extensions.Msal;
using Microsoft.PowerPlatform.Dataverse.Client;
using Microsoft.PowerPlatform.Dataverse.Client.Model;
using PPDS.Auth.Cloud;
using PPDS.Auth.Profiles;

Expand Down Expand Up @@ -140,14 +141,17 @@ public async Task<ServiceClient> CreateServiceClientAsync(
// Ensure MSAL client is initialized
await EnsureMsalClientInitializedAsync().ConfigureAwait(false);

// Get token
var token = await GetTokenAsync(environmentUrl, forceInteractive, cancellationToken).ConfigureAwait(false);
// Get token and prime the cache (may prompt user for interactive auth)
await GetTokenAsync(environmentUrl, forceInteractive, cancellationToken).ConfigureAwait(false);

// Create ServiceClient with token provider
var client = new ServiceClient(
new Uri(environmentUrl),
_ => Task.FromResult(token),
useUniqueInstance: true);
// Create ServiceClient using ConnectionOptions to ensure org metadata discovery.
// The provider function uses cached tokens and refreshes silently when needed.
var options = new ConnectionOptions
{
ServiceUri = new Uri(environmentUrl),
AccessTokenProviderFunctionAsync = _ => GetTokenAsync(environmentUrl, forceInteractive: false, CancellationToken.None)
};
var client = new ServiceClient(options);

if (!client.IsReady)
{
Expand Down
17 changes: 9 additions & 8 deletions src/PPDS.Auth/Credentials/ManagedIdentityCredentialProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using Azure.Core;
using Azure.Identity;
using Microsoft.PowerPlatform.Dataverse.Client;
using Microsoft.PowerPlatform.Dataverse.Client.Model;
using PPDS.Auth.Profiles;

namespace PPDS.Auth.Credentials;
Expand Down Expand Up @@ -90,17 +91,17 @@ public async Task<ServiceClient> CreateServiceClientAsync(
// Normalize URL
environmentUrl = environmentUrl.TrimEnd('/');

// Get token
var token = await GetTokenAsync(environmentUrl, cancellationToken).ConfigureAwait(false);

// Create ServiceClient with token provider
// Create ServiceClient using ConnectionOptions to ensure org metadata discovery.
// The provider function acquires tokens on demand and refreshes when needed.
ServiceClient client;
try
{
client = new ServiceClient(
new Uri(environmentUrl),
_ => Task.FromResult(token),
useUniqueInstance: true);
var options = new ConnectionOptions
{
ServiceUri = new Uri(environmentUrl),
AccessTokenProviderFunctionAsync = _ => GetTokenAsync(environmentUrl, CancellationToken.None)
};
client = new ServiceClient(options);
}
catch (Exception ex)
{
Expand Down
Loading