[package]
edition = "2021"
rust-version = "1.88"
name = "loginflow"
version = "0.1.1"
authors = ["Santh Project <contact@santh.dev>"]
build = false
publish = true
autolib = false
autobins = false
autoexamples = false
autotests = false
autobenches = false
description = "Browser-driven login discovery, form drive, MFA, and session capture into authjar"
homepage = "https://santh.dev"
readme = "README.md"
keywords = [
"login",
"authentication",
"session",
"browser",
"mfa",
]
categories = [
"authentication",
"web-programming",
]
license = "MIT OR Apache-2.0"
repository = "https://github.com/santhreal/loginflow"
[package.metadata.santh]
status = "beta"
[features]
browser = [
"dep:runtime-headless",
"guise?/browser",
]
captchaforge = [
"dep:captchaforge",
"browser",
]
default = []
stealth = [
"dep:guise",
"guise/fingerprint",
"guise/http",
]
[lib]
name = "loginflow"
path = "src/lib.rs"
[[bin]]
name = "loginflow"
path = "src/bin/loginflow.rs"
[[test]]
name = "adversarial_discover_empty_html"
path = "tests/adversarial/discover_empty_html.rs"
[[test]]
name = "adversarial_discover_js_obfuscation"
path = "tests/adversarial/discover_js_obfuscation.rs"
[[test]]
name = "adversarial_discover_webauthn_malformed_html"
path = "tests/adversarial/discover_webauthn_malformed_html.rs"
[[test]]
name = "adversarial_mfa_sms_relay"
path = "tests/adversarial/mfa_sms_relay.rs"
[[test]]
name = "concurrent_loginflow_concurrent"
path = "tests/concurrent/loginflow_concurrent.rs"
[[test]]
name = "contract_cli_fails_on_missing_args"
path = "tests/contract/cli_fails_on_missing_args.rs"
[[test]]
name = "contract_cli_shows_help"
path = "tests/contract/cli_shows_help.rs"
[[test]]
name = "gap_mfa_email_link"
path = "tests/gap/mfa_email_link.rs"
[[test]]
name = "integration_missing_host_rejected"
path = "tests/integration/missing_host_rejected.rs"
[[test]]
name = "integration_spa_hydration"
path = "tests/integration/spa_hydration.rs"
[[test]]
name = "property_decode_rejects_strings_without_two_dots"
path = "tests/property/decode_rejects_strings_without_two_dots.rs"
[[test]]
name = "property_does_not_panic_on_any_string"
path = "tests/property/does_not_panic_on_any_string.rs"
[[test]]
name = "regression_redaction"
path = "tests/regression/redaction.rs"
[[test]]
name = "unit_auth0_authorize_href_matches"
path = "tests/unit/auth0_authorize_href_matches.rs"
[[test]]
name = "unit_best_entry_is_deterministic"
path = "tests/unit/best_entry_is_deterministic.rs"
[[test]]
name = "unit_captcha_dispatch"
path = "tests/unit/captcha_dispatch.rs"
[[test]]
name = "unit_credentials_toml_parses_username_password_and_totp"
path = "tests/unit/credentials_toml_parses_username_password_and_totp.rs"
[[test]]
name = "unit_custom_hydration_wait_can_be_configured"
path = "tests/unit/custom_hydration_wait_can_be_configured.rs"
[[test]]
name = "unit_decode_invalid_base64"
path = "tests/unit/decode_invalid_base64.rs"
[[test]]
name = "unit_decode_invalid_json"
path = "tests/unit/decode_invalid_json.rs"
[[test]]
name = "unit_decode_invalid_structure"
path = "tests/unit/decode_invalid_structure.rs"
[[test]]
name = "unit_decode_valid_jwt"
path = "tests/unit/decode_valid_jwt.rs"
[[test]]
name = "unit_default_hydration_wait_is_reasonable"
path = "tests/unit/default_hydration_wait_is_reasonable.rs"
[[test]]
name = "unit_detects_totp_and_marks_not_http_simple"
path = "tests/unit/detects_totp_and_marks_not_http_simple.rs"
[[test]]
name = "unit_discover_no_webauthn"
path = "tests/unit/discover_no_webauthn.rs"
[[test]]
name = "unit_discover_webauthn_authentication"
path = "tests/unit/discover_webauthn_authentication.rs"
[[test]]
name = "unit_discover_webauthn_registration"
path = "tests/unit/discover_webauthn_registration.rs"
[[test]]
name = "unit_discovers_google_and_github_providers"
path = "tests/unit/discovers_google_and_github_providers.rs"
[[test]]
name = "unit_discovers_username_password_and_csrf"
path = "tests/unit/discovers_username_password_and_csrf.rs"
[[test]]
name = "unit_extract_auth_headers"
path = "tests/unit/extract_auth_headers.rs"
[[test]]
name = "unit_extract_invalid_bearer_ignored"
path = "tests/unit/extract_invalid_bearer_ignored.rs"
[[test]]
name = "unit_gitlab_oauth_path_matches"
path = "tests/unit/gitlab_oauth_path_matches.rs"
[[test]]
name = "unit_ignores_privacy_footer_link"
path = "tests/unit/ignores_privacy_footer_link.rs"
[[test]]
name = "unit_known_hotp_slice_matches_rfc4226_step_0"
path = "tests/unit/known_hotp_slice_matches_rfc4226_step_0.rs"
[[test]]
name = "unit_loginflow_builder_accepts_captcha_solver"
path = "tests/unit/loginflow_builder_accepts_captcha_solver.rs"
[[test]]
name = "unit_mfa_email_link"
path = "tests/unit/mfa_email_link.rs"
[[test]]
name = "unit_mfa_sms_relay"
path = "tests/unit/mfa_sms_relay.rs"
[[test]]
name = "unit_oauth_drive"
path = "tests/unit/oauth_drive.rs"
[[test]]
name = "unit_register_bearer"
path = "tests/unit/register_bearer.rs"
[[test]]
name = "unit_register_bearer_invalid"
path = "tests/unit/register_bearer_invalid.rs"
[[test]]
name = "unit_register_bearer_without_prefix"
path = "tests/unit/register_bearer_without_prefix.rs"
[[test]]
name = "unit_resolves_relative_oauth_href"
path = "tests/unit/resolves_relative_oauth_href.rs"
[[test]]
name = "unit_respond_to_webauthn_challenge_error"
path = "tests/unit/respond_to_webauthn_challenge_error.rs"
[[test]]
name = "unit_respond_to_webauthn_challenge_success"
path = "tests/unit/respond_to_webauthn_challenge_success.rs"
[[test]]
name = "unit_scald_fastpath"
path = "tests/unit/scald_fastpath.rs"
[[test]]
name = "unit_session_json_roundtrip_scald_auth"
path = "tests/unit/session_json_roundtrip_scald_auth.rs"
[[test]]
name = "unit_skips_search_style_username_fields"
path = "tests/unit/skips_search_style_username_fields.rs"
[[test]]
name = "unit_totp_changes_across_steps"
path = "tests/unit/totp_changes_across_steps.rs"
[[test]]
name = "unit_totp_is_deterministic_for_fixed_step"
path = "tests/unit/totp_is_deterministic_for_fixed_step.rs"
[[bench]]
name = "login_bench"
path = "benches/login_bench.rs"
harness = false
[dependencies.async-trait]
version = "=0.1.89"
[dependencies.authjar]
version = "0.2.5"
[dependencies.base32]
version = "=0.5.1"
[dependencies.captchaforge]
version = "=0.2.36"
optional = true
default-features = false
[dependencies.clap]
version = "=4.6.1"
features = ["derive"]
[dependencies.guise]
version = "0.1.3"
features = [
"fingerprint",
"human",
]
optional = true
default-features = false
[dependencies.reqwest]
version = "=0.12.28"
features = [
"cookies",
"json",
"rustls-tls",
]
default-features = false
[dependencies.runtime-headless]
version = "0.1.1"
optional = true
[dependencies.santh-cli]
version = "0.1.1"
[dependencies.santh-tracing]
version = "0.2.1"
[dependencies.scraper]
version = "=0.26.0"
[dependencies.secfinding]
version = "0.4.1"
[dependencies.secrecy]
version = "=0.10.3"
features = ["serde"]
[dependencies.serde]
version = "=1.0.228"
features = ["derive"]
[dependencies.serde_json]
version = "1"
[dependencies.thiserror]
version = "=2.0.18"
[dependencies.tokio]
version = "=1.51.1"
features = [
"macros",
"rt",
"rt-multi-thread",
"time",
"net",
"io-util",
]
[dependencies.toml]
version = "=0.8.23"
[dependencies.totp-lite]
version = "=2.0.1"
[dependencies.tracing]
version = "0.1.44"
[dependencies.url]
version = "=2.5.8"
[dependencies.zeroize]
version = "1.9"
[dev-dependencies.assert_cmd]
version = "2"
[dev-dependencies.criterion]
version = "0.5.1"
[dev-dependencies.proptest]
version = "=1.9.0"
[dev-dependencies.rcgen]
version = "0.14.7"
features = [
"pem",
"x509-parser",
]
[dev-dependencies.rustls]
version = "0.23.27"
features = ["ring"]
[dev-dependencies.tokio]
version = "=1.51.1"
features = [
"macros",
"rt-multi-thread",
"net",
"io-util",
]
[dev-dependencies.tokio-rustls]
version = "0.26.4"
[dev-dependencies.wiremock]
version = "=0.6.5"
[lints.rust]
unsafe_code = "forbid"