/// <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) { }
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); } }
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); }
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)); }
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); } } } }