internal SignedTokenVerificationOptions ToVerificationOptions()
            {
                var options = new SignedTokenVerificationOptions
                {
                    // Google signed tokens must be signed with these certificates.
                    CertificatesUrl         = GoogleAuthConsts.JsonWebKeySetUrl,
                    ForceCertificateRefresh = ForceGoogleCertRefresh,
                    IssuedAtClockTolerance  = IssuedAtClockTolerance,
                    ExpiryClockTolerance    = ExpirationTimeClockTolerance,
                    CertificateCache        = CertificateCache,
                };

                options.Clock = Clock ?? options.Clock;

                foreach (string audience in Audience ?? Enumerable.Empty <string>())
                {
                    options.TrustedAudiences.Add(audience);
                }
                // These are the trusted issuers for Google signed tokens.
                foreach (string issuer in ValidJwtIssuers)
                {
                    options.TrustedIssuers.Add(issuer);
                }

                return(options);
            }
        /// <summary>
        /// Verifies that the given token is a valid, not expired, signed token.
        /// </summary>
        /// <param name="signedJwt">The token to verify.</param>
        /// <param name="options">The options to use for verification.
        /// May be null in which case default options will be used.</param>
        /// <param name="cancellationToken">The cancellation token for the operation.</param>
        /// <returns>The payload contained by the token.</returns>
        /// <exception cref="InvalidJwtException">If the token is invalid or expired.</exception>
        public async static Task <Payload> VerifySignedTokenAsync(
            string signedJwt, SignedTokenVerificationOptions options = null, CancellationToken cancellationToken = default)
        {
            var signedToken = SignedToken <Header, Payload> .FromSignedToken(signedJwt);

            await SignedTokenVerification.VerifySignedTokenAsync(signedToken, options, cancellationToken).ConfigureAwait(false);

            return(signedToken.Payload);
        }
        internal async static Task VerifySignedTokenAsync <TJswHeader, TJswPayload>(
            SignedToken <TJswHeader, TJswPayload> signedToken, SignedTokenVerificationOptions options, CancellationToken cancellationToken)
            where TJswHeader : Header
            where TJswPayload : Payload
        {
            signedToken.ThrowIfNull(nameof(signedToken));
            options = options == null ? new SignedTokenVerificationOptions() : new SignedTokenVerificationOptions(options);
            options.CertificateCache ??= s_certificateCache;

            // Start the signature validation task...
            Task signatureVerificationTask = signedToken.Header.Algorithm switch
            {
                "RS256" => VerifyRS256TokenAsync(signedToken, options, cancellationToken),
#if ES256_SUPPORTED
                "ES256" => VerifyES256TokenAsync(signedToken, options, cancellationToken),
#else
                "ES256" => throw new InvalidOperationException("ES256 signed token verification is not supported in this platform."),
#endif
                _ => throw new InvalidJwtException("Signing algorithm must be either RS256 or ES256.")
            };

            // ... let's validate everything else while we wait for signature validation...

            // The signed token issuer should be one of the trusted issuers.
            if (options.TrustedIssuers.Count > 0 && !options.TrustedIssuers.Contains(signedToken.Payload.Issuer))
            {
                var validList = string.Join(", ", options.TrustedIssuers.Select(x => $"'{x}'"));
                throw new InvalidJwtException($"JWT issuer incorrect. Must be one of: {validList}");
            }
            // All audiences on the signed token should be trusted audiences.
            if (options.TrustedAudiences.Count > 0 && signedToken.Payload.AudienceAsList.Except(options.TrustedAudiences).Any())
            {
                throw new InvalidJwtException("JWT contains untrusted 'aud' claim.");
            }
            // Issued at and expiration are present. Save them for time related validity checks.
            DateTimeOffset issuedAt  = signedToken.Payload.IssuedAt ?? throw new InvalidJwtException("JWT must contain 'iat' and 'exp' claims");
            DateTimeOffset expiresAt = signedToken.Payload.ExpiresAt ?? throw new InvalidJwtException("JWT must contain 'iat' and 'exp' claims");

            // Check that the token was issued in the past.
            var utcNow = options.Clock.UtcNow;

            if (utcNow + options.IssuedAtClockTolerance < issuedAt)
            {
                throw new InvalidJwtException("JWT is not yet valid.");
            }
            // Check that the token is not yet expired.
            if (utcNow - options.ExpiryClockTolerance > expiresAt)
            {
                throw new InvalidJwtException("JWT has expired.");
            }

            // ... and finally let's wait for signature validation to be done.
            await signatureVerificationTask.ConfigureAwait(false);
        }
        private async static Task VerifyES256TokenAsync <TJswHeader, TJswPayload>(
            SignedToken <TJswHeader, TJswPayload> signedToken, SignedTokenVerificationOptions options, CancellationToken cancellationToken)
            where TJswHeader : Header
            where TJswPayload : Payload
        {
            var certificates = await GetCertificatesAsync(
                options, GoogleAuthConsts.IapKeySetUrl, FromKeyToECDsa, cancellationToken).ConfigureAwait(false);

            foreach (ECDsa certificate in certificates)
            {
                if (certificate.VerifyHash(signedToken.Sha256Hash, signedToken.Signature))
                {
                    return;
                }
            }
            throw new InvalidJwtException("JWT invalid, unable to verify signature.");
예제 #5
0
 /// <summary>
 /// Creates a new instance of <see cref="SignedTokenVerificationOptions"/>
 /// by copying over all the values from <paramref name="other"/>.
 /// </summary>
 /// <param name="other">The option set to build this instance from.</param>
 public SignedTokenVerificationOptions(SignedTokenVerificationOptions other)
 {
     other.ThrowIfNull(nameof(other));
     foreach (var audience in other.TrustedAudiences)
     {
         TrustedAudiences.Add(audience);
     }
     foreach (var issuer in other.TrustedIssuers)
     {
         TrustedIssuers.Add(issuer);
     }
     CertificatesUrl         = other.CertificatesUrl;
     ForceCertificateRefresh = other.ForceCertificateRefresh;
     IssuedAtClockTolerance  = other.IssuedAtClockTolerance;
     ExpiryClockTolerance    = other.ExpiryClockTolerance;
     CertificateCache        = other.CertificateCache;
     Clock = other.Clock;
 }
        private async static Task VerifyRS256TokenAsync <TJswHeader, TJswPayload>(
            SignedToken <TJswHeader, TJswPayload> signedToken, SignedTokenVerificationOptions options, CancellationToken cancellationToken)
            where TJswHeader : Header
            where TJswPayload : Payload
        {
            var certificates = await GetCertificatesAsync(
                options, GoogleAuthConsts.JsonWebKeySetUrl, FromKeyToRsa, cancellationToken).ConfigureAwait(false);

            foreach (RSA certificate in certificates)
            {
#if NET45
                if (((RSACryptoServiceProvider)certificate).VerifyHash(signedToken.Sha256Hash, Sha256Oid, signedToken.Signature))
#elif NETSTANDARD1_3 || NETSTANDARD2_0 || NET461
                if (certificate.VerifyHash(signedToken.Sha256Hash, signedToken.Signature, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1))
#else
#error Unsupported platform
#endif
                {
                    return;
                }
            }
            throw new InvalidJwtException("JWT invalid, unable to verify signature.");
 /// <summary>
 /// Verifies that the given token is a valid, not expired, signed token.
 /// </summary>
 /// <param name="signedJwt">The token to verify.</param>
 /// <param name="options">The options to use for verification.
 /// May be null in which case default options will be used.</param>
 /// <param name="cancellationToken">The cancellation token for the operation.</param>
 /// <returns>The payload contained by the token.</returns>
 /// <exception cref="InvalidJwtException">If the token is invalid or expired.</exception>
 public static Task <Payload> VerifySignedTokenAsync(
     string signedJwt, SignedTokenVerificationOptions options = null, CancellationToken cancellationToken = default) =>
 VerifySignedTokenAsync <Payload>(signedJwt, options, cancellationToken);