【发布时间】:2018-09-13 08:59:55
【问题描述】:
我正在尝试使用第三方身份提供商 PingFed 连接到 Dynamics 365 在线实例。我不负责此身份提供者的配置,也无权访问其任何设置。
使用下面的代码,我能够识别身份验证提供程序并成功进行身份验证,但是,一旦我们被重定向到 Microsoft 登录服务,就会引发错误,我已通过 Fiddler 捕获了该错误。查看 SAML 断言时,它是 SAML 1.1。
我们收到的错误是:
The request is not a valid SAML 2.0 protocol message
如果我使用交互式提示进行身份验证,我可以成功登录。这不能满足我们的需求,因为我们需要有一个服务帐户对 CRM 进行身份验证。
为什么当我们以交互方式登录时它会起作用,而当我们通过代码尝试时它却不起作用?
负责管理身份验证的团队无法提供帮助,所以我希望有人能对发生的事情有所了解?
public static void OverrideExample(bool usePrompt = false)
{
try
{
// Define organization Url
Uri orgUrl = new Uri("https://myorg.crm6.dynamics.com");
bool usePrompt = false;
// Call your existing authentication implementation
AuthenticationResult accessToken = MichelOverrideExampleHookImplementation.GetAccessTokenFromAzureAD(orgUrl, usePrompt);
// Create instance of your hook
var hook = new MichelOverrideExampleHookImplementation();
// Add token to your hook
hook.AddAccessToken(orgUrl, accessToken);
// Register the hook with the CrmServiceClient
CrmServiceClient.AuthOverrideHook = hook;
// Create a new instance of CrmServiceClient, pass your organization url and make sure useUniqueInstance = true!
var client = new CrmServiceClient(orgUrl, useUniqueInstance: true);
if (!client.IsReady)
{
// Connection failed, report error
Console.Error.WriteLine(client.LastCrmException?.Message ?? client.LastCrmError);
}
else
{
// Connection success
// TODO Add your code here
var qry = new QueryExpression("account");
qry.ColumnSet = new ColumnSet(true);
var results = client.RetrieveMultiple(qry);
Console.WriteLine("Connection Succesfull!");
}
}
catch (Exception ex)
{
Console.WriteLine("Exception: " + ex.Message);
}
}
}
public class MichelOverrideExampleHookImplementation : IOverrideAuthHookWrapper
{
// In memory cache of access tokens
Dictionary<string, AuthenticationResult> accessTokens = new Dictionary<string, Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationResult>();
public void AddAccessToken(Uri orgUri, Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationResult accessToken)
{
// Access tokens can be matched on the hostname,
// different endpoints in the same organization can use the same access token
accessTokens[orgUri.Host] = accessToken;
}
public string GetAuthToken(Uri connectedUri)
{
// Check if you have an access token for this host
if (accessTokens.ContainsKey(connectedUri.Host) && accessTokens[connectedUri.Host].ExpiresOn > DateTime.Now)
{
return accessTokens[connectedUri.Host].AccessToken;
}
else
{
accessTokens[connectedUri.Host] = GetAccessTokenFromAzureAD(connectedUri);
}
return null;
}
public static AuthenticationResult GetAccessTokenFromAzureAD(Uri orgUrl,bool usePrompt = false)
{
string resource = "https://myorg.crm6.dynamics.com";
AuthenticationParameters ap = AuthenticationParameters.CreateFromResourceUrlAsync(
new Uri("https://myorg.crm6.dynamics.com/api/data/")).Result;
String authorityUrl = ap.Authority;
String resourceUrl = ap.Resource;
// TODO Substitute your correct CRM root service address,
// TODO Substitute your app registration values that can be obtained after you
// register the app in Active Directory on the Microsoft Azure portal.
string clientId = "120e36b3-0d1a-4596-a0d4-b31eb384607e";
string redirectUrl = "app://myredirecturl";
var userCred = new UserCredential("user@myorg.com", "password");
// Authenticate the registered application with Azure Active Directory.
//Previous authority urls: https://login.windows.net/common
AuthenticationContext authContext = new AuthenticationContext(authorityUrl, false);
AuthenticationResult result = null;
if (usePrompt)
{
result = authContext.AcquireToken(resource, clientId, new Uri(redirectUrl));
}
else
{
result = authContext.AcquireToken(resourceUrl, clientId, userCred);
}
return result;
}
【问题讨论】: