Active Directory 身份验证库程序化 SSO
Active Directory Authentication Library Programmatic SSO
我们正在使用 ADAL C# 库获取 Azure AD 应用程序的令牌(包括图形 API)。
我们使用本地 ADFS 3.0 服务器为 Azure AD 设置了 AD Connect。因此,当本机客户端应用程序调用....
var ourDomain = "abc.com";
var authority = "https://login.microsoftonline.com/" + ourDomain;
var authenticationContext = new AuthenticationContext(authority);
var graphResourceUri = "https://graph.windows.net";
var azureADApplicationClientId = "7718c738-0000-0000-0000-4382476f1c65";
var result = authenticationContext.AcquireToken(
graphResourceUri,
azureADApplicationClientId, new Uri("https://localhost"),
PromptBehavior.RefreshSession,
new UserIdentifier($"jdoe@{ourDomain}", UserIdentifierType.RequiredDisplayableId),
$"domain_hint={ourDomain}");
弹出 MSOnline 登录 window,立即重定向到我们位于
的 ADFS 服务器
https://ouradfs.abc.com/adfs/ls/wia?username=.......................
然后立即关闭,因为 NTLM(Windows 集成)身份验证发生并成功...并且 result
包含我们[上登录的 Windows 用户的有效访问令牌=18=] 域名.
尽管此过程不需要任何点击...它仍然是 "interactive"。我希望能够利用 Windows 集成身份验证以编程方式在非交互式环境(例如 Windows 服务或计划任务)中获取此类访问令牌。这样我就不必在我们的 Windows 服务或计划任务中嵌入客户端机密。
这可能吗?
更新:
这是代码行
var authenticationContext = new AuthenticationContext("https://login.microsoftonline.com/mycorpdev.onmicrosoft.com");
AuthenticationResult result = authenticationContext.AcquireToken(
"https://graph.windows.net",
nativeApplicationClientId,
new Uri("https://localhost"),
PromptBehavior.Never,
new UserIdentifier("jsmith@mycorp.com"), UserIdentifierType.RequiredDisplayableId), $"domain_hint=mycorp.com");
以及准确的错误:
Microsoft.IdentityModel.Clients.ActiveDirectory.AdalException occurred
ErrorCode=user_interaction_required
HResult=-2146233088
Message=user_interaction_required: One of two conditions was encountered: 1. The PromptBehavior.Never flag was passed, but the constraint could not be honored, because user interaction was required. 2. An error occurred during a silent web authentication that prevented the http authentication flow from completing in a short enough time frame
Source=Microsoft.IdentityModel.Clients.ActiveDirectory
StackTrace:
at Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext.RunAsyncTask[T](Task`1 task)
另一项更新:
这有效(客户端 ID 和租户与用户的家庭租户相同)
var authenticationContext = new AuthenticationContext("https://login.microsoftonline.com/mycorp.onmicrosoft.com");
AuthenticationResult result = authenticationContext.AcquireToken(
"https://graph.windows.net",
nativeApplicationClientIdFromHomeTenantNotOtherTenant,
new Uri("https://localhost"),
PromptBehavior.Never,
new UserIdentifier("jsmith@mycorp.com"), UserIdentifierType.RequiredDisplayableId), $"domain_hint=mycorp.com");
更新 - 这是来自 Fiddler 的捕获
GET https://login.microsoftonline.com/mycorpdev.onmicrosoft.com/oauth2/authorize?resource=https%3A%2F%2Fgraph.windows.net&client_id=78ebfdee-8144-48f8-9a96-1bd5418c0492&response_type=code&redirect_uri=https%3A%2F%2Flocalhost%2F&login_hint=jsmith%40mycorp.COM&client-request-id=7a8878d4-2762-4784-9d29-6f49b147d474&prompt=attempt_none&x-client-SKU=.NET&x-client-Ver=2.19.0.0&x-client-CPU=x64&x-client-OS=Microsoft+Windows+NT+6.1.7601+Service+Pack+1&domain_hint=mycorpdev.onmicrosoft.com HTTP/1.1
Accept: */*
Accept-Language: en-US
UA-CPU: AMD64
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.1; Win64; x64; Trident/7.0; .NET CLR 2.0.50727; SLCC2; Media Center PC 6.0; Tablet PC 2.0; .NET CLR 3.5.30729; .NET CLR 3.0.30729; .NET4.0C; .NET4.0E)
Host: login.microsoftonline.com
Connection: Keep-Alive
Cookie: ESTSAUTHPERSISTENT=AAABAAEAiL9Kn2Z27UubvWFPbm0gLfZQz8hXb5bXR-iThmkV-FLuLx102LOLAWrogj3rvf40Xl0xjZntMo0Kzvbo0x38Z2CpfCjtOwyyVpp1DWlxyyRPBbl4Z4da5pFuYjfCLPqExGUvo5gBoMdeQ-0MobfbSV2GQCHgbL1CFRjOu6YJZUEgnk7Vyls4rOlHGaqEGpzm5OeFQj3acldcvD9C4PX1gGsV-2g5GU8Frx3co4YzqYHMbhp6fgzf18sfgWaaG9caWj756P2oDvqe9qAlehXk51cA2AUacM2h-k2rtN8F341p7tnOFkNBzCj_E4z3bTnwHodimoXLiDlDWjFWkzAANyAA
HTTP/1.1 302 Found
Cache-Control: no-cache, no-store
Pragma: no-cache
Content-Type: text/html; charset=utf-8
Content-Encoding: gzip
Expires: -1
Location: https://localhost/?error=login_required&error_description=AADSTS50058%3a+User+account+identifier+is+not+provided.%0d%0aTrace+ID%3a+133f0405-eb3d-452d-a8b6-b6ba6267af7c%0d%0aCorrelation+ID%3a+7a8878d4-2762-4784-9d29-6f49b147d474%0d%0aTimestamp%3a+2016-02-21+04%3a54%3a07Z
Vary: Accept-Encoding
Server: Microsoft-IIS/8.5
x-ms-request-id: 133f0405-eb3d-452d-a8b6-b6ba6267af7c
client-request-id: 7a8878d4-2762-4784-9d29-6f49b147d474
x-ms-gateway-service-instanceid: ESTSFE_IN_420
X-Content-Type-Options: nosniff
Strict-Transport-Security: max-age=31536000; includeSubDomains
P3P: CP="DSP CUR OTPi IND OTRi ONL FIN"
Set-Cookie: flight-uxoptin=true; path=/; secure; HttpOnly
Set-Cookie: x-ms-gateway-slice=productionb; path=/; secure; HttpOnly
Set-Cookie: stsservicecookie=ests; path=/; secure; HttpOnly
X-Powered-By: ASP.NET
Date: Sun, 21 Feb 2016 04:54:05 GMT
Content-Length: 0
再更新一次...
如果我尝试使用集成身份验证(注意它确实会尝试正确重定向到我们的 ADFS...):
var authenticationContext = new AuthenticationContext(""https://login.microsoftonline.com/" + UserPrincipal.Current.UserPrincipalName.Split('@')[1]), false);
var nativeClientId = "00000000-0f32-4c38-bdb9-4ea5bd732c69";
var token = authenticationContext.AcquireTokenAsync(Constants.ReportingApplicationUri, nativeClientId, new UserCredential()).Result;
System.AggregateException occurred
HResult=-2146233088
Message=One or more errors occurred.
Source=mscorlib
StackTrace:
at System.Threading.Tasks.Task`1.GetResultCore(Boolean waitCompletionNotification)
:line 68
InnerException:
ErrorCode=federated_service_returned_error
HResult=-2146233088
Message=Federated service at https://ds1.mycorp.com/adfs/services/trust/2005/windowstransport returned error: The message with Action 'http://docs.oasis-open.org/ws-sx/ws-trust/200512/RST/Issue' cannot be processed at the receiver, due to a ContractFilter mismatch at the EndpointDispatcher. This may be because of either a contract mismatch (mismatched Actions between sender and receiver) or a binding/security mismatch between the sender and the receiver. Check that sender and receiver have the same contract and the same binding (including security requirements, e.g. Message, Transport, None).
Source=Microsoft.IdentityModel.Clients.ActiveDirectory
StatusCode=500
StackTrace:
at Microsoft.IdentityModel.Clients.ActiveDirectory.WsTrustRequest.<SendRequestAsync>d__1.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.IdentityModel.Clients.ActiveDirectory.AcquireTokenNonInteractiveHandler.<PreTokenRequest>d__4.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.IdentityModel.Clients.ActiveDirectory.AcquireTokenHandlerBase.<RunAsync>d__0.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext.<AcquireTokenCommonAsync>d__0.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext.<AcquireTokenAsync>d__14.MoveNext()
InnerException:
HResult=-2146233079
Message=The remote server returned an error: (500) Internal Server Error.
Source=System
StackTrace:
at System.Net.HttpWebRequest.EndGetResponse(IAsyncResult asyncResult)
at System.Threading.Tasks.TaskFactory`1.FromAsyncCoreLogic(IAsyncResult iar, Func`2 endFunction, Action`1 endAction, Task`1 promise, Boolean requiresSynchronization)
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.IdentityModel.Clients.ActiveDirectory.HttpWebRequestWrapper.<GetResponseSyncOrAsync>d__2.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.IdentityModel.Clients.ActiveDirectory.WsTrustRequest.<SendRequestAsync>d__1.MoveNext()
InnerException:
当然可以。您可以通过 2 种替代方式进行。
- 通过
PromptBehavior.Never
。这将使用一个不可见的浏览器,使操作 100% 非交互式。这适用于 Kerberos 和任何其他会话类型(例如 cookie)。
- 使用接受
UserCredential
的 AcquireToken
重载。传递给它一个空的 UserCredential
,如 new UserCredential()
。这将强制使用 Kerberos 身份验证。
我们正在使用 ADAL C# 库获取 Azure AD 应用程序的令牌(包括图形 API)。
我们使用本地 ADFS 3.0 服务器为 Azure AD 设置了 AD Connect。因此,当本机客户端应用程序调用....
var ourDomain = "abc.com";
var authority = "https://login.microsoftonline.com/" + ourDomain;
var authenticationContext = new AuthenticationContext(authority);
var graphResourceUri = "https://graph.windows.net";
var azureADApplicationClientId = "7718c738-0000-0000-0000-4382476f1c65";
var result = authenticationContext.AcquireToken(
graphResourceUri,
azureADApplicationClientId, new Uri("https://localhost"),
PromptBehavior.RefreshSession,
new UserIdentifier($"jdoe@{ourDomain}", UserIdentifierType.RequiredDisplayableId),
$"domain_hint={ourDomain}");
弹出 MSOnline 登录 window,立即重定向到我们位于
的 ADFS 服务器https://ouradfs.abc.com/adfs/ls/wia?username=.......................
然后立即关闭,因为 NTLM(Windows 集成)身份验证发生并成功...并且 result
包含我们[上登录的 Windows 用户的有效访问令牌=18=] 域名.
尽管此过程不需要任何点击...它仍然是 "interactive"。我希望能够利用 Windows 集成身份验证以编程方式在非交互式环境(例如 Windows 服务或计划任务)中获取此类访问令牌。这样我就不必在我们的 Windows 服务或计划任务中嵌入客户端机密。
这可能吗?
更新: 这是代码行
var authenticationContext = new AuthenticationContext("https://login.microsoftonline.com/mycorpdev.onmicrosoft.com");
AuthenticationResult result = authenticationContext.AcquireToken(
"https://graph.windows.net",
nativeApplicationClientId,
new Uri("https://localhost"),
PromptBehavior.Never,
new UserIdentifier("jsmith@mycorp.com"), UserIdentifierType.RequiredDisplayableId), $"domain_hint=mycorp.com");
以及准确的错误:
Microsoft.IdentityModel.Clients.ActiveDirectory.AdalException occurred
ErrorCode=user_interaction_required
HResult=-2146233088
Message=user_interaction_required: One of two conditions was encountered: 1. The PromptBehavior.Never flag was passed, but the constraint could not be honored, because user interaction was required. 2. An error occurred during a silent web authentication that prevented the http authentication flow from completing in a short enough time frame
Source=Microsoft.IdentityModel.Clients.ActiveDirectory
StackTrace:
at Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext.RunAsyncTask[T](Task`1 task)
另一项更新:
这有效(客户端 ID 和租户与用户的家庭租户相同)
var authenticationContext = new AuthenticationContext("https://login.microsoftonline.com/mycorp.onmicrosoft.com");
AuthenticationResult result = authenticationContext.AcquireToken(
"https://graph.windows.net",
nativeApplicationClientIdFromHomeTenantNotOtherTenant,
new Uri("https://localhost"),
PromptBehavior.Never,
new UserIdentifier("jsmith@mycorp.com"), UserIdentifierType.RequiredDisplayableId), $"domain_hint=mycorp.com");
更新 - 这是来自 Fiddler 的捕获
GET https://login.microsoftonline.com/mycorpdev.onmicrosoft.com/oauth2/authorize?resource=https%3A%2F%2Fgraph.windows.net&client_id=78ebfdee-8144-48f8-9a96-1bd5418c0492&response_type=code&redirect_uri=https%3A%2F%2Flocalhost%2F&login_hint=jsmith%40mycorp.COM&client-request-id=7a8878d4-2762-4784-9d29-6f49b147d474&prompt=attempt_none&x-client-SKU=.NET&x-client-Ver=2.19.0.0&x-client-CPU=x64&x-client-OS=Microsoft+Windows+NT+6.1.7601+Service+Pack+1&domain_hint=mycorpdev.onmicrosoft.com HTTP/1.1
Accept: */*
Accept-Language: en-US
UA-CPU: AMD64
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.1; Win64; x64; Trident/7.0; .NET CLR 2.0.50727; SLCC2; Media Center PC 6.0; Tablet PC 2.0; .NET CLR 3.5.30729; .NET CLR 3.0.30729; .NET4.0C; .NET4.0E)
Host: login.microsoftonline.com
Connection: Keep-Alive
Cookie: ESTSAUTHPERSISTENT=AAABAAEAiL9Kn2Z27UubvWFPbm0gLfZQz8hXb5bXR-iThmkV-FLuLx102LOLAWrogj3rvf40Xl0xjZntMo0Kzvbo0x38Z2CpfCjtOwyyVpp1DWlxyyRPBbl4Z4da5pFuYjfCLPqExGUvo5gBoMdeQ-0MobfbSV2GQCHgbL1CFRjOu6YJZUEgnk7Vyls4rOlHGaqEGpzm5OeFQj3acldcvD9C4PX1gGsV-2g5GU8Frx3co4YzqYHMbhp6fgzf18sfgWaaG9caWj756P2oDvqe9qAlehXk51cA2AUacM2h-k2rtN8F341p7tnOFkNBzCj_E4z3bTnwHodimoXLiDlDWjFWkzAANyAA
HTTP/1.1 302 Found
Cache-Control: no-cache, no-store
Pragma: no-cache
Content-Type: text/html; charset=utf-8
Content-Encoding: gzip
Expires: -1
Location: https://localhost/?error=login_required&error_description=AADSTS50058%3a+User+account+identifier+is+not+provided.%0d%0aTrace+ID%3a+133f0405-eb3d-452d-a8b6-b6ba6267af7c%0d%0aCorrelation+ID%3a+7a8878d4-2762-4784-9d29-6f49b147d474%0d%0aTimestamp%3a+2016-02-21+04%3a54%3a07Z
Vary: Accept-Encoding
Server: Microsoft-IIS/8.5
x-ms-request-id: 133f0405-eb3d-452d-a8b6-b6ba6267af7c
client-request-id: 7a8878d4-2762-4784-9d29-6f49b147d474
x-ms-gateway-service-instanceid: ESTSFE_IN_420
X-Content-Type-Options: nosniff
Strict-Transport-Security: max-age=31536000; includeSubDomains
P3P: CP="DSP CUR OTPi IND OTRi ONL FIN"
Set-Cookie: flight-uxoptin=true; path=/; secure; HttpOnly
Set-Cookie: x-ms-gateway-slice=productionb; path=/; secure; HttpOnly
Set-Cookie: stsservicecookie=ests; path=/; secure; HttpOnly
X-Powered-By: ASP.NET
Date: Sun, 21 Feb 2016 04:54:05 GMT
Content-Length: 0
再更新一次...
如果我尝试使用集成身份验证(注意它确实会尝试正确重定向到我们的 ADFS...):
var authenticationContext = new AuthenticationContext(""https://login.microsoftonline.com/" + UserPrincipal.Current.UserPrincipalName.Split('@')[1]), false);
var nativeClientId = "00000000-0f32-4c38-bdb9-4ea5bd732c69";
var token = authenticationContext.AcquireTokenAsync(Constants.ReportingApplicationUri, nativeClientId, new UserCredential()).Result;
System.AggregateException occurred
HResult=-2146233088
Message=One or more errors occurred.
Source=mscorlib
StackTrace:
at System.Threading.Tasks.Task`1.GetResultCore(Boolean waitCompletionNotification)
:line 68
InnerException:
ErrorCode=federated_service_returned_error
HResult=-2146233088
Message=Federated service at https://ds1.mycorp.com/adfs/services/trust/2005/windowstransport returned error: The message with Action 'http://docs.oasis-open.org/ws-sx/ws-trust/200512/RST/Issue' cannot be processed at the receiver, due to a ContractFilter mismatch at the EndpointDispatcher. This may be because of either a contract mismatch (mismatched Actions between sender and receiver) or a binding/security mismatch between the sender and the receiver. Check that sender and receiver have the same contract and the same binding (including security requirements, e.g. Message, Transport, None).
Source=Microsoft.IdentityModel.Clients.ActiveDirectory
StatusCode=500
StackTrace:
at Microsoft.IdentityModel.Clients.ActiveDirectory.WsTrustRequest.<SendRequestAsync>d__1.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.IdentityModel.Clients.ActiveDirectory.AcquireTokenNonInteractiveHandler.<PreTokenRequest>d__4.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.IdentityModel.Clients.ActiveDirectory.AcquireTokenHandlerBase.<RunAsync>d__0.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext.<AcquireTokenCommonAsync>d__0.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext.<AcquireTokenAsync>d__14.MoveNext()
InnerException:
HResult=-2146233079
Message=The remote server returned an error: (500) Internal Server Error.
Source=System
StackTrace:
at System.Net.HttpWebRequest.EndGetResponse(IAsyncResult asyncResult)
at System.Threading.Tasks.TaskFactory`1.FromAsyncCoreLogic(IAsyncResult iar, Func`2 endFunction, Action`1 endAction, Task`1 promise, Boolean requiresSynchronization)
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.IdentityModel.Clients.ActiveDirectory.HttpWebRequestWrapper.<GetResponseSyncOrAsync>d__2.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.IdentityModel.Clients.ActiveDirectory.WsTrustRequest.<SendRequestAsync>d__1.MoveNext()
InnerException:
当然可以。您可以通过 2 种替代方式进行。
- 通过
PromptBehavior.Never
。这将使用一个不可见的浏览器,使操作 100% 非交互式。这适用于 Kerberos 和任何其他会话类型(例如 cookie)。 - 使用接受
UserCredential
的AcquireToken
重载。传递给它一个空的UserCredential
,如new UserCredential()
。这将强制使用 Kerberos 身份验证。