Search code examples
rustzendeskzendesk-apirust-rocket

Rust: Thread panics when run inside an iframe but not in a regular browser tab


I'm playing around with building an app on Zendesk using Rust. Below is a server-side rust function that gets called in a route

pub fn oktaredirectprocessor(mut cookies: Cookies, code: String, state: String) -> bool {
    let cookie_state_string = cookies.get_private("state").unwrap().value().to_string();
    println!("cookie retrieved in oktaredirectprocessor is {}", cookie_state_string);
    if state != cookie_state_string {
        debug!("State did not match");
        false
    }else if code == ""{
        debug!("Code is empty");
        false
    }else{
        let oktarequest: Oktarequest = Oktarequest::new();
        let http = reqwest::Client::new();
        let config = oidc::discovery::discover(&http, oktarequest.issuer).expect("error in config-discovery-oidc");
        let jwks = oidc::discovery::jwks(&http, config.jwks_uri.clone()).expect("error in jwks-discovery-oidc");
        let provider = oidc::discovery::Discovered(config);
        let client = oidc::Client::new(oktarequest.id, oktarequest.secret, oktarequest.redirect, provider, jwks);
        let mut token = client.request_token(&http, code.as_str()).expect("error in request token-oidc");
        client.decode_token(&mut token.id_token).expect("error in decode token oidc");
        client.validate_token(&token.id_token, Some(cookies.get_private("nonce").unwrap().value()), None).expect("error in validate token oidc");               cookies.add_private(Cookie::new("access_token", token.access_token().to_string()));
        true
    }

}

It works when I run localhost directly from a browser tab. However, the thread panics if I run the app from inside Zendesk. Below is the backtrace. I'm a novice coder and don't understand it entirely.

thread '<unnamed>' panicked at 'called `Option::unwrap()` on a `None` value', src/main.rs:214:60
stack backtrace:
   0: rust_begin_unwind
             at /rustc/7efc097c4fe6e97f54a44cee91c56189e9ddb41c/library/std/src/panicking.rs:493:5
   1: core::panicking::panic_fmt
             at /rustc/7efc097c4fe6e97f54a44cee91c56189e9ddb41c/library/core/src/panicking.rs:92:14
   2: core::panicking::panic
             at /rustc/7efc097c4fe6e97f54a44cee91c56189e9ddb41c/library/core/src/panicking.rs:50:5
   3: core::option::Option<T>::unwrap
             at /rustc/7efc097c4fe6e97f54a44cee91c56189e9ddb41c/library/core/src/option.rs:386:21
   4: app_remote::oktaredirecthandler
             at ./src/main.rs:214:28
   5: app_remote::rocket_route_fn_oktaredirecthandler
             at ./src/main.rs:213:4
   6: core::ops::function::Fn::call
             at /rustc/7efc097c4fe6e97f54a44cee91c56189e9ddb41c/library/core/src/ops/function.rs:70:5
   7: <F as rocket::handler::Handler>::handle
             at /Users/nalinnarayan/.cargo/registry/src/github.com-1ecc6299db9ec823/rocket-0.4.5/src/handler.rs:177:9
   8: rocket::rocket::Rocket::route
             at /Users/nalinnarayan/.cargo/registry/src/github.com-1ecc6299db9ec823/rocket-0.4.5/src/rocket.rs:296:27
   9: rocket::rocket::Rocket::route_and_process
             at /Users/nalinnarayan/.cargo/registry/src/github.com-1ecc6299db9ec823/rocket-0.4.5/src/rocket.rs:242:34
  10: rocket::rocket::Rocket::dispatch
             at /Users/nalinnarayan/.cargo/registry/src/github.com-1ecc6299db9ec823/rocket-0.4.5/src/rocket.rs:217:28
  11: <rocket::rocket::Rocket as hyper::server::Handler>::handle
             at /Users/nalinnarayan/.cargo/registry/src/github.com-1ecc6299db9ec823/rocket-0.4.5/src/rocket.rs:82:24
  12: hyper::server::Worker<H>::keep_alive_loop
             at /Users/nalinnarayan/.cargo/registry/src/github.com-1ecc6299db9ec823/hyper-0.10.16/src/server/mod.rs:340:13
  13: hyper::server::Worker<H>::handle_connection
             at /Users/nalinnarayan/.cargo/registry/src/github.com-1ecc6299db9ec823/hyper-0.10.16/src/server/mod.rs:282:15
  14: hyper::server::handle::{{closure}}
             at /Users/nalinnarayan/.cargo/registry/src/github.com-1ecc6299db9ec823/hyper-0.10.16/src/server/mod.rs:242:34
  15: hyper::server::listener::spawn_with::{{closure}}
             at /Users/nalinnarayan/.cargo/registry/src/github.com-1ecc6299db9ec823/hyper-0.10.16/src/server/listener.rs:50:31

Weirdly, the problematic "cookies.get_private("state").unwrap()" works in another function even when run inside Zendesk. Below is the function where it works

fn oktalogin(mut cookies: Cookies) -> Redirect {
    let oktarequest: Oktarequest = Oktarequest::new();
    cookies.add_private(Cookie::new("state", oktarequest.state.clone()));
    cookies.add_private(Cookie::new("nonce", oktarequest.nonce.clone()));
    println!("stored state cookie from random gen is: {}", cookies.get_private("state").unwrap().value().to_string());
    let http = reqwest::Client::new();
    let config = oidc::discovery::discover(&http, oktarequest.issuer).expect("error in config-discovery-oidc");
    let jwks = oidc::discovery::jwks(&http, config.jwks_uri.clone()).expect("error in jwks-discovery-oidc");
    let provider = oidc::discovery::Discovered(config);
    let client = oidc::Client::new(oktarequest.id, oktarequest.secret, oktarequest.redirect, provider, jwks);
    let options = Options{
        scope: Some("openid profile email".to_string()),
        state: Some(oktarequest.state.clone()),
        nonce: Some(oktarequest.nonce.clone()),
        ..Default::default()
        };
    let auth_url = client.auth_url(&options);
    Redirect::to(auth_url.into_string())
}

My cargo.toml

[dependencies]
rocket = { version = "0.4.5", features = ["private-cookies"] }
rocket_codegen = "0.4.5"
serde = "1.0.116"
serde_derive = "1.0.116"
diesel = { version = "1.4.5", features = ["postgres"] }
dotenv = "0.15.0"
serde_urlencoded = "0.7.0"
oidc = "0.3.0"
ring = "=0.16.15"
log = "0.4.8"
rand = "0.7.3"
reqwest = "=0.9.24"
inth-oauth2 = "0.16.0"
url = "=1.7.2"
serde_json = "1.0"
console_log = { version = "0.2", features = ["color"] }

[dependencies.rocket_contrib]
version = "*"
default-features = false
features = ["tera_templates", "json"]

Solution

  • Why it worked in localhost was that cookie remained. To work correctly needs to add code when cookie doesn't exist, without unwrap().

    let cookie_state_string = match cookies.get_private("state") {
        Ok(v) => v.value().to_string(),
        Err(_) => return false,
    };