change webview user agent
This commit is contained in:
parent
39efa4545e
commit
6a4f5fb8bc
|
|
@ -1,17 +1,22 @@
|
|||
use browser_emulation::browser_profile::BrowserProfile;
|
||||
|
||||
/// The User-Agent used for BOTH the Midjourney login webview and every
|
||||
/// first-party Midjourney HTTP/websocket call.
|
||||
///
|
||||
/// It must be identical in both places: Cloudflare's `cf_clearance` cookie
|
||||
/// (captured by the webview) is bound to the exact User-Agent, so the wreq
|
||||
/// calls that replay those cookies have to present the same string. A
|
||||
/// mainstream desktop Safari UA (rather than the default WKWebView UA) also
|
||||
/// makes Google's embedded-webview risk detection less likely to force a
|
||||
/// passkey step-up during sign-in.
|
||||
pub const MIDJOURNEY_USER_AGENT: &str =
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.6 Safari/605.1.15";
|
||||
|
||||
/// The browser profile used for ALL first-party Midjourney HTTP/websocket
|
||||
/// traffic (submit, imagine, index page, image downloads, websocket upgrade).
|
||||
///
|
||||
/// Cloudflare's `cf_clearance` cookie is bound to the User-Agent of the browser
|
||||
/// that solved the challenge — here, the app's login webview, which on macOS is
|
||||
/// a WKWebView (Safari engine). So we emulate Safari to keep the fingerprint
|
||||
/// consistent with the captured cookies.
|
||||
///
|
||||
/// NB: for `cf_clearance` to validate, the emulated UA must match the webview's
|
||||
/// actual UA closely. If Cloudflare still challenges, the next step is to
|
||||
/// capture the webview's exact `navigator.userAgent` at login and use it as a
|
||||
/// `BrowserProfile::Custom` UA override.
|
||||
/// Safari fingerprint + the exact [`MIDJOURNEY_USER_AGENT`], to stay consistent
|
||||
/// with the macOS WKWebView login window that captured the cookies.
|
||||
pub fn midjourney_browser_profile() -> BrowserProfile {
|
||||
BrowserProfile::Safari18
|
||||
BrowserProfile::safari_macos_with_user_agent(MIDJOURNEY_USER_AGENT)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -52,6 +52,15 @@ pub trait LoginWindowSite: Send + Sync {
|
|||
self.login_website().credential_service()
|
||||
}
|
||||
|
||||
/// An optional User-Agent override for the login webview. Return `Some` when
|
||||
/// the site's sign-in misbehaves under the default WKWebView UA (e.g. Google
|
||||
/// forcing a passkey step-up), or when the captured cookies must later be
|
||||
/// replayed by an HTTP client under a specific UA (Cloudflare `cf_clearance`
|
||||
/// is UA-bound, so the two must match). Defaults to the platform default.
|
||||
fn user_agent(&self) -> Option<&'static str> {
|
||||
None
|
||||
}
|
||||
|
||||
/// SSO / identity-provider hosts that mean "still logging in".
|
||||
fn auth_flow_hostnames(&self) -> &[&str] {
|
||||
DEFAULT_AUTH_FLOW_HOSTNAMES
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
use crate::credentials::login_website::LoginWebsite;
|
||||
use crate::services::midjourney::utils::midjourney_browser_profile::MIDJOURNEY_USER_AGENT;
|
||||
use crate::windows::login_window::login_journey::LoginJourney;
|
||||
use crate::windows::login_window::login_window_trait::LoginWindowSite;
|
||||
use once_cell::sync::Lazy;
|
||||
|
|
@ -31,6 +32,14 @@ impl LoginWindowSite for MidjourneyLoginWindow {
|
|||
DESTINATION_HOSTNAMES
|
||||
}
|
||||
|
||||
// Google's sign-in forces a passkey step-up under the default WKWebView UA
|
||||
// (which the embedded browser can't satisfy). Present a mainstream desktop
|
||||
// Safari UA — the SAME one the Midjourney HTTP/websocket calls use — so the
|
||||
// captured cf_clearance cookie validates when those calls replay it.
|
||||
fn user_agent(&self) -> Option<&'static str> {
|
||||
Some(MIDJOURNEY_USER_AGENT)
|
||||
}
|
||||
|
||||
// Midjourney's session rides in the AuthUserTokenV3 cookie pair (the same
|
||||
// names midjourney_client's cookie_store_has_auth_cookies checks); either
|
||||
// appearing in a cookie-cleared window is the definitive login signal.
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@ pub async fn open_login_window(
|
|||
};
|
||||
|
||||
let start_url = WebviewUrl::External(first_url);
|
||||
let window = WebviewWindowBuilder::new(app, &window_name, start_url)
|
||||
let mut builder = WebviewWindowBuilder::new(app, &window_name, start_url)
|
||||
.title(site.window_title())
|
||||
.center()
|
||||
.resizable(true)
|
||||
|
|
@ -64,8 +64,17 @@ pub async fn open_login_window(
|
|||
.closable(true)
|
||||
.min_inner_size(200.0, 800.0)
|
||||
.focused(true)
|
||||
.devtools(true)
|
||||
.build()?;
|
||||
.devtools(true);
|
||||
|
||||
// Some sites (e.g. Midjourney, whose Google sign-in otherwise forces a
|
||||
// passkey step-up that the default WKWebView can't satisfy) need a
|
||||
// mainstream desktop User-Agent. It must match the UA the site's HTTP client
|
||||
// later uses, since Cloudflare's cf_clearance is UA-bound.
|
||||
if let Some(user_agent) = site.user_agent() {
|
||||
builder = builder.user_agent(user_agent);
|
||||
}
|
||||
|
||||
let window = builder.build()?;
|
||||
|
||||
let webview = window.get_webview(&window_name)
|
||||
.ok_or_else(|| anyhow!("no webview found"))?;
|
||||
|
|
|
|||
|
|
@ -34,6 +34,19 @@ pub struct CustomBrowserProfile {
|
|||
}
|
||||
|
||||
impl BrowserProfile {
|
||||
/// A macOS Safari profile with an explicit User-Agent override.
|
||||
///
|
||||
/// Use this when the UA must EXACTLY match another client's — e.g. an
|
||||
/// embedded webview that captured the cookies, since Cloudflare's
|
||||
/// `cf_clearance` is bound to the exact User-Agent string.
|
||||
pub fn safari_macos_with_user_agent(user_agent: impl Into<String>) -> Self {
|
||||
Self::Custom(CustomBrowserProfile {
|
||||
emulation: Emulation::Safari18,
|
||||
os: EmulationOS::MacOS,
|
||||
maybe_user_agent: Some(user_agent.into()),
|
||||
})
|
||||
}
|
||||
|
||||
/// Build a ready-to-use client carrying this profile's full fingerprint and
|
||||
/// identity headers. This is the usual entry point.
|
||||
pub fn build_client(&self) -> Result<Client, wreq::Error> {
|
||||
|
|
|
|||
Loading…
Reference in New Issue