use mobiler_core::*;
use serde::{Deserialize, Serialize};
const AUTHORIZE: &str = "https://accounts.example.com/authorize";
const TOKEN_ENDPOINT: &str = "https://accounts.example.com/token";
const CLIENT_ID: &str = "your-client-id";
const SCHEME: &str = "dev.test.smoke";
#[derive(Serialize, Deserialize, Clone)]
pub enum Msg {
LoginPressed,
LoggedIn(PluginResponse), Exchanged(PluginResponse), Stored(PluginResponse),
}
#[derive(Default, Serialize, Deserialize)]
pub struct Model {
pub signed_in: bool,
pub status: Option<String>,
pub state: String,
pub verifier: String,
}
impl MyApp {
fn handle(&self, msg: Msg, model: &mut Model, cx: &mut Cx<Msg>) {
match msg {
Msg::LoginPressed => {
let redirect = format!("{SCHEME}://oauth");
let url = format!(
"{AUTHORIZE}?response_type=code&client_id={CLIENT_ID}\
&redirect_uri={redirect}&scope=openid%20profile&state={}\
&code_challenge={}&code_challenge_method=S256",
model.state, model.verifier );
let input = serde_json::json!({ "url": url, "scheme": SCHEME }).to_string();
cx.plugin("oauth", "login", input, Msg::LoggedIn);
}
Msg::LoggedIn(r) => {
if !r.ok {
model.status = Some(format!("Login cancelled/failed: {}", r.output));
return;
}
let Some(code) = query_param(&r.output, "code") else {
model.status = Some("No authorization code in redirect.".into());
return;
};
let body = format!(
"grant_type=authorization_code&code={code}&client_id={CLIENT_ID}\
&redirect_uri={SCHEME}://oauth&code_verifier={}",
model.verifier
);
cx.post(TOKEN_ENDPOINT, body, Msg::Exchanged);
}
Msg::Exchanged(r) => {
if !r.ok {
model.status = Some(format!("Token exchange failed: {}", r.output));
return;
}
let input = serde_json::json!({ "key": "oauth_tokens", "value": r.output }).to_string();
cx.plugin("securestore", "set", input, Msg::Stored);
}
Msg::Stored(r) => {
model.signed_in = r.ok;
model.status = Some(if r.ok { "Signed in ✓".into() } else { format!("Save failed: {}", r.output) });
}
}
}
}
fn query_param(url: &str, key: &str) -> Option<String> {
let query = url.split_once('?').map(|(_, q)| q)?;
query.split('&').find_map(|pair| {
let (k, v) = pair.split_once('=')?;
(k == key).then(|| v.to_string())
})
}