/// <summary>
 /// Initializes a new <c>VssOAuthJwtBearerClientCredential</c> with the specified client identifier and audience. The
 /// credential will be used for the JWT Bearer Token Profile for Client Authentication as a client assertion.
 /// </summary>
 /// <param name="clientId">The client identifier issued by the authorization server</param>
 /// <param name="audience">The target audience for the bearer assertion. This is usually the authorization URL</param>
 /// <param name="signingCredentials">The signing credentials for proof of client identity</param>
 public VssOAuthJwtBearerClientCredential(
     String clientId,
     String audience,
     VssSigningCredentials signingCredentials)
     : this(clientId, new VssOAuthJwtBearerAssertion(clientId, clientId, audience, signingCredentials))
 {
 }
 /// <summary>
 /// Initializes a new <c>VssOAuthJwtBearerAssertion</c> with the specified issuer, subject, audience,
 /// and signing credentials for generating a bearer token.
 /// </summary>
 /// <param name="issuer">The iss claim for the bearer token</param>
 /// <param name="subject">The sub claim for the bearer token</param>
 /// <param name="audience">The aud claim for the bearer token</param>
 /// <param name="signingCredentials">The credentials used to sign the bearer token</param>
 public VssOAuthJwtBearerAssertion(
     String issuer,
     String subject,
     String audience,
     VssSigningCredentials signingCredentials)
     : this(issuer, subject, audience, null, signingCredentials)
 {
 }
Exemple #3
0
        public override VssCredentials GetVssCredentials(IHostContext context)
        {
            var clientId         = this.CredentialData.Data.GetValueOrDefault("clientId", null);
            var authorizationUrl = this.CredentialData.Data.GetValueOrDefault("authorizationUrl", null);

            // We expect the key to be in the machine store at this point. Configuration should have set all of
            // this up correctly so we can use the key to generate access tokens.
            var keyManager         = context.GetService <IRSAKeyManager>();
            var signingCredentials = VssSigningCredentials.Create(() => keyManager.GetKey());
            var clientCredential   = new VssOAuthJwtBearerClientCredential(clientId, authorizationUrl, signingCredentials);
            var agentCredential    = new VssOAuthCredential(new Uri(authorizationUrl, UriKind.Absolute), VssOAuthGrant.ClientCredentials, clientCredential);

            // Construct a credentials cache with a single OAuth credential for communication. The windows credential
            // is explicitly set to null to ensure we never do that negotiation.
            return(new VssCredentials(null, agentCredential, CredentialPromptType.DoNotPrompt));
        }
        /// <summary>
        /// Initializes a new <c>VssOAuthJwtBearerAssertion</c> with the specified issuer, subject, audience,
        /// and signing credentials for generating a bearer token.
        /// </summary>
        /// <param name="issuer">The iss claim for the bearer token</param>
        /// <param name="subject">The sub claim for the bearer token</param>
        /// <param name="audience">The aud claim for the bearer token</param>
        /// <param name="additionalClaims">An optional list of additional claims to provide with the bearer token</param>
        /// <param name="signingCredentials">The credentials used to sign the bearer token</param>
        public VssOAuthJwtBearerAssertion(
            String issuer,
            String subject,
            String audience,
            IList <Claim> additionalClaims,
            VssSigningCredentials signingCredentials)
        {
            m_issuer             = issuer;
            m_subject            = subject;
            m_audience           = audience;
            m_signingCredentials = signingCredentials;

            if (additionalClaims != null)
            {
                this.additionalClaims = new List <Claim>(additionalClaims);
            }
        }
Exemple #5
0
        internal static JWTAlgorithm ValidateSigningCredentials(VssSigningCredentials credentials, bool allowExpiredToken = false)
        {
            if (credentials == null)
            {
                return(JWTAlgorithm.None);
            }

            if (!credentials.CanSignData)
            {
                throw new InvalidCredentialsException(JwtResources.SigningTokenNoPrivateKey());
            }

            if (!allowExpiredToken && credentials.ValidTo.ToUniversalTime() < (DateTime.UtcNow - TimeSpan.FromMinutes(5)))
            {
                throw new InvalidCredentialsException(JwtResources.SigningTokenExpired());
            }

            return(credentials.SignatureAlgorithm);
        }
Exemple #6
0
        public override VssCredentials GetVssCredentials(IHostContext context)
        {
            var clientId         = this.CredentialData.Data.GetValueOrDefault("clientId", null);
            var authorizationUrl = this.CredentialData.Data.GetValueOrDefault("authorizationUrl", null);

            ArgUtil.NotNullOrEmpty(clientId, nameof(clientId));
            ArgUtil.NotNullOrEmpty(authorizationUrl, nameof(authorizationUrl));

            // For TFS, we need make sure the Schema/Host/Port component of the authorization url also match configuration url.
            // We can't do this for VSTS, since its SPS/TFS urls are different.
            var configStore = context.GetService <IConfigurationStore>();

            if (configStore.IsConfigured())
            {
                UriBuilder configServerUrl         = new UriBuilder(configStore.GetSettings().ServerUrl);
                UriBuilder authorizationUrlBuilder = new UriBuilder(authorizationUrl);
                if (!UrlUtil.IsHosted(configServerUrl.Uri.AbsoluteUri) &&
                    Uri.Compare(configServerUrl.Uri, authorizationUrlBuilder.Uri, UriComponents.SchemeAndServer, UriFormat.Unescaped, StringComparison.OrdinalIgnoreCase) != 0)
                {
                    authorizationUrlBuilder.Scheme = configServerUrl.Scheme;
                    authorizationUrlBuilder.Host   = configServerUrl.Host;
                    authorizationUrlBuilder.Port   = configServerUrl.Port;

                    var trace = context.GetTrace(nameof(OAuthCredential));
                    trace.Info($"Replace authorization url's scheme://host:port component with agent configure url's scheme://host:port: '{authorizationUrlBuilder.Uri.AbsoluteUri}'.");

                    authorizationUrl = authorizationUrlBuilder.Uri.AbsoluteUri;
                }
            }

            // We expect the key to be in the machine store at this point. Configuration should have set all of
            // this up correctly so we can use the key to generate access tokens.
            var keyManager         = context.GetService <IRSAKeyManager>();
            var signingCredentials = VssSigningCredentials.Create(() => keyManager.GetKey());
            var clientCredential   = new VssOAuthJwtBearerClientCredential(clientId, authorizationUrl, signingCredentials);
            var agentCredential    = new VssOAuthCredential(new Uri(authorizationUrl, UriKind.Absolute), VssOAuthGrant.ClientCredentials, clientCredential);

            // Construct a credentials cache with a single OAuth credential for communication. The windows credential
            // is explicitly set to null to ensure we never do that negotiation.
            return(new VssCredentials(null, agentCredential, CredentialPromptType.DoNotPrompt));
        }
        private static JWTHeader GetHeader(VssSigningCredentials credentials, bool allowExpired)
        {
            //note credentials are allowed to be null here, see ValidateSigningCredentials
            JWTHeader header = new JWTHeader();

            JWTAlgorithm alg = JsonWebTokenUtilities.ValidateSigningCredentials(credentials, allowExpired);

            header.Algorithm = alg;

            if (alg != JWTAlgorithm.None)
            {
                // Some signing credentials may need to set headers for the JWT
                var jwtHeaderProvider = credentials as IJsonWebTokenHeaderProvider;
                if (jwtHeaderProvider != null)
                {
                    jwtHeaderProvider.SetHeaders(header);
                }
            }

            return(header);
        }
        public override VssCredentials GetVssCredentials(IHostContext context)
        {
            var clientId         = this.CredentialData.Data.GetValueOrDefault("clientId", null);
            var authorizationUrl = this.CredentialData.Data.GetValueOrDefault("authorizationUrl", null);

            // For back compat with .credential file that doesn't has 'oauthEndpointUrl' section
            var oauthEndpointUrl = this.CredentialData.Data.GetValueOrDefault("oauthEndpointUrl", authorizationUrl);

            ArgUtil.NotNullOrEmpty(clientId, nameof(clientId));
            ArgUtil.NotNullOrEmpty(authorizationUrl, nameof(authorizationUrl));

            // We expect the key to be in the machine store at this point. Configuration should have set all of
            // this up correctly so we can use the key to generate access tokens.
            var keyManager         = context.GetService <IRSAKeyManager>();
            var signingCredentials = VssSigningCredentials.Create(() => keyManager.GetKey(), StringUtil.ConvertToBoolean(CredentialData.Data.GetValueOrDefault("requireFipsCryptography"), false));
            var clientCredential   = new VssOAuthJwtBearerClientCredential(clientId, authorizationUrl, signingCredentials);
            var agentCredential    = new VssOAuthCredential(new Uri(oauthEndpointUrl, UriKind.Absolute), VssOAuthGrant.ClientCredentials, clientCredential);

            // Construct a credentials cache with a single OAuth credential for communication. The windows credential
            // is explicitly set to null to ensure we never do that negotiation.
            return(new VssCredentials(agentCredential, CredentialPromptType.DoNotPrompt));
        }
        private static JsonWebToken Create(string issuer, string audience, DateTime validFrom, DateTime validTo, DateTime issuedAt, IEnumerable <Claim> additionalClaims, JsonWebToken actor, string actorToken, VssSigningCredentials credentials, bool allowExpiredCertificate)
        {
            ArgumentUtility.CheckStringForNullOrEmpty(issuer, nameof(issuer));
            ArgumentUtility.CheckStringForNullOrEmpty(audience, nameof(audience)); // Audience isn't actually required...

            validFrom = validFrom == default(DateTime) ? DateTime.UtcNow : validFrom.ToUniversalTime();
            validTo   = validTo == default(DateTime) ? DateTime.UtcNow + TimeSpan.FromSeconds(DefaultLifetime) : validTo.ToUniversalTime();
            //issuedAt is optional, and breaks certain scenarios if it is present, and breaks others if it is not.
            //so only include it if it is explicitly set.
            issuedAt = issuedAt == default(DateTime) ? default(DateTime) : issuedAt.ToUniversalTime();

            JWTHeader  header  = GetHeader(credentials, allowExpiredCertificate);
            JWTPayload payload = new JWTPayload(additionalClaims)
            {
                Issuer = issuer, Audience = audience, ValidFrom = validFrom, ValidTo = validTo, IssuedAt = issuedAt
            };

            if (actor != null)
            {
                payload.Actor = actor;
            }
            else if (actorToken != null)
            {
                payload.ActorToken = actorToken;
            }

            byte[] signature = GetSignature(header, payload, header.Algorithm, credentials);

            return(new JsonWebToken(header, payload, signature));
        }
        public static JsonWebToken Create(string issuer, string audience, DateTime validFrom, DateTime validTo, DateTime issuedAt, IEnumerable <Claim> additionalClaims, VssSigningCredentials credentials)
        {
            //if you are calling this version claims can't be null
            ArgumentUtility.CheckForNull(additionalClaims, nameof(additionalClaims));

            return(Create(issuer, audience, validFrom, validTo, issuedAt, additionalClaims, null, null, credentials, allowExpiredCertificate: false));
        }
        //We chose factory methods for creation, because creation
        //generally involves signing the token, which is a bigger operation
        //than just using a constructor implies

        //this method is used to instantiate the "self-signed" token for obtaining
        //the access token
        public static JsonWebToken Create(string issuer, string audience, DateTime validFrom, DateTime validTo, VssSigningCredentials credentials)
        {
            return(Create(issuer, audience, validFrom, validTo, default(DateTime), null, null, null, credentials, allowExpiredCertificate: false));
        }
        //if we alread have the alg, we assume that the creds have been validated already,
        //to save the expense of validating twice in the create function...
        private static byte[] GetSignature(JWTHeader header, JWTPayload payload, JWTAlgorithm alg, VssSigningCredentials signingCredentials)
        {
            if (alg == JWTAlgorithm.None)
            {
                return(null);
            }

            ArgumentUtility.CheckForNull(header, nameof(header));
            ArgumentUtility.CheckForNull(payload, nameof(payload));

            string encoding = string.Format("{0}.{1}", header.JsonEncode(), payload.JsonEncode());

            byte[] bytes = Encoding.UTF8.GetBytes(encoding);

            switch (alg)
            {
            case JWTAlgorithm.HS256:
            case JWTAlgorithm.RS256:
                return(signingCredentials.SignData(bytes));

            default:
                throw new InvalidOperationException();
            }
        }
        private static byte[] GetSignature(JWTHeader header, JWTPayload payload, VssSigningCredentials credentials, bool allowExpired)
        {
            JWTAlgorithm alg = JsonWebTokenUtilities.ValidateSigningCredentials(credentials, allowExpired);

            return(GetSignature(header, payload, alg, credentials));
        }
Exemple #14
0
        private async Task <VssCredentials> GetNewOAuthAuthorizationSetting(CancellationToken token)
        {
            Trace.Info("Start checking oauth authorization url update.");
            while (true)
            {
                var backoff = BackoffTimerHelper.GetRandomBackoff(TimeSpan.FromMinutes(30), TimeSpan.FromMinutes(45));
                await HostContext.Delay(backoff, token);

                try
                {
                    var migratedAuthorizationUrl = await _runnerServer.GetRunnerAuthUrlAsync(_settings.PoolId, _settings.AgentId);

                    if (!string.IsNullOrEmpty(migratedAuthorizationUrl))
                    {
                        var credData = _configStore.GetCredentials();
                        var clientId = credData.Data.GetValueOrDefault("clientId", null);
                        var currentAuthorizationUrl = credData.Data.GetValueOrDefault("authorizationUrl", null);
                        Trace.Info($"Current authorization url: {currentAuthorizationUrl}, new authorization url: {migratedAuthorizationUrl}");

                        if (string.Equals(currentAuthorizationUrl, migratedAuthorizationUrl, StringComparison.OrdinalIgnoreCase))
                        {
                            // We don't need to update credentials.
                            Trace.Info("No needs to update authorization url");
                            await Task.Delay(TimeSpan.FromMilliseconds(-1), token);
                        }

                        var keyManager         = HostContext.GetService <IRSAKeyManager>();
                        var signingCredentials = VssSigningCredentials.Create(() => keyManager.GetKey());

                        var migratedClientCredential = new VssOAuthJwtBearerClientCredential(clientId, migratedAuthorizationUrl, signingCredentials);
                        var migratedRunnerCredential = new VssOAuthCredential(new Uri(migratedAuthorizationUrl, UriKind.Absolute), VssOAuthGrant.ClientCredentials, migratedClientCredential);

                        Trace.Info("Try connect service with Token Service OAuth endpoint.");
                        var runnerServer = HostContext.CreateService <IRunnerServer>();
                        await runnerServer.ConnectAsync(new Uri(_settings.ServerUrl), migratedRunnerCredential);

                        await runnerServer.GetAgentPoolsAsync();

                        Trace.Info($"Successfully connected service with new authorization url.");

                        var migratedCredData = new CredentialData
                        {
                            Scheme = Constants.Configuration.OAuth,
                            Data   =
                            {
                                { "clientId",         clientId                 },
                                { "authorizationUrl", migratedAuthorizationUrl },
                                { "oauthEndpointUrl", migratedAuthorizationUrl },
                            },
                        };

                        _configStore.SaveMigratedCredential(migratedCredData);
                        return(migratedRunnerCredential);
                    }
                    else
                    {
                        Trace.Verbose("No authorization url updates");
                    }
                }
                catch (Exception ex)
                {
                    Trace.Error("Fail to get/test new authorization url.");
                    Trace.Error(ex);

                    try
                    {
                        await _runnerServer.ReportRunnerAuthUrlErrorAsync(_settings.PoolId, _settings.AgentId, ex.ToString());
                    }
                    catch (Exception e)
                    {
                        // best effort
                        Trace.Error("Fail to report the migration error");
                        Trace.Error(e);
                    }
                }
            }
        }