ADAL .Net Core nuget包不支持UserPasswordCredential

在ADAL.Net 3.x中,UserPasswordCredential在2.x的UserCredential之上引入。 但同一个UserPasswordCredential在同一个nuget包下的.Net Core中没有公开?

UserCredential类只有一个属性UserName

namespace Microsoft.IdentityModel.Clients.ActiveDirectory { // // Summary: // Credential used for integrated authentication on domain-joined machines. public class UserCredential { // // Summary: // Constructor to create user credential. Using this constructor would imply integrated // authentication with logged in user and it can only be used in domain joined scenarios. public UserCredential(); // // Summary: // Constructor to create credential with client id and secret // // Parameters: // userName: // Identifier of the user application requests token on behalf. public UserCredential(string userName); // // Summary: // Gets identifier of the user. public string UserName { get; } } } 

由于.NetCore中没有UserPasswordCredential,而UserCredential只使用一个参数用户名,如何输入用户密码并在.Net Core中实现以下代码?

 authContext.AcquireTokenAsync(WebAPIResourceId, ClientId, userPasswordCredential); 

我在.Net Core 1.0版本中特别使用ADAL 3.13.4版本

要使用资源所有者密码凭据授予流来获取Azure AD的访问令牌,我们可以使用HttpClient来调用http请求。 以下是供您参考的示例:

 HttpClient client = new HttpClient(); string tokenEndpoint = "https://login.microsoftonline.com/{tenantId}/oauth2/token"; var body = "resource={resourceUrl}&client_id={clientId}&grant_type=password&username={userName}&password={password}"; var stringContent = new StringContent(body, Encoding.UTF8, "application/x-www-form-urlencoded"); var result=await client.PostAsync(tokenEndpoint, stringContent).ContinueWith((response) => { return response.Result.Content.ReadAsStringAsync().Result; }); JObject jobject = JObject.Parse(result); var token = jobject["access_token"].Value(); 

您是对的, UserPasswordCredential 不适用于.NET Core, UserCredential不再接受用户名和密码。 这意味着ADAL v3不支持.NET Core上的用户名/密码流。

以下是我为解决这个问题所做的工作。 我在.NET Core中使用静态方法复制了相同的行为,因为缺少UserPasswordCredential类。 这是基于在.NET版本中使用UserPasswordCredential类时会发生什么的fiddler跟踪。 由于.NET DLL似乎是混淆的,因此这是捕获它的function的最佳尝试。

 public const string Saml11Bearer = "urn:ietf:params:oauth:grant-type:saml1_1-bearer"; public const string Saml20Bearer = "urn:ietf:params:oauth:grant-type:saml2-bearer"; public const string JwtBearer = "urn:ietf:params:oauth:grant-type:jwt-bearer"; ///  /// Acquire an AAD authentication token silently for an AAD App (Native) with an AAD account /// /// NOTE: This process was ported from the Microsoft.IdentityModel.Clients.ActiveDirectory's /// AuthenticationContext.AcquireTokenAsync method, which can silently authenticate using the UserPasswordCredential class. /// Since this class is missing from .NET Core, this method can be used to perform the same without any dependencies. ///  /// AAD login /// AAD pass /// Tenant ID /// Resource ID: the Azure app that will be accessed /// The Application ID of the calling app. This guid can be obtained from Azure Portal > app auth setup > Advanced Settings public static string GetAuthTokenForAADNativeApp(string user, SecureString pass, string tenantId, string resourceUrl, string clientId) { string tokenForUser = string.Empty; string authority = "https://login.microsoftonline.com/" + tenantId; // The AD Authority used for login string clientRequestID = Guid.NewGuid().ToString(); // Discover the preferred openid / oauth2 endpoint for the tenant (by authority) string api = "https://login.microsoftonline.com/common/discovery/instance?api-version=1.1&authorization_endpoint=" + authority + "/oauth2/authorize"; string openIdPreferredNetwork = string.Empty; var client = new HttpClient(); client.DefaultRequestHeaders.Clear(); client.DefaultRequestHeaders.Add("client-request-id", clientRequestID); client.DefaultRequestHeaders.Add("return-client-request-id", "true"); client.DefaultRequestHeaders.Add("Accept", "application/json"); var responseTask = client.GetAsync(api); responseTask.Wait(); if (responseTask.Result.Content != null) { var responseString = responseTask.Result.Content.ReadAsStringAsync(); responseString.Wait(); try { dynamic json = JObject.Parse(responseString.Result); openIdPreferredNetwork = json.metadata[0].preferred_network; // eg login.microsoftonline.com } catch { } } if (string.IsNullOrEmpty(openIdPreferredNetwork)) openIdPreferredNetwork = "login.microsoftonline.com"; // Get the federation metadata url & federation active auth url by user realm (by user domain) responseTask = client.GetAsync("https://" + openIdPreferredNetwork + "/common/userrealm/" + user + "?api-version=1.0"); responseTask.Wait(); string federation_metadata_url = string.Empty; string federation_active_auth_url = string.Empty; if (responseTask.Result.Content != null) { var responseString = responseTask.Result.Content.ReadAsStringAsync(); responseString.Wait(); try { dynamic json = JObject.Parse(responseString.Result); federation_metadata_url = json.federation_metadata_url; // eg https://sts.{domain}.com.au/adfs/services/trust/mex federation_active_auth_url = json.federation_active_auth_url; // eg https://sts.{domain}.com.au/adfs/services/trust/2005/usernamemixed } catch { } } if(string.IsNullOrEmpty(federation_metadata_url) || string.IsNullOrEmpty(federation_active_auth_url)) return string.Empty; // Get federation metadata responseTask = client.GetAsync(federation_metadata_url); responseTask.Wait(); string federationMetadataXml = null; if (responseTask.Result.Content != null) { var responseString = responseTask.Result.Content.ReadAsStringAsync(); responseString.Wait(); try { federationMetadataXml = responseString.Result; } catch { } } if (string.IsNullOrEmpty(federationMetadataXml)) return string.Empty; // Post credential to the federation active auth URL string messageId = Guid.NewGuid().ToString("D").ToLower(); string postData = @"   http://schemas.xmlsoap.org/ws/2005/02/trust/RST/Issue urn:uuid:" + messageId + @"  http://www.w3.org/2005/08/addressing/anonymous  " + federation_active_auth_url + @"   " + DateTime.Now.ToString("o") + @" " + DateTime.Now.AddMinutes(10).ToString("o") + @"   " + user + @" " + FromSecureString(pass) + @"        urn:federation:MicrosoftOnline   http://schemas.xmlsoap.org/ws/2005/05/identity/NoProofKey http://schemas.xmlsoap.org/ws/2005/02/trust/Issue   "; var content = new StringContent(postData, Encoding.UTF8, "application/soap+xml"); client.DefaultRequestHeaders.Clear(); client.DefaultRequestHeaders.Add("SOAPAction", "http://schemas.xmlsoap.org/ws/2005/02/trust/RST/Issue"); client.DefaultRequestHeaders.Add("client-request-id", clientRequestID); client.DefaultRequestHeaders.Add("return-client-request-id", "true"); client.DefaultRequestHeaders.Add("Accept", "application/json"); responseTask = client.PostAsync(federation_active_auth_url, content); responseTask.Wait(); XmlDocument xml = new XmlDocument(); string assertion = string.Empty; string grant_type = string.Empty; if (responseTask.Result.Content != null) { HttpResponseMessage rseponse = responseTask.Result; Task responseContentTask = rseponse.Content.ReadAsStringAsync(); responseContentTask.Wait(); try { xml.LoadXml(responseContentTask.Result); } catch { } var nodeList = xml.GetElementsByTagName("saml:Assertion"); if (nodeList.Count > 0) { assertion = nodeList[0].OuterXml; // The grant type depends on the assertion value returned previously  grant_type = Saml11Bearer; string majorVersion = nodeList[0].Attributes["MajorVersion"] != null ? nodeList[0].Attributes["MajorVersion"].Value : string.Empty; if (majorVersion == "1") grant_type = Saml11Bearer; if (majorVersion == "2") grant_type = Saml20Bearer; else grant_type = Saml11Bearer; // Default to Saml11Bearer } } // Post to obtain an oauth2 token to for the resource // (*) Pass in the assertion XML node encoded to base64 in the post, as is done here https://blogs.msdn.microsoft.com/azuredev/2018/01/22/accessing-the-power-bi-apis-in-a-federated-azure-ad-setup/ UserAssertion ua = new UserAssertion(assertion, grant_type, Uri.EscapeDataString(user)); UTF8Encoding encoding = new UTF8Encoding(); Byte[] byteSource = encoding.GetBytes(ua.Assertion); string base64ua = Uri.EscapeDataString(Convert.ToBase64String(byteSource)); postData = "resource={resourceUrl}&client_id={clientId}&grant_type={grantType}&assertion={assertion}&scope=openid" .Replace("{resourceUrl}", Uri.EscapeDataString(resourceUrl)) .Replace("{clientId}", Uri.EscapeDataString(clientId)) .Replace("{grantType}", Uri.EscapeDataString(grant_type)) .Replace("{assertion}", base64ua); content = new StringContent(postData, Encoding.UTF8, "application/x-www-form-urlencoded"); client.DefaultRequestHeaders.Clear(); client.DefaultRequestHeaders.Add("client-request-id", clientRequestID); client.DefaultRequestHeaders.Add("return-client-request-id", "true"); client.DefaultRequestHeaders.Add("Accept", "application/json"); responseTask = client.PostAsync("https://" + openIdPreferredNetwork + "/common/oauth2/token", content); responseTask.Wait(); if (responseTask.Result.Content != null) { var responseString = responseTask.Result.Content.ReadAsStringAsync(); responseString.Wait(); try { dynamic json = JObject.Parse(responseString.Result); tokenForUser = json.access_token; } catch { } } if (string.IsNullOrEmpty(federationMetadataXml)) return string.Empty; return tokenForUser; } private static string FromSecureString(SecureString value) { string stringBSTR; IntPtr bSTR = Marshal.SecureStringToBSTR(value); if (bSTR == IntPtr.Zero) { return string.Empty; } try { stringBSTR = Marshal.PtrToStringBSTR(bSTR); } finally { Marshal.FreeBSTR(bSTR); } return stringBSTR; }