Skip to content

Commit

Permalink
Add IPGEO to DFP rules endpoint (#77)
Browse files Browse the repository at this point in the history
This PR adds support for IPGEO in the fraud rules endpoint. You can now set rules on CIDR blocks, ASNs, and country codes. The fingerprint lookup endpoint will now also return information on which rule type and identifier were applied, if a `RULE_MATCH` reason was returned.

This PR also adds support for importing private signing certificates for your SAML connections.
  • Loading branch information
ci-stytch authored Feb 20, 2025
1 parent 94c61c9 commit 6d96ce3
Show file tree
Hide file tree
Showing 7 changed files with 148 additions and 26 deletions.
2 changes: 2 additions & 0 deletions stytch/src/main/kotlin/com/stytch/java/b2b/models/sso/SSO.kt
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,8 @@ public data class X509Certificate
val createdAt: Instant? = null,
@Json(name = "expires_at")
val expiresAt: Instant? = null,
@Json(name = "updated_at")
val updatedAt: Instant? = null,
)

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -422,6 +422,13 @@ public data class UpdateConnectionRequest
*/
@Json(name = "identity_provider")
val identityProvider: UpdateConnectionRequestIdentityProvider? = null,
/**
* A PKCS1 format RSA private key used for signing SAML requests. Only PKCS1 format (starting with "-----BEGIN RSA PRIVATE
* KEY-----") is supported. When provided, Stytch will generate a new x509 certificate from this key and return it in the
* signing_certificates array.
*/
@Json(name = "signing_private_key")
val signingPrivateKey: String? = null,
)

/**
Expand Down
2 changes: 1 addition & 1 deletion stytch/src/main/kotlin/com/stytch/java/common/Version.kt
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
package com.stytch.java.common

internal const val VERSION = "7.5.0"
internal const val VERSION = "7.6.0"
Original file line number Diff line number Diff line change
Expand Up @@ -23,25 +23,49 @@ import java.util.concurrent.CompletableFuture
public interface Rules {
/**
* Set a rule for a particular `visitor_id`, `browser_id`, `visitor_fingerprint`, `browser_fingerprint`,
* `hardware_fingerprint`, or `network_fingerprint`. This is helpful in cases where you want to allow or block a specific
* user or fingerprint. You should be careful when setting rules for `browser_fingerprint`, `hardware_fingerprint`, or
* `network_fingerprint` as they can be shared across multiple users, and you could affect more users than intended.
* `hardware_fingerprint`, `network_fingerprint`, `cidr_block`, `asn`, or `country_code`. This is helpful in cases where
* you want to allow or block a specific user or fingerprint. You should be careful when setting rules for
* `browser_fingerprint`, `hardware_fingerprint`, or `network_fingerprint` as they can be shared across multiple users,
* and you could affect more users than intended.
*
* You may not set an `ALLOW` rule for a `country_code`.
*
* Rules are applied in the order specified above. For example, if an end user has an `ALLOW` rule set for their
* `visitor_id` but a `BLOCK` rule set for their `hardware_fingerprint`, they will receive an `ALLOW` verdict because the
* `visitor_id` rule takes precedence.
*
* If there are conflicts between multiple `cidr_block` rules (for example, if the `ip_address` of the end user overlaps
* with multiple CIDR blocks that have rules set), the conflicts are resolved as follows:
* - The smallest block size takes precedence. For example, if an `ip_address` overlaps with a `cidr_block` rule of
* `ALLOW` for a block with a prefix of `/32` and a `cidr_block` rule of `BLOCK` with a prefix of `/24`, the rule match
* verdict will be `ALLOW`.
* - Among equivalent size blocks, `BLOCK` takes precedence over `CHALLENGE`, which takes precedence over `ALLOW`. For
* example, if an `ip_address` overlaps with two `cidr_block` rules with blocks of the same size that return `CHALLENGE`
* and `ALLOW`, the rule match verdict will be `CHALLENGE`.
*/
public suspend fun set(data: SetRequest): StytchResult<SetResponse>

/**
* Set a rule for a particular `visitor_id`, `browser_id`, `visitor_fingerprint`, `browser_fingerprint`,
* `hardware_fingerprint`, or `network_fingerprint`. This is helpful in cases where you want to allow or block a specific
* user or fingerprint. You should be careful when setting rules for `browser_fingerprint`, `hardware_fingerprint`, or
* `network_fingerprint` as they can be shared across multiple users, and you could affect more users than intended.
* `hardware_fingerprint`, `network_fingerprint`, `cidr_block`, `asn`, or `country_code`. This is helpful in cases where
* you want to allow or block a specific user or fingerprint. You should be careful when setting rules for
* `browser_fingerprint`, `hardware_fingerprint`, or `network_fingerprint` as they can be shared across multiple users,
* and you could affect more users than intended.
*
* You may not set an `ALLOW` rule for a `country_code`.
*
* Rules are applied in the order specified above. For example, if an end user has an `ALLOW` rule set for their
* `visitor_id` but a `BLOCK` rule set for their `hardware_fingerprint`, they will receive an `ALLOW` verdict because the
* `visitor_id` rule takes precedence.
*
* If there are conflicts between multiple `cidr_block` rules (for example, if the `ip_address` of the end user overlaps
* with multiple CIDR blocks that have rules set), the conflicts are resolved as follows:
* - The smallest block size takes precedence. For example, if an `ip_address` overlaps with a `cidr_block` rule of
* `ALLOW` for a block with a prefix of `/32` and a `cidr_block` rule of `BLOCK` with a prefix of `/24`, the rule match
* verdict will be `ALLOW`.
* - Among equivalent size blocks, `BLOCK` takes precedence over `CHALLENGE`, which takes precedence over `ALLOW`. For
* example, if an `ip_address` overlaps with two `cidr_block` rules with blocks of the same size that return `CHALLENGE`
* and `ALLOW`, the rule match verdict will be `CHALLENGE`.
*/
public fun set(
data: SetRequest,
Expand All @@ -50,13 +74,25 @@ public interface Rules {

/**
* Set a rule for a particular `visitor_id`, `browser_id`, `visitor_fingerprint`, `browser_fingerprint`,
* `hardware_fingerprint`, or `network_fingerprint`. This is helpful in cases where you want to allow or block a specific
* user or fingerprint. You should be careful when setting rules for `browser_fingerprint`, `hardware_fingerprint`, or
* `network_fingerprint` as they can be shared across multiple users, and you could affect more users than intended.
* `hardware_fingerprint`, `network_fingerprint`, `cidr_block`, `asn`, or `country_code`. This is helpful in cases where
* you want to allow or block a specific user or fingerprint. You should be careful when setting rules for
* `browser_fingerprint`, `hardware_fingerprint`, or `network_fingerprint` as they can be shared across multiple users,
* and you could affect more users than intended.
*
* You may not set an `ALLOW` rule for a `country_code`.
*
* Rules are applied in the order specified above. For example, if an end user has an `ALLOW` rule set for their
* `visitor_id` but a `BLOCK` rule set for their `hardware_fingerprint`, they will receive an `ALLOW` verdict because the
* `visitor_id` rule takes precedence.
*
* If there are conflicts between multiple `cidr_block` rules (for example, if the `ip_address` of the end user overlaps
* with multiple CIDR blocks that have rules set), the conflicts are resolved as follows:
* - The smallest block size takes precedence. For example, if an `ip_address` overlaps with a `cidr_block` rule of
* `ALLOW` for a block with a prefix of `/32` and a `cidr_block` rule of `BLOCK` with a prefix of `/24`, the rule match
* verdict will be `ALLOW`.
* - Among equivalent size blocks, `BLOCK` takes precedence over `CHALLENGE`, which takes precedence over `ALLOW`. For
* example, if an `ip_address` overlaps with two `cidr_block` rules with blocks of the same size that return `CHALLENGE`
* and `ALLOW`, the rule match verdict will be `CHALLENGE`.
*/
public fun setCompletable(data: SetRequest): CompletableFuture<StytchResult<SetResponse>>
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,36 @@ public enum class RuleAction {
NONE,
}

@JsonClass(generateAdapter = false)
public enum class RuleType {
@Json(name = "VISITOR_ID")
VISITOR_ID,

@Json(name = "BROWSER_ID")
BROWSER_ID,

@Json(name = "VISITOR_FINGERPRINT")
VISITOR_FINGERPRINT,

@Json(name = "BROWSER_FINGERPRINT")
BROWSER_FINGERPRINT,

@Json(name = "HARDWARE_FINGERPRINT")
HARDWARE_FINGERPRINT,

@Json(name = "NETWORK_FINGERPRINT")
NETWORK_FINGERPRINT,

@Json(name = "CIDR_BLOCK")
CIDR_BLOCK,

@Json(name = "ASN")
ASN,

@Json(name = "COUNTRY_CODE")
COUNTRY_CODE,
}

@JsonClass(generateAdapter = false)
public enum class VerdictAction {
@Json(name = "ALLOW")
Expand Down Expand Up @@ -220,4 +250,16 @@ public data class Verdict
*/
@Json(name = "is_authentic_device")
val isAuthenticDevice: Boolean,
/**
* The type of rule match that was applied (e.g. `VISITOR_ID`), if any. This field will only be present if there is a
* `RULE_MATCH` reason in the list of verdict reasons.
*/
@Json(name = "rule_match_type")
val ruleMatchType: RuleType? = null,
/**
* The rule that was applied (e.g. a specific visitor ID value), if any. This field will only be present if there is a
* `RULE_MATCH` reason in the list of verdict reasons.
*/
@Json(name = "rule_match_identifier")
val ruleMatchIdentifier: String? = null,
)
Original file line number Diff line number Diff line change
Expand Up @@ -19,39 +19,39 @@ public data class SetRequest
@JvmOverloads
constructor(
/**
* The action that should be returned by a fingerprint lookup for that fingerprint or ID with a `RULE_MATCH` reason. The
* following values are valid: `ALLOW`, `BLOCK`, `CHALLENGE`, or `NONE`. If a `NONE` action is specified, it will clear
* the stored rule.
* The action that should be returned by a fingerprint lookup for that identifier with a `RULE_MATCH` reason. The
* following values are valid: `ALLOW`, `BLOCK`, `CHALLENGE`, or `NONE`. For country codes, `ALLOW` actions are not
* allowed. If a `NONE` action is specified, it will clear the stored rule.
*/
@Json(name = "action")
val action: RuleAction,
/**
* The visitor ID we want to set a rule for. Only one fingerprint or ID can be specified in the request.
* The visitor ID we want to set a rule for. Only one identifier can be specified in the request.
*/
@Json(name = "visitor_id")
val visitorId: String? = null,
/**
* The browser ID we want to set a rule for. Only one fingerprint or ID can be specified in the request.
* The browser ID we want to set a rule for. Only one identifier can be specified in the request.
*/
@Json(name = "browser_id")
val browserId: String? = null,
/**
* The visitor fingerprint we want to set a rule for. Only one fingerprint or ID can be specified in the request.
* The visitor fingerprint we want to set a rule for. Only one identifier can be specified in the request.
*/
@Json(name = "visitor_fingerprint")
val visitorFingerprint: String? = null,
/**
* The browser fingerprint we want to set a rule for. Only one fingerprint or ID can be specified in the request.
* The browser fingerprint we want to set a rule for. Only one identifier can be specified in the request.
*/
@Json(name = "browser_fingerprint")
val browserFingerprint: String? = null,
/**
* The hardware fingerprint we want to set a rule for. Only one fingerprint or ID can be specified in the request.
* The hardware fingerprint we want to set a rule for. Only one identifier can be specified in the request.
*/
@Json(name = "hardware_fingerprint")
val hardwareFingerprint: String? = null,
/**
* The network fingerprint we want to set a rule for. Only one fingerprint or ID can be specified in the request.
* The network fingerprint we want to set a rule for. Only one identifier can be specified in the request.
*/
@Json(name = "network_fingerprint")
val networkFingerprint: String? = null,
Expand All @@ -66,6 +66,25 @@ public data class SetRequest
*/
@Json(name = "description")
val description: String? = null,
/**
* The CIDR block we want to set a rule for. You may pass either an IP address or a CIDR block. The CIDR block prefix must
* be between 16 and 32, inclusive. If an end user's IP address is within this CIDR block, this rule will be applied. Only
* one identifier can be specified in the request.
*/
@Json(name = "cidr_block")
val cidrBlock: String? = null,
/**
* The country code we want to set a rule for. The country code must be a valid ISO 3166-1 alpha-2 code. You may not set
* `ALLOW` rules for country codes. Only one identifier can be specified in the request.
*/
@Json(name = "country_code")
val countryCode: String? = null,
/**
* The ASN we want to set a rule for. The ASN must be the string representation of an integer between 0 and 4294967295,
* inclusive. Only one identifier can be specified in the request.
*/
@Json(name = "asn")
val asn: String? = null,
)

/**
Expand Down Expand Up @@ -93,32 +112,32 @@ public data class SetResponse
@Json(name = "status_code")
val statusCode: Int,
/**
* The cookie stored on the user's device that uniquely identifies them.
* The visitor ID that a rule was set for.
*/
@Json(name = "visitor_id")
val visitorId: String? = null,
/**
* Combination of VisitorID and NetworkFingerprint to create a clear identifier of a browser.
* The browser ID that a rule was set for.
*/
@Json(name = "browser_id")
val browserId: String? = null,
/**
* Cookie-less way of identifying a unique user.
* The visitor fingerprint that a rule was set for.
*/
@Json(name = "visitor_fingerprint")
val visitorFingerprint: String? = null,
/**
* Combination of signals to identify a browser and its specific version.
* The browser fingerprint that a rule was set for.
*/
@Json(name = "browser_fingerprint")
val browserFingerprint: String? = null,
/**
* Combinations of signals to identify an operating system and architecture.
* The hardware fingerprint that a rule was set for.
*/
@Json(name = "hardware_fingerprint")
val hardwareFingerprint: String? = null,
/**
* Combination of signals associated with a specific network commonly known as TLS fingerprinting.
* The network fingerprint that a rule was set for.
*/
@Json(name = "network_fingerprint")
val networkFingerprint: String? = null,
Expand All @@ -128,4 +147,20 @@ public data class SetResponse
*/
@Json(name = "expires_at")
val expiresAt: Instant? = null,
/**
* The CIDR block that a rule was set for. If an end user's IP address is within this CIDR block, this rule will be
* applied.
*/
@Json(name = "cidr_block")
val cidrBlock: String? = null,
/**
* The country code that a rule was set for.
*/
@Json(name = "country_code")
val countryCode: String? = null,
/**
* The ASN that a rule was set for.
*/
@Json(name = "asn")
val asn: String? = null,
)
2 changes: 1 addition & 1 deletion version.gradle.kts
Original file line number Diff line number Diff line change
@@ -1 +1 @@
version = "7.5.0"
version = "7.6.0"

0 comments on commit 6d96ce3

Please sign in to comment.