public void FromNonGoogleSigned() { var signedToken = SignedToken <JsonWebSignature.Header, JsonWebSignature.Payload> .FromSignedToken(FakeCertificateCache.JwtNonGoogleSigned); // Just a small test that everything got correctly unpacked. Assert.DoesNotContain("google.com", signedToken.Payload.Issuer); }
public async Task FetchesOidcToken() { // A little bit after the tokens returned from OidcTokenFakes were issued. var clock = new MockClock(new DateTime(2020, 5, 13, 15, 0, 0, 0, DateTimeKind.Utc)); var messageHandler = new OidcTokenResponseSuccessMessageHandler(); var initializer = new ServiceAccountCredential.Initializer("MyId", "http://will.be.ignored") { Clock = clock, ProjectId = "a_project_id", HttpClientFactory = new MockHttpClientFactory(messageHandler) }; var credential = new ServiceAccountCredential(initializer.FromPrivateKey(PrivateKey)); // The fake Oidc server returns valid tokens (expired in the real world for safety) // but with a set audience that lets us know if the token was refreshed or not. var oidcToken = await credential.GetOidcTokenAsync(OidcTokenOptions.FromTargetAudience("will.be.ignored")); var signedToken = SignedToken <Header, Payload> .FromSignedToken(await oidcToken.GetAccessTokenAsync()); Assert.Equal("https://first_call.test", signedToken.Payload.Audience); // Move the clock some but not enough that the token expires. clock.UtcNow = clock.UtcNow.AddMinutes(20); signedToken = SignedToken <Header, Payload> .FromSignedToken(await oidcToken.GetAccessTokenAsync()); Assert.Equal("https://first_call.test", signedToken.Payload.Audience); // Only the first call should have resulted in a request. The second time the token hadn't expired. Assert.Equal(1, messageHandler.Calls); }
public async Task RefreshesOidcToken() { // A little bit after the tokens returned from OidcTokenFakes were issued. var clock = new MockClock(new DateTime(2020, 5, 13, 15, 0, 0, 0, DateTimeKind.Utc)); var messageHandler = new OidcTokenResponseSuccessMessageHandler(); var initializer = new ServiceAccountCredential.Initializer("MyId", "http://will.be.ignored") { Clock = clock, ProjectId = "a_project_id", HttpClientFactory = new MockHttpClientFactory(messageHandler) }; var credential = new ServiceAccountCredential(initializer.FromPrivateKey(PrivateKey)); var oidcToken = await credential.GetOidcTokenAsync(OidcTokenOptions.FromTargetAudience("audience")); var signedToken = SignedToken <Header, Payload> .FromSignedToken(await oidcToken.GetAccessTokenAsync()); Assert.Equal("https://first_call.test", signedToken.Payload.Audience); // Move the clock so that the token expires. clock.UtcNow = clock.UtcNow.AddHours(2); signedToken = SignedToken <Header, Payload> .FromSignedToken(await oidcToken.GetAccessTokenAsync()); Assert.Equal("https://subsequent_calls.test", signedToken.Payload.Audience); // Two calls, because the second time we tried to get the token, the first one had expired. Assert.Equal(2, messageHandler.Calls); }
public async Task RefreshesOidcToken() { // A little bit after the tokens returned from OidcTokenFakes were issued. var clock = new MockClock(new DateTime(2020, 5, 21, 9, 20, 0, 0, DateTimeKind.Utc)); var messageHandler = new OidcComputeSuccessMessageHandler(); var initializer = new ComputeCredential.Initializer("http://will.be.ignored", "http://will.be.ignored") { Clock = clock, HttpClientFactory = new MockHttpClientFactory(messageHandler) }; var credential = new ComputeCredential(initializer); // The fake Oidc server returns valid tokens (expired in the real world for safty) // but with a set audience that lets us know if the token was refreshed or not. var oidcToken = await credential.GetOidcTokenAsync(OidcTokenOptions.FromTargetAudience("will.be.ignored")); var signedToken = SignedToken <Header, Payload> .FromSignedToken(await oidcToken.GetAccessTokenAsync()); Assert.Equal("https://first_call.test", signedToken.Payload.Audience); // Move the clock so that the token expires. clock.UtcNow = clock.UtcNow.AddHours(2); signedToken = SignedToken <Header, Payload> .FromSignedToken(await oidcToken.GetAccessTokenAsync()); Assert.Equal("https://subsequent_calls.test", signedToken.Payload.Audience); // Two calls, because the second time we tried to get the token, the first one had expired. Assert.Equal(2, messageHandler.Calls); }
public void FromGoogleSigned_ToJsonWebSignatureTypes() { // This works, we just don't get the specific fields that we can get with // GoogleJsonWebSignature.Header and GoogleJsonWebSignature.Payload. var signedToken = SignedToken <JsonWebSignature.Header, JsonWebSignature.Payload> .FromSignedToken(FakeCertificateCache.JwtGoogleSigned); // Just a small test that everything got correctly unpacked. Assert.Contains("google.com", signedToken.Payload.Issuer); }
public void FromGoogleSigned() { var signedToken = SignedToken <GoogleJsonWebSignature.Header, GoogleJsonWebSignature.Payload> .FromSignedToken(FakeCertificateCache.JwtGoogleSigned); // Just a small test that everything got correctly unpacked. Assert.Contains("google.com", signedToken.Payload.Issuer); // And that we have some Google specific info on the payload. Assert.NotNull(signedToken.Payload.HostedDomain); }
internal virtual void Validate(SignedToken signedToken, RevocationData data) { if (data == null) { throw new ArgumentException("data cannot be null"); } if (!revocationInfo.ContainsKey(signedToken)) { throw new ArgumentException(signedToken + " must be a key of revocationInfo"); } revocationInfo.Put(signedToken, data); }
internal virtual void AddNotYetVerifiedToken(SignedToken signedToken) { if (!revocationInfo.ContainsKey(signedToken)) { LOG.Info("New token to validate " + signedToken + " hashCode " + signedToken.GetHashCode ()); revocationInfo.Put(signedToken, null); if (signedToken is CRLToken) { neededCRL.AddItem(((CRLToken)signedToken).GetX509crl()); } else { if (signedToken is OCSPRespToken) { neededOCSPResp.AddItem(((OCSPRespToken)signedToken).GetOcspResp()); } else { if (signedToken is CertificateToken) { bool found = false; CertificateAndContext newCert = ((CertificateToken)signedToken).GetCertificateAndContext (); foreach (CertificateAndContext c in neededCertificates) { if (c.GetCertificate().Equals(newCert.GetCertificate())) { found = true; break; } } if (!found) { neededCertificates.AddItem(newCert); } } } } } else { LOG.Info("Token was already in list " + signedToken); } }
public void FromNonGoogleSigned_ToGoogleJsonWebSignatureTypes() { // This works, because JSON deserialization will just leave unassigned // any of the Google specific fields since they are not present on the JSON. // Validation of this token as a Google signed token won't work though. var signedToken = SignedToken <GoogleJsonWebSignature.Header, GoogleJsonWebSignature.Payload> .FromSignedToken(FakeCertificateCache.JwtNonGoogleSigned); // Just a small test that everything got correctly unpacked. Assert.DoesNotContain("google.com", signedToken.Payload.Issuer); // But we don't have info for any of the specific Google fields. Assert.Null(signedToken.Payload.Scope); Assert.Null(signedToken.Payload.Prn); Assert.Null(signedToken.Payload.HostedDomain); Assert.Null(signedToken.Payload.Email); Assert.Null(signedToken.Payload.Name); Assert.Null(signedToken.Payload.GivenName); Assert.Null(signedToken.Payload.FamilyName); Assert.Null(signedToken.Payload.Picture); Assert.Null(signedToken.Payload.Locale); }
public async Task FromComputeCredential_FetchesOidcToken() { // A little bit after the tokens returned from OidcTokenFakes were issued. var clock = new MockClock(new DateTime(2020, 5, 21, 9, 20, 0, 0, DateTimeKind.Utc)); var messageHandler = new OidcComputeSuccessMessageHandler(); var initializer = new ComputeCredential.Initializer("http://will.be.ignored", "http://will.be.ignored") { Clock = clock, HttpClientFactory = new MockHttpClientFactory(messageHandler) }; var computeCredential = new ComputeCredential(initializer); var googleCredential = GoogleCredential.FromComputeCredential(computeCredential); // The fake Oidc server returns valid tokens (expired in the real world for safty) // but with a set audience that lets us know if the token was refreshed or not. var oidcToken = await googleCredential.GetOidcTokenAsync(OidcTokenOptions.FromTargetAudience("will.be.ignored")); var signedToken = SignedToken <Header, Payload> .FromSignedToken(await oidcToken.GetAccessTokenAsync()); Assert.Equal("https://first_call.test", signedToken.Payload.Audience); }
public void FromSignedToken_ThrowsIfEmpty() { Assert.Throws <ArgumentException>( () => SignedToken <JsonWebSignature.Header, JsonWebSignature.Payload> .FromSignedToken(string.Empty)); }
/// <summary>Build the validation context for the specific date</summary> /// <param name="validationDate"></param> /// <param name="optionalSource"></param> /// <exception cref="System.IO.IOException"></exception> public virtual void Validate(DateTime validationDate, CertificateSource optionalSource , ICrlSource optionalCRLSource, IOcspSource optionalOCPSSource) { int previousSize = revocationInfo.Count; int previousVerified = VerifiedTokenCount(); SignedToken signedToken = GetOneNotYetVerifiedToken(); if (signedToken != null) { CertificateSource otherSource = optionalSource; if (signedToken != null) { otherSource = new CompositeCertificateSource(signedToken.GetWrappedCertificateSource (), optionalSource); } CertificateAndContext issuer = GetIssuerCertificate(signedToken, otherSource, validationDate ); RevocationData data = null; if (issuer == null) { LOG.Warn("Don't found any issuer for token " + signedToken); data = new RevocationData(signedToken); } else { AddNotYetVerifiedToken(new CertificateToken(issuer)); if (issuer.GetCertificate().SubjectDN.Equals(issuer.GetCertificate ().IssuerDN)) { SignedToken trustedToken = new CertificateToken(issuer); RevocationData noNeedToValidate = new RevocationData(); // noNeedToValidate.setRevocationData(CertificateSourceType.TRUSTED_LIST); Validate(trustedToken, noNeedToValidate); } if (issuer.GetCertificateSource() == CertificateSourceType.TRUSTED_LIST) { SignedToken trustedToken = new CertificateToken(issuer); RevocationData noNeedToValidate = new RevocationData(); noNeedToValidate.SetRevocationData(CertificateSourceType.TRUSTED_LIST); Validate(trustedToken, noNeedToValidate); } if (signedToken is CertificateToken) { CertificateToken ct = (CertificateToken)signedToken; CertificateStatus status = GetCertificateValidity(ct.GetCertificateAndContext(), issuer, validationDate, optionalCRLSource, optionalOCPSSource); data = new RevocationData(signedToken); if (status != null) { data.SetRevocationData(status.StatusSource); if (status.StatusSource is X509Crl) { AddNotYetVerifiedToken(new CRLToken((X509Crl)status.StatusSource)); } else { if (status.StatusSource is BasicOcspResp) { AddNotYetVerifiedToken(new OCSPRespToken((BasicOcspResp)status.StatusSource)); } } } else { LOG.Warn("No status for " + signedToken); } } else { if (signedToken is CRLToken || signedToken is OCSPRespToken || signedToken is TimestampToken) { data = new RevocationData(signedToken); data.SetRevocationData(issuer); } else { throw new RuntimeException("Not supported token type " + signedToken.GetType().Name ); } } } Validate(signedToken, data); LOG.Info(this.ToString()); int newSize = revocationInfo.Count; int newVerified = VerifiedTokenCount(); if (newSize != previousSize || newVerified != previousVerified) { Validate(validationDate, otherSource, optionalCRLSource, optionalOCPSSource); } } }
public void FromSignedToken_ThrowsIfNull() { Assert.Throws <ArgumentNullException>( () => SignedToken <JsonWebSignature.Header, JsonWebSignature.Payload> .FromSignedToken(null)); }
/// <summary> /// Asynchronously parses a <see cref="TokenResponse"/> instance from the specified <see cref="HttpResponseMessage"/>. /// </summary> /// <param name="response">The http response from which to parse the token.</param> /// <param name="clock">The clock used to set the <see cref="Issued"/> value of the token.</param> /// <param name="logger">The logger used to output messages incase of error.</param> /// <exception cref="TokenResponseException"> /// The response was not successful or there is an error parsing the response into valid <see cref="TokenResponse"/> instance. /// </exception> /// <returns> /// A task containing the <see cref="TokenResponse"/> parsed form the response message. /// </returns> public static async Task <TokenResponse> FromHttpResponseAsync(HttpResponseMessage response, IClock clock, ILogger logger) { var content = await response.Content.ReadAsStringAsync().ConfigureAwait(false); var typeName = ""; try { if (!response.IsSuccessStatusCode) { typeName = nameof(TokenErrorResponse); var error = NewtonsoftJsonSerializer.Instance.Deserialize <TokenErrorResponse>(content); throw new TokenResponseException(error, response.StatusCode); } TokenResponse newToken; // GCE's metadata server identity endpoint doesn't return a TokenResponse but the raw // id_token, so we build a TokenResponse from that. if (response.RequestMessage?.RequestUri?.AbsoluteUri.StartsWith(GoogleAuthConsts.ComputeOidcTokenUrl) == true) { newToken = new TokenResponse { IdToken = content }; } else { typeName = nameof(TokenResponse); newToken = NewtonsoftJsonSerializer.Instance.Deserialize <TokenResponse>(content); } // We make some modifications to the token before returning, to guarantee consistency // for our code across endpoint usage. // We should set issuance ourselves. newToken.IssuedUtc = clock.UtcNow; // If no access token was specified, then we're probably receiving // and OIDC token for IAP. The IdToken is used for access in that case. newToken.AccessToken ??= newToken.IdToken; // If no expiry is specified, maybe the IdToken has it specified. // We can try and get it from there. if (newToken.ExpiresInSeconds is null && newToken.IdToken != null) { // Unpack the IdToken. var idToken = SignedToken <JsonWebSignature.Header, JsonWebSignature.Payload> .FromSignedToken(newToken.IdToken); // If no expiry was specified in the ID token, there's nothing we can do. if (idToken.Payload.ExpirationTimeSeconds.HasValue) { var expiration = UnixEpoch.AddSeconds(idToken.Payload.ExpirationTimeSeconds.Value); newToken.ExpiresInSeconds = (long)(expiration - newToken.IssuedUtc).TotalSeconds; } } return(newToken); } catch (Newtonsoft.Json.JsonException ex) { logger.Error(ex, $"Exception was caught when deserializing {typeName}. Content is: {content}"); throw new TokenResponseException(new TokenErrorResponse { Error = "Server response does not contain a JSON object. Status code is: " + response.StatusCode }, response.StatusCode); } }
/// <summary>The default constructor for RevocationData.</summary> /// <remarks>The default constructor for RevocationData.</remarks> /// <param name="signedToken"></param> public RevocationData(SignedToken signedToken) { this.targetToken = signedToken; }
public void FromSignedToken_ThrowsIfIncorrectPartCount(string jwt) { Assert.Throws <InvalidJwtException>( () => SignedToken <JsonWebSignature.Header, JsonWebSignature.Payload> .FromSignedToken(jwt)); }
public void FromSignedToken_ThrowsIfNotValidJson() { Assert.Throws <JsonReaderException>( () => SignedToken <JsonWebSignature.Header, JsonWebSignature.Payload> .FromSignedToken( "YmFzZV82NF9oZWFkZXJfbm9fanNvbg==.YmFzZV82NF9wYXlsb2FkX25vX2pzb24=.c2lnbmF0dXJl")); }
/// <param name="signedToken"></param> /// <param name="optionalSource"></param> /// <param name="validationDate"></param> /// <returns></returns> /// <exception cref="System.IO.IOException">An error occurs when accessing the CertificateSource /// </exception> internal virtual CertificateAndContext GetIssuerCertificate(SignedToken signedToken , CertificateSource optionalSource, DateTime validationDate) { if (signedToken.GetSignerSubjectName() == null) { return(null); } IList <CertificateAndContext> list = new CompositeCertificateSource(trustedListCertificatesSource , optionalSource).GetCertificateBySubjectName(signedToken.GetSignerSubjectName() ); if (list != null) { foreach (CertificateAndContext cert in list) { LOG.Info(cert.ToString()); if (validationDate != null) { try { cert.GetCertificate().CheckValidity(validationDate); } catch (CertificateExpiredException) { LOG.Info("Was expired"); continue; } catch (CertificateNotYetValidException) { LOG.Info("Was not yet valid"); continue; } if (cert.GetCertificateSource() == CertificateSourceType.TRUSTED_LIST && cert.GetContext () != null) { ServiceInfo info = (ServiceInfo)cert.GetContext(); if (info.GetStatusStartingDateAtReferenceTime() != null && validationDate.CompareTo( //jbonilla Before info.GetStatusStartingDateAtReferenceTime()) < 0) { LOG.Info("Was not valid in the TSL"); continue; } else { if (info.GetStatusEndingDateAtReferenceTime() != null && validationDate.CompareTo(info //jbonilla After .GetStatusEndingDateAtReferenceTime()) > 0) { LOG.Info("Was not valid in the TSL"); continue; } } } } if (signedToken.IsSignedBy(cert.GetCertificate())) { return(cert); } } } return(null); }
public void FromSignedToken_ThrowsIfNotBase64() { Assert.Throws <FormatException>( () => SignedToken <JsonWebSignature.Header, JsonWebSignature.Payload> .FromSignedToken( "not_base64_header.not_base64_serialized_payload.signature")); }