feat: authentication

This commit is contained in:
newt 2024-11-27 00:22:53 +00:00
parent 76c294b709
commit c30bfdb2eb
14 changed files with 501 additions and 50 deletions

View file

@ -4,7 +4,8 @@
"node_modules": true,
".svelte-kit": true,
"**/target": true,
"build": true
"build": true,
"src-tauri/gen": true
},
"rust-analyzer.linkedProjects": [
"src-tauri/Cargo.toml"

View file

@ -14,4 +14,4 @@ cargo +cmd:
[doc('Run tauri command')]
tauri +cmd:
pnpm run tauri {cmd}
pnpm run tauri {{cmd}}

View file

@ -14,6 +14,7 @@
"license": "MIT",
"dependencies": {
"@tauri-apps/api": "^2",
"@tauri-apps/plugin-http": "~2",
"@tauri-apps/plugin-shell": "^2"
},
"devDependencies": {

View file

@ -11,6 +11,9 @@ importers:
'@tauri-apps/api':
specifier: ^2
version: 2.1.1
'@tauri-apps/plugin-http':
specifier: ~2
version: 2.0.1
'@tauri-apps/plugin-shell':
specifier: ^2
version: 2.0.1
@ -439,6 +442,9 @@ packages:
engines: {node: '>= 10'}
hasBin: true
'@tauri-apps/plugin-http@2.0.1':
resolution: {integrity: sha512-j6IA3pVBybSCwPpsihpX4z8bs6PluuGtr06ahL/xy4D8HunNBTmRmadJrFOQi0gOAbaig4MkQ15nzNLBLy8R1A==}
'@tauri-apps/plugin-shell@2.0.1':
resolution: {integrity: sha512-akU1b77sw3qHiynrK0s930y8zKmcdrSD60htjH+mFZqv5WaakZA/XxHR3/sF1nNv9Mgmt/Shls37HwnOr00aSw==}
@ -1318,6 +1324,10 @@ snapshots:
'@tauri-apps/cli-win32-ia32-msvc': 2.1.0
'@tauri-apps/cli-win32-x64-msvc': 2.1.0
'@tauri-apps/plugin-http@2.0.1':
dependencies:
'@tauri-apps/api': 2.1.1
'@tauri-apps/plugin-shell@2.0.1':
dependencies:
'@tauri-apps/api': 2.1.1

352
src-tauri/Cargo.lock generated
View file

@ -85,6 +85,12 @@ dependencies = [
"system-deps",
]
[[package]]
name = "atomic-waker"
version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0"
[[package]]
name = "autocfg"
version = "1.4.0"
@ -342,7 +348,7 @@ dependencies = [
"bitflags 2.6.0",
"block",
"cocoa-foundation",
"core-foundation",
"core-foundation 0.10.0",
"core-graphics",
"foreign-types",
"libc",
@ -357,7 +363,7 @@ checksum = "e14045fb83be07b5acf1c0884b2180461635b433455fa35d1cd6f17f1450679d"
dependencies = [
"bitflags 2.6.0",
"block",
"core-foundation",
"core-foundation 0.10.0",
"core-graphics-types",
"libc",
"objc",
@ -408,6 +414,16 @@ dependencies = [
"url",
]
[[package]]
name = "core-foundation"
version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f"
dependencies = [
"core-foundation-sys",
"libc",
]
[[package]]
name = "core-foundation"
version = "0.10.0"
@ -431,7 +447,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fa95a34622365fa5bbf40b20b75dba8dfa8c94c734aea8ac9a5ca38af14316f1"
dependencies = [
"bitflags 2.6.0",
"core-foundation",
"core-foundation 0.10.0",
"core-graphics-types",
"foreign-types",
"libc",
@ -444,7 +460,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d44a101f213f6c4cdc1853d4b78aef6db6bdfa3468798cc1d9912f4735013eb"
dependencies = [
"bitflags 2.6.0",
"core-foundation",
"core-foundation 0.10.0",
"libc",
]
@ -563,6 +579,12 @@ dependencies = [
"syn 2.0.89",
]
[[package]]
name = "data-url"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c297a1c74b71ae29df00c3e22dd9534821d60eb9af5a0192823fa2acea70c2a"
[[package]]
name = "deranged"
version = "0.3.11"
@ -707,14 +729,15 @@ name = "echoed"
version = "0.1.0"
dependencies = [
"regex",
"reqwest",
"reqwest_cookie_store",
"serde",
"serde_json",
"tauri",
"tauri-build",
"tauri-plugin-http",
"tauri-plugin-shell",
"thiserror 2.0.3",
"url",
]
[[package]]
@ -1068,8 +1091,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7"
dependencies = [
"cfg-if",
"js-sys",
"libc",
"wasi 0.11.0+wasi-snapshot-preview1",
"wasm-bindgen",
]
[[package]]
@ -1226,6 +1251,25 @@ dependencies = [
"syn 2.0.89",
]
[[package]]
name = "h2"
version = "0.4.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ccae279728d634d083c00f6099cb58f01cc99c145b84b8be2f6c74618d79922e"
dependencies = [
"atomic-waker",
"bytes",
"fnv",
"futures-core",
"futures-sink",
"http",
"indexmap 2.6.0",
"slab",
"tokio",
"tokio-util",
"tracing",
]
[[package]]
name = "hashbrown"
version = "0.12.3"
@ -1325,6 +1369,7 @@ dependencies = [
"bytes",
"futures-channel",
"futures-util",
"h2",
"http",
"http-body",
"httparse",
@ -1335,6 +1380,24 @@ dependencies = [
"want",
]
[[package]]
name = "hyper-rustls"
version = "0.27.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08afdbb5c31130e3034af566421053ab03787c640246a446327f550d11bcb333"
dependencies = [
"futures-util",
"http",
"hyper",
"hyper-util",
"rustls",
"rustls-pki-types",
"tokio",
"tokio-rustls",
"tower-service",
"webpki-roots",
]
[[package]]
name = "hyper-util"
version = "0.1.10"
@ -2591,6 +2654,58 @@ dependencies = [
"memchr",
]
[[package]]
name = "quinn"
version = "0.11.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62e96808277ec6f97351a2380e6c25114bc9e67037775464979f3037c92d05ef"
dependencies = [
"bytes",
"pin-project-lite",
"quinn-proto",
"quinn-udp",
"rustc-hash",
"rustls",
"socket2",
"thiserror 2.0.3",
"tokio",
"tracing",
]
[[package]]
name = "quinn-proto"
version = "0.11.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2fe5ef3495d7d2e377ff17b1a8ce2ee2ec2a18cde8b6ad6619d65d0701c135d"
dependencies = [
"bytes",
"getrandom 0.2.15",
"rand 0.8.5",
"ring",
"rustc-hash",
"rustls",
"rustls-pki-types",
"slab",
"thiserror 2.0.3",
"tinyvec",
"tracing",
"web-time",
]
[[package]]
name = "quinn-udp"
version = "0.5.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7d5a626c6807713b15cac82a6acaccd6043c9a5408c24baae07611fec3f243da"
dependencies = [
"cfg_aliases",
"libc",
"once_cell",
"socket2",
"tracing",
"windows-sys 0.59.0",
]
[[package]]
name = "quote"
version = "1.0.37"
@ -2746,12 +2861,15 @@ dependencies = [
"bytes",
"cookie",
"cookie_store",
"encoding_rs",
"futures-core",
"futures-util",
"h2",
"http",
"http-body",
"http-body-util",
"hyper",
"hyper-rustls",
"hyper-util",
"ipnet",
"js-sys",
@ -2761,11 +2879,17 @@ dependencies = [
"once_cell",
"percent-encoding",
"pin-project-lite",
"quinn",
"rustls",
"rustls-pemfile",
"rustls-pki-types",
"serde",
"serde_json",
"serde_urlencoded",
"sync_wrapper",
"system-configuration",
"tokio",
"tokio-rustls",
"tokio-util",
"tower-service",
"url",
@ -2773,6 +2897,7 @@ dependencies = [
"wasm-bindgen-futures",
"wasm-streams",
"web-sys",
"webpki-roots",
"windows-registry",
]
@ -2788,12 +2913,33 @@ dependencies = [
"url",
]
[[package]]
name = "ring"
version = "0.17.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d"
dependencies = [
"cc",
"cfg-if",
"getrandom 0.2.15",
"libc",
"spin",
"untrusted",
"windows-sys 0.52.0",
]
[[package]]
name = "rustc-demangle"
version = "0.1.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f"
[[package]]
name = "rustc-hash"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "583034fd73374156e66797ed8e5b0d5690409c9226b22d87cb7f19821c05d152"
[[package]]
name = "rustc_version"
version = "0.4.1"
@ -2803,6 +2949,49 @@ dependencies = [
"semver",
]
[[package]]
name = "rustls"
version = "0.23.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c9cc1d47e243d655ace55ed38201c19ae02c148ae56412ab8750e8f0166ab7f"
dependencies = [
"once_cell",
"ring",
"rustls-pki-types",
"rustls-webpki",
"subtle",
"zeroize",
]
[[package]]
name = "rustls-pemfile"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50"
dependencies = [
"rustls-pki-types",
]
[[package]]
name = "rustls-pki-types"
version = "1.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "16f1201b3c9a7ee8039bcadc17b7e605e2945b27eee7631788c1bd2b0643674b"
dependencies = [
"web-time",
]
[[package]]
name = "rustls-webpki"
version = "0.102.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "64ca1bc8749bd4cf37b5ce386cc146580777b4e8572c7b97baf22c83f444bee9"
dependencies = [
"ring",
"rustls-pki-types",
"untrusted",
]
[[package]]
name = "ryu"
version = "1.0.18"
@ -3140,6 +3329,12 @@ dependencies = [
"system-deps",
]
[[package]]
name = "spin"
version = "0.9.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67"
[[package]]
name = "stable_deref_trait"
version = "1.2.0"
@ -3178,6 +3373,12 @@ version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
[[package]]
name = "subtle"
version = "2.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
[[package]]
name = "swift-rs"
version = "1.0.7"
@ -3231,6 +3432,27 @@ dependencies = [
"syn 2.0.89",
]
[[package]]
name = "system-configuration"
version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b"
dependencies = [
"bitflags 2.6.0",
"core-foundation 0.9.4",
"system-configuration-sys",
]
[[package]]
name = "system-configuration-sys"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4"
dependencies = [
"core-foundation-sys",
"libc",
]
[[package]]
name = "system-deps"
version = "6.2.2"
@ -3252,7 +3474,7 @@ checksum = "6682a07cf5bab0b8a2bd20d0a542917ab928b5edb75ebd4eda6b05cbaab872da"
dependencies = [
"bitflags 2.6.0",
"cocoa",
"core-foundation",
"core-foundation 0.10.0",
"core-graphics",
"crossbeam-channel",
"dispatch",
@ -3430,6 +3652,49 @@ dependencies = [
"walkdir",
]
[[package]]
name = "tauri-plugin-fs"
version = "2.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "96ba7d46e86db8c830d143ef90ab5a453328365b0cc834c24edea4267b16aba0"
dependencies = [
"anyhow",
"dunce",
"glob",
"percent-encoding",
"schemars",
"serde",
"serde_json",
"serde_repr",
"tauri",
"tauri-plugin",
"thiserror 1.0.69",
"url",
"uuid",
]
[[package]]
name = "tauri-plugin-http"
version = "2.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c752aee1b00ec3c4d4f440095995d9bd2c640b478f2067d1fba388900b82eb96"
dependencies = [
"data-url",
"http",
"regex",
"reqwest",
"schemars",
"serde",
"serde_json",
"tauri",
"tauri-plugin",
"tauri-plugin-fs",
"thiserror 1.0.69",
"tokio",
"url",
"urlpattern",
]
[[package]]
name = "tauri-plugin-shell"
version = "2.0.2"
@ -3641,6 +3906,21 @@ dependencies = [
"zerovec",
]
[[package]]
name = "tinyvec"
version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938"
dependencies = [
"tinyvec_macros",
]
[[package]]
name = "tinyvec_macros"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
[[package]]
name = "tokio"
version = "1.41.1"
@ -3653,9 +3933,32 @@ dependencies = [
"mio",
"pin-project-lite",
"socket2",
"tokio-macros",
"windows-sys 0.52.0",
]
[[package]]
name = "tokio-macros"
version = "2.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.89",
]
[[package]]
name = "tokio-rustls"
version = "0.26.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c7bc40d0e5a97695bb96e27995cd3a08538541b0a846f65bba7a359f36700d4"
dependencies = [
"rustls",
"rustls-pki-types",
"tokio",
]
[[package]]
name = "tokio-util"
version = "0.7.12"
@ -3852,10 +4155,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493"
[[package]]
name = "url"
version = "2.5.3"
name = "untrusted"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8d157f1b96d14500ffdc1f10ba712e780825526c03d9a49b4d0324b0d9113ada"
checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1"
[[package]]
name = "url"
version = "2.5.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60"
dependencies = [
"form_urlencoded",
"idna",
@ -4056,6 +4365,16 @@ dependencies = [
"wasm-bindgen",
]
[[package]]
name = "web-time"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb"
dependencies = [
"js-sys",
"wasm-bindgen",
]
[[package]]
name = "webkit2gtk"
version = "2.0.1"
@ -4100,6 +4419,15 @@ dependencies = [
"system-deps",
]
[[package]]
name = "webpki-roots"
version = "0.26.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5d642ff16b7e79272ae451b7322067cdc17cadf68c23264be9d94a32319efe7e"
dependencies = [
"rustls-pki-types",
]
[[package]]
name = "webview2-com"
version = "0.33.0"
@ -4649,6 +4977,12 @@ dependencies = [
"synstructure",
]
[[package]]
name = "zeroize"
version = "1.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde"
[[package]]
name = "zerovec"
version = "0.10.4"

View file

@ -18,10 +18,11 @@ tauri = { version = "2", features = [] }
tauri-plugin-shell = "2"
serde = { version = "1", features = ["derive"] }
serde_json = "1"
reqwest = { version = "0.12.9", default-features = false, features = ["json", "multipart"] }
reqwest_cookie_store = { path = "../reqwest_cookie_store_tokio" }
thiserror = "2.0.3"
regex = { version = "1.11.1", default-features = false, features = ["perf", "std"] }
url = { version = "2.5.4", default-features = false }
tauri-plugin-http = { version = "2", features = ["multipart", "json"] }
[profile.dev]
incremental = true # Compile your binary in smaller steps.

View file

@ -2,9 +2,12 @@
"$schema": "../gen/schemas/desktop-schema.json",
"identifier": "default",
"description": "Capability for the main window",
"windows": ["main"],
"windows": [
"main"
],
"permissions": [
"core:default",
"shell:allow-open"
"shell:allow-open",
"http:default"
]
}
}

View file

@ -0,0 +1 @@
pub mod auth;

View file

@ -0,0 +1,89 @@
use crate::prelude::*;
use tauri_plugin_http::reqwest::multipart::Form;
use url::Url;
#[derive(Debug, thiserror::Error)]
pub enum Error {
#[error("Failed to find {0}.")]
Find(&'static str),
#[error("Invalid details.")]
InvalidDetails,
#[error("Invalid URL.")]
InvalidUrl,
}
/// Authenticate with echo360
#[command]
pub async fn authenticate(
email: Cow<'static, str>,
password: Cow<'static, str>,
url: Cow<'static, str>,
state: State<'_>,
) -> Result<()> {
let tauri_state = state;
let state = state.lock().await;
let parsed_url = Url::parse(&url)?;
let domain = parsed_url.domain().ok_or(Error::InvalidUrl)?;
// get csrf token
state.client.get(url.to_string()).send().await?;
let csrf_token = state
.jar
.lock()
.await
.get(domain, "/", "PLAY_SESSION")
.map(|cookie| {
let value = cookie.value().to_string();
value
.split('&')
.nth(1)
.map(|part| part.split('=').last())
.flatten()
.map(|x| x.to_string())
})
.flatten()
.ok_or(Error::Find("CSRF token"))?;
// authenticate
state
.client
.post(format!("{}/login", url))
.query(&[("csrfToken", csrf_token)])
.multipart(Form::new().text("email", email).text("password", password))
.send()
.await?;
// check if authentication was successful
drop(state);
if is_authenticated(url, tauri_state).await? {
Ok(())
} else {
Err(Error::InvalidDetails.into())
}
}
/// Check if the user is authenticated with echo360
#[command]
pub async fn is_authenticated(url: Cow<'static, str>, state: State<'_>) -> Result<bool> {
let state = state.lock().await;
// fetch courses page
let html = state
.client
.get(format!("{}/courses", url))
.send()
.await?
.text()
.await?;
// the courses page redirects to a sign in page if the user is not authenticated
// if we can find the user's first name somewhere in the html, then the user must be authenticated
let authenticated = regex::Regex::new(r#"\\"firstName\\":\\"(\w+)\\""#)?
.captures(&html)
.is_some();
Ok(authenticated)
}

View file

@ -1,10 +1,15 @@
use reqwest_cookie_store::CookieStoreMutex;
use std::sync::Arc;
use tauri::async_runtime::Mutex;
use tauri_plugin_http::reqwest;
// todo: reqwest client for each profile, or a way to swap the cookie jar used by a reqwest client
/// Tauri application state
pub struct AppState {
/// Reqwest client connected to profile cookie jar
pub client: reqwest::Client,
/// Cookie jar attached to the reqwest client
pub jar: Arc<CookieStoreMutex>,
}
/// Wrapper around `tauri::State` to use custom app state.

View file

@ -1,13 +1,23 @@
use crate::commands::*;
pub type Result<T, E = Error> = std::result::Result<T, E>;
#[derive(Debug, thiserror::Error)]
pub enum Error {
#[error(transparent)]
Tauri(#[from] tauri::Error),
#[error(transparent)]
Reqwest(#[from] reqwest::Error),
#[error(transparent)]
Http(#[from] tauri_plugin_http::reqwest::Error),
#[error("Failed to parse regex.")]
Regex(#[from] regex::Error),
#[error("Failed to parse url.")]
Url(#[from] url::ParseError),
#[error(transparent)]
Auth(#[from] auth::Error),
}
impl serde::Serialize for Error {

View file

@ -1,46 +1,23 @@
use data::{AppState, State};
use error::Result;
use commands::auth::*;
use data::AppState;
use reqwest_cookie_store::CookieStoreMutex;
use std::sync::Arc;
use tauri::{async_runtime::Mutex, Manager};
mod commands;
mod data;
mod error;
mod prelude;
#[macro_use]
extern crate tauri;
const DEFAULT_ECHO360: &str = "https://echo360.org.uk";
/// Check if the user is authenticated with echo360
#[command(rename_all = "snake_case")]
async fn is_authenticated(state: State<'_>) -> Result<bool> {
// get client
let state = state.lock().await;
// fetch courses page
let html = state
.client
.get(format!("{}/courses", DEFAULT_ECHO360))
.send()
.await?
.text()
.await?;
// the courses page redirects to a sign in page if the user is not authenticated
// if we can find the user's first name somewhere in the html, then the user must be authenticated
let authenticated = regex::Regex::new(r#"\\"firstName\\":\\"(\w+)\\""#)?
.captures(&html)
.is_some();
Ok(authenticated)
}
#[cfg_attr(mobile, mobile_entry_point)]
pub fn run() {
tauri::Builder::default()
.plugin(tauri_plugin_http::init())
.plugin(tauri_plugin_shell::init())
.invoke_handler(tauri::generate_handler![is_authenticated])
.invoke_handler(tauri::generate_handler![authenticate, is_authenticated])
.setup(|app| {
// todo: load profile file, or create if it does not exist
@ -48,12 +25,12 @@ pub fn run() {
// todo: load cookies into cookie jar
//? https://docs.rs/tauri/1.8.1/tauri/api/path/fn.app_config_dir.html
let jar = Arc::new(CookieStoreMutex::default());
let client = reqwest::Client::builder()
let client = tauri_plugin_http::reqwest::Client::builder()
.cookie_provider(jar.clone())
.build()?;
// store app state
app.manage(Mutex::new(AppState { client }));
app.manage(Mutex::new(AppState { client, jar }));
Ok(())
})

2
src-tauri/src/prelude.rs Normal file
View file

@ -0,0 +1,2 @@
pub use crate::{data::State, error::Result};
pub use std::borrow::Cow;

View file

@ -1,7 +1,20 @@
<script lang="ts">
import { invoke } from "@tauri-apps/api/core";
let is_authenticated: Promise<boolean> = invoke("is_authenticated");
let email = $state();
let password = $state();
// todo: ensure domain is valid
let url = $state("https://echo360.org.uk");
// svelte-ignore state_referenced_locally
let is_authenticated: Promise<boolean> = $state(invoke("is_authenticated", { url }));
async function login() {
// todo: ensure email is valid
// todo: ensure password is valid
console.log(await invoke("authenticate", { email, password, url }));
is_authenticated = invoke("is_authenticated", { url });
}
</script>
{#await is_authenticated}
@ -10,7 +23,11 @@
{#if authenticated}
<h1 class="text-green-500">Authenticated</h1>
{:else}
<h1 class="text-red-500">Unauthenticated</h1>
<form>
<input type="email" bind:value={email} placeholder="Email" />
<input type="password" bind:value={password} placeholder="Password" />
<button onclick={login}>Login</button>
</form>
{/if}
{/await}