def validate_countersigned_recovery_token!(countersigned_token, context = {})
      # 5. Validate the the issuer field is present in the token,
      # and that it matches the audience field in the original countersigned token.
      begin
        recovery_provider = RecoveryToken.recovery_provider_issuer(Base64.strict_decode64(countersigned_token))
      rescue RecoveryTokenSerializationError => e
        raise CountersignedTokenError.new("Countersigned token is invalid: " + e.message, :countersigned_token_parse_error)
      rescue UnknownProviderError => e
        raise CountersignedTokenError.new(e.message, :recovery_token_invalid_issuer)
      end

      # 1. Parse the countersigned-token.
      # 2. Validate that the version field is 0.
      # 7. Retrieve the current Recovery Provider configuration as described in Section 2.
      # 8. Validate that the counter-signed token signature validates with a current element of the countersign-pubkeys-secp256r1 array.
      begin
        parsed_countersigned_token = recovery_provider.unseal(Base64.strict_decode64(countersigned_token), context)
      rescue TokenFormatError => e
        raise CountersignedTokenError.new(e.message, :countersigned_invalid_token_version)
      rescue CryptoError
        raise CountersignedTokenError.new("Countersigned token has an invalid signature", :countersigned_invalid_signature)
      end

      # 3. De-serialize the original recovery token from the data field.
      # 4. Validate the signature on the original recovery token.
      begin
        recovery_token = self.unseal(parsed_countersigned_token.data, context)
      rescue RecoveryTokenSerializationError => e
        raise CountersignedTokenError.new("Nested recovery token is invalid: " + e.message, :recovery_token_token_parse_error)
      rescue TokenFormatError => e
        raise CountersignedTokenError.new("Nested recovery token format error: #{e.message}", :recovery_token_invalid_token_type)
      rescue CryptoError
        raise CountersignedTokenError.new("Nested recovery token has an invalid signature", :recovery_token_invalid_signature)
      end

      # 5. Validate the the issuer field is present in the countersigned-token,
      # and that it matches the audience field in the original token.

      countersigned_token_issuer = parsed_countersigned_token.issuer
      if countersigned_token_issuer.blank? || countersigned_token_issuer != recovery_token.audience || recovery_provider.origin != countersigned_token_issuer
        raise CountersignedTokenError.new("Validate the the issuer field is present in the countersigned-token, and that it matches the audience field in the original token", :recovery_token_invalid_issuer)
      end

      # 6. Validate the token binding for the countersigned token, if present.
      # (the token binding for the inner token is not relevant)
      # TODO not required, to be implemented later

      # 9. Decrypt the data field from the original recovery token and parse its information, if present.
      # no decryption here is attempted. Attempts to call `decode` will just fail.

      # 10. Apply any additional processing which provider-specific data in the opaque data portion may indicate is necessary.
      begin
        if DateTime.parse(parsed_countersigned_token.issued_time).utc < (Time.now - CLOCK_SKEW).utc
          raise CountersignedTokenError.new("Countersigned recovery token issued at time is too far in the past", :stale_token)
        end
      rescue ArgumentError
        raise CountersignedTokenError.new("Invalid countersigned token issued time", :invalid_issued_time)
      end

      recovery_token
    end
def validate_countersigned_recovery_token!(countersigned_token, context = {})
      # 5. Validate the the issuer field is present in the token,
      # and that it matches the audience field in the original countersigned token.
      begin
        recovery_provider = RecoveryToken.recovery_provider_issuer(Base64.strict_decode64(countersigned_token))
      rescue RecoveryTokenSerializationError => e
        raise CountersignedTokenError.new("Countersigned token is invalid: " + e.message, :countersigned_token_parse_error)
      rescue UnknownProviderError => e
        raise CountersignedTokenError.new(e.message, :recovery_token_invalid_issuer)
      end

      # 1. Parse the countersigned-token.
      # 2. Validate that the version field is 0.
      # 7. Retrieve the current Recovery Provider configuration as described in Section 2.
      # 8. Validate that the counter-signed token signature validates with a current element of the countersign-pubkeys-secp256r1 array.
      begin
        parsed_countersigned_token = recovery_provider.unseal(Base64.strict_decode64(countersigned_token), context)
      rescue TokenFormatError => e
        raise CountersignedTokenError.new(e.message, :countersigned_invalid_token_version)
      rescue CryptoError
        raise CountersignedTokenError.new("Countersigned token has an invalid signature", :countersigned_invalid_signature)
      end

      # 3. De-serialize the original recovery token from the data field.
      # 4. Validate the signature on the original recovery token.
      begin
        recovery_token = self.unseal(parsed_countersigned_token.data, context)
      rescue RecoveryTokenSerializationError => e
        raise CountersignedTokenError.new("Nested recovery token is invalid: " + e.message, :recovery_token_token_parse_error)
      rescue TokenFormatError => e
        raise CountersignedTokenError.new("Nested recovery token format error: #{e.message}", :recovery_token_invalid_token_type)
      rescue CryptoError
        raise CountersignedTokenError.new("Nested recovery token has an invalid signature", :recovery_token_invalid_signature)
      end

      # 5. Validate the the issuer field is present in the countersigned-token,
      # and that it matches the audience field in the original token.

      countersigned_token_issuer = parsed_countersigned_token.issuer
      if countersigned_token_issuer.blank? || countersigned_token_issuer != recovery_token.audience || recovery_provider.origin != countersigned_token_issuer
        raise CountersignedTokenError.new("Validate the the issuer field is present in the countersigned-token, and that it matches the audience field in the original token", :recovery_token_invalid_issuer)
      end

      # 6. Validate the token binding for the countersigned token, if present.
      # (the token binding for the inner token is not relevant)
      # TODO not required, to be implemented later

      # 9. Decrypt the data field from the original recovery token and parse its information, if present.
      # no decryption here is attempted. Attempts to call `decode` will just fail.

      # 10. Apply any additional processing which provider-specific data in the opaque data portion may indicate is necessary.
      begin
        if DateTime.parse(parsed_countersigned_token.issued_time).utc < (Time.now - CLOCK_SKEW).utc
          raise CountersignedTokenError.new("Countersigned recovery token issued at time is too far in the past", :stale_token)
        end
      rescue ArgumentError
        raise CountersignedTokenError.new("Invalid countersigned token issued time", :invalid_issued_time)
      end

      recovery_token
    end
Validates the countersigned recovery token by verifying the signature
 of the countersigned token, parsing out the origin recovery token,
 verifying the signature on the recovery token, and finally decrypting
 the data in the origin recovery token.

 countersigned_token: our original recovery token wrapped in recovery
 token instance that is signed by the recovery provider.
 context: arbitrary data to be passed to Provider#unseal.

 returns a verified recovery token or raises
 an error if the token fails validation.