Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

non https VssConnection with access token - disable required secure connection?

Tags:

tfs

using TFS in our internal network, want to programatically check in changes with an access token, but get this: InvalidOperationException Basic authentication requires a secure connection to the server. Is there a way to turn off requiring secure connection?

var basicCreds = new VssBasicCredential(string.Empty, BuildUnitTestConstants.AccessToken);
var connection = new VssConnection(new Uri(BuildUnitTestConstants.ProjectUri), basicCreds);
var sourceControlServer = connection.GetClient<TfvcHttpClient>();
like image 406
dan Avatar asked Sep 16 '17 03:09

dan


3 Answers

Well, it is possible, albeit not recommended; I needed it as well, because internal IT department would not install TFS with HTTPS (sad story). Also, for testing scenarios it can come quite handy.

As always YMMV and I'm not responsible for what happens when you use it when you shouldn't ;-) You have been warned.

For one you could simply not use the .NET client API, but directly use HttpClient and manually put the PAT in the URL to access the REST API, e.g.:

 http://<WHATEVER>:<BASE64PAT>@<instance>/_apis/...

(Hence that the tfx-cli works nicely with PATs and non-HTTPS TFS instances, most likely because it does just that internally, not using the .NET client API of course - it is a node.js thing.)

If you want to stay with the .NET client API, you can create your own credentials class like so:

using System;
using System.Linq;
using System.Net;
using Microsoft.VisualStudio.Services.Common;

namespace Utilities
{
    /// <summary>
    /// Same as VssBasicCredential, but doesn't throw when URL is a non SSL, i.e. http, URL.
    /// </summary>
    /// <inheritdoc cref="FederatedCredential"/>
    internal sealed class PatCredentials : FederatedCredential
    {
        public PatCredentials()
            : this((VssBasicToken)null)
        {
        }

        public PatCredentials(string userName, string password)
            : this(new VssBasicToken(new NetworkCredential(userName, password)))
        {
        }

        public PatCredentials(ICredentials initialToken)
            : this(new VssBasicToken(initialToken))
        {
        }

        public PatCredentials(VssBasicToken initialToken)
            : base(initialToken)
        {
        }

        public override VssCredentialsType CredentialType => VssCredentialsType.Basic;

        public override bool IsAuthenticationChallenge(IHttpResponse webResponse)
        {
            if (webResponse == null ||
                webResponse.StatusCode != HttpStatusCode.Found &&
                webResponse.StatusCode != HttpStatusCode.Found &&
                webResponse.StatusCode != HttpStatusCode.Unauthorized)
            {
                return false;
            }

            return webResponse.Headers.GetValues("WWW-Authenticate").Any(x => x.StartsWith("Basic", StringComparison.OrdinalIgnoreCase));
        }

        protected override IssuedTokenProvider OnCreateTokenProvider(Uri serverUrl, IHttpResponse response)
        {
            return new BasicAuthTokenProvider(this, serverUrl);
        }

        private sealed class BasicAuthTokenProvider : IssuedTokenProvider
        {
            public BasicAuthTokenProvider(IssuedTokenCredential credential, Uri serverUrl)
                : base(credential, serverUrl, serverUrl)
            {
            }
            protected override string AuthenticationScheme => "Basic";
            public override bool GetTokenIsInteractive => this.CurrentToken == null;
        }
    }
}

Then create your VssCredentials using this class:

var credentials = new PatCredentials("", personalAccessToken);
var connection = new VssConnection(serverUrl, credentials);

(Shameless plug I use it in my TfsInfoService).

like image 105
Christian.K Avatar answered Nov 14 '22 22:11

Christian.K


Nowadays you don't need to use the workaround provided by @Christian.K

Simply set the following env variable:

VSS_ALLOW_UNSAFE_BASICAUTH=true

Source: code of Microsoft.VisualStudio.Services.Common.VssBasicCredential:

    protected override IssuedTokenProvider OnCreateTokenProvider(
      Uri serverUrl,
      IHttpResponse response)
    {
      bool result;
      if (serverUrl.Scheme != "https" && (!bool.TryParse(Environment.GetEnvironmentVariable("VSS_ALLOW_UNSAFE_BASICAUTH") ?? "false", out result) || !result))
        throw new InvalidOperationException(CommonResources.BasicAuthenticationRequiresSsl());
      return (IssuedTokenProvider) new BasicAuthTokenProvider(this, serverUrl);
    }

To set the environment variable programatically:

Environment.SetEnvironmentVariable("VSS_ALLOW_UNSAFE_BASICAUTH", "true")
like image 9
Mugen Avatar answered Nov 14 '22 20:11

Mugen


No this is not possible. The problem of enabling PAT over unsecured connections is that anyone could intercept the token easily and would be able to use it for their own purposes.

Either enable SSL on the TSF instance, this is highly recommended. Or use Windows Authentication to use a secure form of authentication to TFS over an unsecured channel.

like image 3
jessehouwing Avatar answered Nov 14 '22 20:11

jessehouwing