I'm trying to access my TFS server programmatically from outside the domain where the server is installed. A basic test program would look like this :
class Program
{
static void Main(string[] args)
{
Uri tfsUri = new Uri("<serverUri>");
TfsConfigurationServer _ConfigurationServer = TfsConfigurationServerFactory.GetConfigurationServer(tfsUri);
CatalogNode projectCollectionCatalog = _ConfigurationServer.CatalogNode.QueryChildren(new[] { CatalogResourceTypes.ProjectCollection }, false, CatalogQueryOptions.None)[0]; // actual connection tries to happen here
}
}
Another version, with credentials forced :
class Program
{
static void Main(string[] args)
{
Uri tfsUri = new Uri("<serverURI>");
TfsConfigurationServer _ConfigurationServer = new TfsConfigurationServer(tfsUri, new NetworkCredential("<DifferentKindOfUsernames>", "<Password>"));
CatalogNode projectCollectionCatalog = _ConfigurationServer.CatalogNode.QueryChildren(new[] { CatalogResourceTypes.ProjectCollection }, false, CatalogQueryOptions.None)[0];
}
}
Another version with a mix of both previous version :
public class ConnectByImplementingCredentialsProvider : ICredentialsProvider
{
public ICredentials GetCredentials(Uri uri, ICredentials iCredentials)
{
return new NetworkCredential("<DifferentKindOfUsernames>", "<Password>", "<DomainOrNot>");
}
public void NotifyCredentialsAuthenticated(Uri uri)
{
throw new ApplicationException("Unable to authenticate");
}
}
class Program
{
static void Main(string[] args)
{
string _myUri = @"<serverUri>";
ConnectByImplementingCredentialsProvider connect = new ConnectByImplementingCredentialsProvider();
ICredentials iCred = new NetworkCredential("<DifferentKindOfUsernames>", "<Password>", "<DomainOrNot>");
connect.GetCredentials(new Uri(_myUri), iCred);
TfsConfigurationServer configurationServer =
TfsConfigurationServerFactory.GetConfigurationServer(new Uri(_myUri), connect);
configurationServer.EnsureAuthenticated();
}
}
And a version with an active directory Impersonator :
class Program
{
static void Main(string[] args)
{
using (new Impersonator("<DifferentKindOfUsernames>", "<DomainOrNot>", "<Password>"))
{
Uri tfsUri = new Uri("<serverUri>");
TfsConfigurationServer _ConfigurationServer = TfsConfigurationServerFactory.GetConfigurationServer(tfsUri);
CatalogNode projectCollectionCatalog = _ConfigurationServer.CatalogNode.QueryChildren(new[] { CatalogResourceTypes.ProjectCollection }, false, CatalogQueryOptions.None)[0]; // actual connection tries to happen here
}
}
}
serverURI
being in the form of http://<servername>:8080/tfs
or http://<serverip>:8080/tfs
(both tested, with hosts file up to date) which is what is set as Notification URL on the TFS Server. This program works inside the domain.
DifferentKindOfUsernames
being anything from 'DOMAIN\Username', 'LocallyDuplicatedUsername', 'LOCALMACHINE\Username' with the appropriate password, password being the same in the domain and on the machine.
This simple access won't work outside of the domain , and I have this error :
TF30063: You are not authorized to access <serverUri>
translated in a web context (using the same process in an asp.net website), it is a 401 error :
The remote server returned an error: (401) Unauthorized.
even if (things tested so far) :
BackConnectionNames
registry key with the domain outsider machine name and ip like described here.I have tested the access to the serverURI from a browser. The uri works and I have access to the TFS Collection if I give the credentials with DomainName\User + Password. I tested this before any of the modifications I described before. I wonder what could be the difference between the programmatic access and the browser access besides all the things I have tested so far.
You're not passing credentials to build the connection. This means that you're using your currently logged in credentials from the host outside of the domain. I'm not an expert on Windows Authentication, but I think that this can, in certain circumstances, work transparently (if the username and password are identical) but it appears to depend on the NTLM version being used, the client and server operating systems, trust relationships and security zones, the IIS configuration and perhaps the phase of the moon.
In other words, you probably want to pass the credentials to the connection:
TfsConfigurationServer _ConfigurationServer = new TfsConfigurationServer(uri, new NetworkCredential("username", "password", "DOMAIN"));
Note that it's strongly recommended to enable SSL/TLS if you're connecting to your server over an untrusted (public) network.
(I corrected this to use the three-arg constructor for NetworkCredential -- my mistake. As you note, if you put DOMAIN\username
in the username argument to NetworkCredential, it will treat it as \DOMAIN\username
instead of DOMAIN\username
. This, I suppose, it why nobody lets me write C# code.)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With