I've set up a development server with AD and I'm trying to figure out how to connect to it via .NET. I'm working on the same machine that AD is installed on. I've gotten the DC name from AD, and the name of the machine, but the connection just does not work. I'm using the same credentials I used to connect to the server.
Any suggestions?
DirectoryEntry directoryEntry = new DirectoryEntry("LDAP://[dc.computername.com]", "administrator", "[adminpwd]");
Can you connect to the RootDSE container?
DirectoryEntry rootDSE = new DirectoryEntry("LDAP://RootDSE", "administrator", "[adminpwd]");
If that works, you can then read out some of the properties stored in that root container
if (rootDSE != null)
{
Console.WriteLine("RootDSE Properties:\n\n");
foreach (string propName in rootDSE.Properties.PropertyNames)
{
Console.WriteLine("{0:-20d}: {1}", propName, rootDSE.Properties[propName][0]);
}
}
This will show you some information about what LDAP paths are present in your installation.
Try something like this, using the System.DirectoryServices.Protocols namespace :
//Define your connection
LdapConnection ldapConnection = new LdapConnection("123.456.789.10:389");
try
{
//Authenticate the username and password
using (ldapConnection)
{
//Pass in the network creds, and the domain.
var networkCredential = new NetworkCredential(Username, Password, Domain);
//Since we're using unsecured port 389, set to false. If using port 636 over SSL, set this to true.
ldapConnection.SessionOptions.SecureSocketLayer = false;
ldapConnection.SessionOptions.VerifyServerCertificate += delegate { return true; };
//To force NTLM\Kerberos use AuthType.Negotiate, for non-TLS and unsecured, use AuthType.Basic
ldapConnection.AuthType = AuthType.Basic;
ldapConnection.Bind(networkCredential);
}
catch (LdapException ldapException)
{
//Authentication failed, exception will dictate why
}
}
Related
I'm trying to use Microsoft.Identity.Client and Microsoft.SharePoint.Client libraries to authenticate to an On-premise SharePoint server and then query it.
I obtain the Azure AD access token from which the SharePoint server is a part of like following:
private readonly string[] m_scopes = { "user.read", "https://sql.azuresynapse-dogfood.net/user_impersonation" };
var publicAppBuilder = PublicClientApplicationBuilder.Create("MyClientId").WithAuthority("https://login.microsoftonline.com/a******com.onmicrosoft.com");
publicAppBuilder.WithRedirectUri("https://login.microsoftonline.com/common/oauth2/nativeclient");
var app = publicAppBuilder.Build();
AuthenticationResult result = null;
result = app.AcquireTokenInteractive(m_scopes).ExecuteAsync().GetAwaiter().GetResult();
if (result != null)
{
m_mediator.AccessToken = result.AccessToken;
}
When I get the access token I put it in the request header as follows:
args.WebRequestExecutor.RequestHeaders["Authorization"] = "Bearer " + m_mediator.AccessToken;
Which is inside the ClientContext.ExecutingWebRequest subscribed method:
clientContext.ExecutingWebRequest += (sender, args) =>
which is triggered by
context.ExecuteQuery();
The remote server returned an error: (401) Unauthorized.
or
The remote server returned an error: (403) Forbidden.
How can I establish the connection? I want to avoid using app-only registration, I want to authenticate using Azure AD MFA (Interactive) method.Please note that I have all the permissions needed and I am an admin on both Azure AD where SharePoint is joined, as well on the SharePoint server itself. I authenticate through the browser just fine.
I've tried multiple things so far:
I tried creating a separate request where I forward the previously acquired accessToken as Authorization: Bearer token
I tried reading the FedAuth from the authentication connection window, so I can forward it in my HTTP request but with no success
I tried creating a "Web browser" using a WebBrowser C# class and reading the cookies that are on a browser level like the following: cookieContainer = webBrowser1.Document.Cookie; but I had no success.
I'm expecting to Authenticate via Azure AD and then connect to SharePoint in order to query it
To resolve the error "The remote server returned an error: (401)
Unauthorized", please try checking the following:
Check whether your URL is correct:
The SharePoint Online URL must always start with HTTPS.
$SiteURL` `=` `"https://crescent.sharepoint.com/sites/marketing"`
Check if you have the right permissions to the site:
Check whether you have sufficient permissions and you are able to open the site in the browser. Make sure to have SharePoint Online Administrator Role.
Check whether the Legacy authentication protocol is enabled:
Make sure to enable Legacy authentication protocol in your tenant, if it is not enabled.
Reference : SharePoint Online: Fix "The remote server returned an error (401) Unauthorized" Error in PowerShell - SharePoint Diary
To resolve the error "The remote server returned an error: (403)
Forbidden.", please try checking the following:
Make sure whether you have provided correct URL and credentials.
Make sure whether you have installed latest version of SharePoint Online Client Component SDK.
Try adding yourself to the site explicitly
Check the lock status of your site and unlock if it is locked.
Please check if any conditional access policies is enabled in your tenant.
If you try to connect to the Tenant Admin site, make sure the Tenant Admin URL like below:
https://YourDomain-admin.sharepoint.com
Reference : SharePoint Online: Fix "The remote server returned an error: (403) Forbidden." Error in PowerShell - SharePoint Diary.
I've found a solution.
I basically iterate through all cookies whenever a browser navigates through a new page and parse all the cookies until I get the fedAuth cookie:
I created a web browser from System.Windows.Forms.WebBrowser
In the WebBrowserNavigatedEventHandler for Navigated I do the following:
if (webBrowser1.Url.AbsoluteUri == "about:blank")
{
return;
}
var cookieData = GetWebBrowserCookie.GetCookieInternal(webBrowser1.Url, false);
if (string.IsNullOrEmpty(cookieData) == false)
{
var dict = ParseCookieData(cookieData);
if (dict.ContainsKey("FedAuth") && !string.IsNullOrEmpty(dict["FedAuth"]))
{
m_mediator.FedAuthCookie = dict["FedAuth"];
if (dict.ContainsKey("rtFa") && !string.IsNullOrEmpty(dict["rtFa"]))
{
m_mediator.RtFaCookie = dict["rtFa"];
}
m_mediator.UpdateConfiguration();
this.Close();
}
}
The ParseCookieData method looks like this:
private IDictionary<string, string> ParseCookieData(string cookieData)
{
var cookieDictionary = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
if (string.IsNullOrEmpty(cookieData))
{
return cookieDictionary;
}
var values = cookieData.TrimEnd(';').Split(';');
foreach (var parts in values.Select(c => c.Split(new[] { '=' }, 2)))
{
var cookieName = parts[0].Trim();
var cookieValue = parts.Length == 1 ? string.Empty : parts[1];
cookieDictionary[cookieName] = cookieValue;
}
return cookieDictionary;
}
and GetWebBrowserCookie class looks like this:
[SecurityCritical]
public static string GetCookieInternal(Uri uri, bool throwIfNoCookie)
{
uint pchCookieData = 0;
string url = UriToString(uri);
uint flag = (uint)NativeMethods.InternetFlags.INTERNET_COOKIE_HTTPONLY;
//Gets the size of the string builder
if (NativeMethods.InternetGetCookieEx(url, null, null, ref pchCookieData, flag, IntPtr.Zero))
{
pchCookieData++;
StringBuilder cookieData = new StringBuilder((int)pchCookieData);
//Read the cookie
if (NativeMethods.InternetGetCookieEx(url, null, cookieData, ref pchCookieData, flag, IntPtr.Zero))
{
DemandWebPermission(uri);
return cookieData.ToString();
}
}
int lastErrorCode = Marshal.GetLastWin32Error();
if (throwIfNoCookie || (lastErrorCode != (int)NativeMethods.ErrorFlags.ERROR_NO_MORE_ITEMS))
{
throw new Win32Exception(lastErrorCode);
}
return null;
}
private static void DemandWebPermission(Uri uri)
{
string uriString = UriToString(uri);
if (uri.IsFile)
{
string localPath = uri.LocalPath;
new FileIOPermission(FileIOPermissionAccess.Read, localPath).Demand();
}
else
{
new WebPermission(NetworkAccess.Connect, uriString).Demand();
}
}
private static string UriToString(Uri uri)
{
if (uri == null)
{
return string.Empty;
}
UriComponents components = (uri.IsAbsoluteUri ? UriComponents.AbsoluteUri : UriComponents.SerializationInfoString);
return new StringBuilder(uri.GetComponents(components, UriFormat.SafeUnescaped), 2083).ToString();
}
This way we open up a pop-up C# web browser, authenticate the user through the web using MFA and then close the browser when we acquire an authentication cookie so we can continue working with HTTP requests towards the Sharepoint server.
Source: https://github.com/OceanAirdrop/SharePointOnlineGetFedAuthAndRtfaCookie
I'm trying to use a local login form to authenticate a user credentials against its external provider (Azure Active Directory).
I understand that, per client, you can enable local login. That helps, as when set to true, I'll get the local login form but but I'm still unclear as to how to fire off the middle ware for that external provider. Is there a way to send client credentials to the external provider to receive an ID token? My current code redirects to the Microsoft login; and then back to my identity server, and then the client application. I want the user to login in through identity server but not have them know it's really authenticating against Azure.
Here's my start up:
var schemeName = "Azure-AD";
var dataProtectionProvibder = app.ApplicationServices.GetRequiredService<IDataProtectionProvider>();
var distributedCache = app.ApplicationServices.GetRequiredService<IDistributedCache>();
var dataProtector = dataProtectionProvider.CreateProtector(
typeof(OpenIdConnectMiddleware).FullName,
typeof(string).FullName, schemeName,
"v1");
var dataFormat = new CachedPropertiesDataFormat(distributedCache, dataProtector);
///
/// Azure AD Configuration
///
var clientId = Configuration["AzureActiveDirectory:ClientId"];
var tenantId = Configuration["AzureActiveDirectory:TenantId"];
Redirect = Configuration["AzureActiveDirectory:TenantId"];
app.UseOpenIdConnectAuthentication(new OpenIdConnectOptions
{
AuthenticationScheme = schemeName,
DisplayName = "Azure-AD",
SignInScheme = IdentityServerConstants.ExternalCookieAuthenticationScheme,
ClientId = clientId,
Authority = $"https://login.microsoftonline.com/{tenantId}",
ResponseType = OpenIdConnectResponseType.IdToken,
StateDataFormat = dataFormat,
});
app.UseIdentity();
app.UseStaticFiles();
app.UseMvcWithDefaultRoute();
This is the login.
[HttpGet]
public async Task<IActionResult> ExternalLogin(string provider, string returnUrl)
{
var context = this.HttpContext.Authentication;
List<AuthenticationDescription> schemes = context.GetAuthenticationSchemes().ToList();
returnUrl = Url.Action("ExternalLoginCallback", new { returnUrl = returnUrl });
// start challenge and roundtrip the return URL
var props = new AuthenticationProperties
{
RedirectUri = returnUrl,
Items = { { "scheme", provider } }
};
//await HttpContext.Authentication.ChallengeAsync(provider, props);
return new ChallengeResult(provider, props);
}
In my opinion ,we shouldn't directly pass the username/password directly from other Idp to azure AD for authentication as a security implementation .And even Azure AD supports the Resource Owner Password Credentials Grant ,it's only available in native client. I suggest you keep the normal way and don't mix them .
I am using the next code to get the token for Azure AD authentication
errorMessage = "";
AuthenticationResult result = null;
var context = new AuthenticationContext(String.Format(CultureInfo.InvariantCulture, ConfigurationManager.AppSettings["login"], ConfigurationManager.AppSettings["tenantId"]),false);
ClientCredential clientCredential = new ClientCredential(ConfigurationManager.AppSettings["clientId"], ConfigurationManager.AppSettings["key"]);
try
{
result = context.AcquireToken(ConfigurationManager.AppSettings["apiEndpoint"], clientCredential);
}
catch (AdalException ex)
{
if (ex.ErrorCode == "temporarily_unavailable")
{
errorMessage = "Temporarily Unavailable";
return null;
}
else
{
errorMessage = "Unknown Error";
return null;
}
}
string token = result.AccessToken;
var credential = new TokenCloudCredentials(ConfigurationManager.AppSettings["subscriptionId"],token);
//string certificateString = ConfigurationManager.AppSettings["managementCertificate"];
//var cert = new X509Certificate2(Convert.FromBase64String(base64cer));
return credential;
After that I am doing the next to create a website in Azure
using (var computeClient = new WebSiteManagementClient(credentials))
{
var result = computeClient.WebSites.IsHostnameAvailable(websiteName);
if (result.IsAvailable)
{
await computeClient.WebSites.CreateAsync(WebSpaceNames.WestEuropeWebSpace, new WebSiteCreateParameters() {
Name= websiteName,
ServerFarm= ConfigurationManager.AppSettings["servicePlanName"]
});
}
else
{
return ResultCodes.ObjectNameAlreadyUsed;
}
}
But every time I execute that I got the following error:
ForbiddenError: The server failed to authenticate the request. Verify that the certificate is valid and is associated with this subscription.
I tried to import the management certificate as they said here:
https://www.simple-talk.com/cloud/security-and-compliance/windows-azure-management-certificates/
And also tried this one:
http://technetlibrary.com/change-windows-azure-subscription-azure-powershell/198
For importing management certificate.
Also I gave the application permissions to access management API.
Azure AD Authentication DOES NOT use the management certificate authentication.
There is a good documentation and code sample on MSDN on how to resolve your current issue.
Authenticating Service Management Requests
Looks like your application does not have permission to access the Azure API's.
Please use this link to get permissions.
After this please add permissions to access API in app permission or user permission.
Here is my scenario. I have an MVC 5 application that uses Owin as an authentication mechanism. The default template calls the SignInManager.PasswordSignInAsync in the Login action which I would like to overwrite to use LDAP to validate the user instead of looking into the database.
I am able to do the validation via:
PrincipalContext dc = new PrincipalContext(ContextType.Domain, "domain.com", "DC=domain,DC=com", "user_name", "password");
bool authenticated = dc.ValidateCredentials(userName, password);
Then I can retrieve the UserPrincipal using:
UserPrincipal user = UserPrincipal.FindByIdentity(dc, IdentityType.SamAccountName, userName);
However, I am stuck here and I am not sure how to continue with signing in the user. The goal is that after I sign in the user, I would have access to User.Identity including all the roles the user is in. Essentially, the app should behave as if it uses Windows Authentication, but the credentials are provided by the user on the Login page.
You would probably ask why not user Windows Authentication directly. The app will be accessed from the outside of the network, but the requirements are to use AD authentication and authorization. Hence my predicament.
Any suggestions are highly appreciated.
Thank you.
After many hours of research and trial and error, here is what I ended up doing:
AccountController.cs - Create the application user and sign in
ApplicationUser usr = new ApplicationUser() { UserName = model.Email };
bool auth = await UserManager.CheckPasswordAsync(usr, model.Password);
if (auth)
{
List claims = new List();
foreach (var group in Request.LogonUserIdentity.Groups)
{
string role = new SecurityIdentifier(group.Value).Translate(typeof(NTAccount)).Value;
string clean = role.Substring(role.IndexOf("\\") + 1, role.Length - (role.IndexOf("\\") + 1));
claims.Add(new Claim(ClaimTypes.Role, clean));
}
claims.Add(new Claim(ClaimTypes.NameIdentifier, model.Email));
claims.Add(new Claim(ClaimTypes.Name, model.Email));
ClaimsIdentity ci = new ClaimsIdentity(claims, DefaultAuthenticationTypes.ApplicationCookie);
AuthenticationManager.SignIn(new AuthenticationProperties()
{
AllowRefresh = true,
IsPersistent = false,
ExpiresUtc = DateTime.UtcNow.AddDays(7),
}, ci);
return RedirectToLocal(returnUrl);
}
else
{
ModelState.AddModelError("", "Invalid login credentials.");
return View(model);
}
IdentityConfig.cs (CheckPasswordAsync) - Authenticate against LDAP
public override async Task CheckPasswordAsync(ApplicationUser user, string password)
{
PrincipalContext dc = new PrincipalContext(ContextType.Domain, "domain", "DC=domain,DC=com", [user_name], [password]);
bool authenticated = dc.ValidateCredentials(user.UserName, password);
return authenticated;
}
Global.asax - if you are using the Anti Forgery Token in your login form
AntiForgeryConfig.UniqueClaimTypeIdentifier = ClaimTypes.NameIdentifier;
At this point, you will are logged in and can access the User.Identity object. You can also mark controllers and actions with [Authorize(Roles = "some_role"]
It turned out that it was easier than I thought, it is just that not much is really written on the topic (at least I could not find anything).
Also, this code presumes that you are running the app from a server which has access to the Domain Controller on your network. If you are on a DMZ server, you need to discuss this strategy with your network admin for other options.
I hope this saves you some time. I am also eager to hear what the community thinks of this. Maybe there is a better way of handling this situation. If so, please share it here.
Thanks.
Daniel D.
Windows (Phone) 8.1 has a Credential Locker called PasswordVault that you can use to store and encrypt credentials.
I saw from this question you are able to access or get the credentials via the Reference Manager on Windows 8. So, I am looking for something similar, but on the Windows Phone (8.1).
Question: How do you verify that the credentials inside the PaswordVault on the Windows Phone are actually encrypted?
Purpose: I don't like to take things on face value, I want to verify that the credentials are really encrypted.
Password vault is dedicated space for your credentials and it cannot be accesed from any other application.
There is code for storing credentials:
var vault = new Windows.Security.Credentials.PasswordVault();
vault.Add(new Windows.Security.Credentials.PasswordCredential("Your_App_Name", userName, password));
Here is method for retreiving credentials:
public static Windows.Security.Credentials.PasswordCredential GetCredentialFromLocker()
{
try
{
Windows.Security.Credentials.PasswordCredential credential = null;
var vault = new Windows.Security.Credentials.PasswordVault();
var credentialList = vault.FindAllByResource("Your_App_Name");
if (credentialList.Count > 0)
{
if (credentialList.Count == 1)
{
credential = credentialList[0];
}
}
return credential;
}
catch (Exception e)
{
return null;
}
}
Hope it will help.