Allow to login with OIDC
This commit is contained in:
parent
f556fec3bb
commit
7a2ff7ad1d
12 changed files with 782 additions and 29 deletions
|
|
@ -21,5 +21,6 @@ itertools = "0.11.0"
|
|||
log = "0.4.19"
|
||||
pulldown-cmark = "0.9.3"
|
||||
serde = { version = "1.0.164", features = ["derive"] }
|
||||
urlencoding = "2.1.3"
|
||||
uuid = "1.4.0"
|
||||
wasm-bindgen = "0.2.87"
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ use api::{
|
|||
LoginResponse, UserInfo,
|
||||
};
|
||||
use dioxus::prelude::*;
|
||||
use dioxus_router::{use_router, Route, Router};
|
||||
use dioxus_router::{use_route, use_router, Redirect, Route, Router};
|
||||
use gloo_storage::{errors::StorageError, LocalStorage, Storage};
|
||||
use itertools::Itertools;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
|
@ -33,6 +33,11 @@ const API_ROUTE: &str = match option_env!("REGALADE_API_SERVER_BASE") {
|
|||
Some(v) => v,
|
||||
};
|
||||
|
||||
const FRONTEND_ROOT: &str = match option_env!("REGALADE_FRONTEND_DOMAIN") {
|
||||
None => "http://localhost:8080",
|
||||
Some(v) => v,
|
||||
};
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! api {
|
||||
($($arg:tt)*) => {{
|
||||
|
|
@ -204,6 +209,37 @@ async fn do_login(username: String, password: String) -> anyhow::Result<()> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
async fn check_oidc() -> anyhow::Result<bool> {
|
||||
let rsp = gloo_net::http::Request::get(api!("login/has_oidc"))
|
||||
.send()
|
||||
.await?;
|
||||
|
||||
Ok(rsp.status() == 200)
|
||||
}
|
||||
|
||||
fn Openid(cx: Scope) -> Element {
|
||||
let has = use_future(cx, (), |()| check_oidc());
|
||||
|
||||
cx.render(match has.value().unwrap_or(&Ok(false)) {
|
||||
Ok(true) => {
|
||||
let route = api!("login/oidc").to_owned();
|
||||
let ret = urlencoding::encode(&format!("{FRONTEND_ROOT}/login/oidc")).to_string();
|
||||
rsx! {
|
||||
a {
|
||||
href: "{route}?return={ret}",
|
||||
class: "mt-1 w-100 btn btn-lg btn-primary",
|
||||
"Login with OpenID"
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(false) => rsx! {{}},
|
||||
Err(e) => {
|
||||
log::error!("Could not check oidc status: {e:?}");
|
||||
rsx! {{}}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn Login(cx: Scope) -> Element {
|
||||
let error = use_state(cx, || None::<String>);
|
||||
let router = use_router(cx);
|
||||
|
|
@ -256,6 +292,7 @@ fn Login(cx: Scope) -> Element {
|
|||
label { "for": "floatingPass", "Password" }
|
||||
}
|
||||
button { class: "w-100 btn btn-lg btn-primary", "type": "submit", "Login" }
|
||||
Openid {}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
@ -505,9 +542,38 @@ fn Index(cx: Scope) -> Element {
|
|||
cx.render(rsx! {"INDEX"})
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct OidcQuery {
|
||||
token: String,
|
||||
username: String,
|
||||
}
|
||||
|
||||
fn OidcRedirect(cx: Scope) -> Element {
|
||||
let auth = use_route(cx).query::<OidcQuery>();
|
||||
|
||||
cx.render(match auth {
|
||||
None => rsx! {"No authentication query, internal error."},
|
||||
Some(v) => {
|
||||
match LocalStorage::set(
|
||||
"token",
|
||||
LoginInfo {
|
||||
token: v.token,
|
||||
name: v.username,
|
||||
},
|
||||
) {
|
||||
Ok(_) => {
|
||||
gloo_utils::window().location().replace("/").unwrap();
|
||||
rsx! {{}}
|
||||
}
|
||||
Err(_) => rsx! {"Could not store authentication, try again."},
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn App(cx: Scope) -> Element {
|
||||
cx.render(rsx! {
|
||||
Router {
|
||||
Router {
|
||||
Route { to: Page::Home.to(),
|
||||
RegaladeSidebar { current: Page::Home, Index {} }
|
||||
}
|
||||
|
|
@ -524,6 +590,7 @@ fn App(cx: Scope) -> Element {
|
|||
RegaladeSidebar { current: Page::RecipeList, recipe::RecipeView {} }
|
||||
}
|
||||
Route { to: "/login", Login {} }
|
||||
Route { to: "/login/oidc", OidcRedirect {} }
|
||||
Route { to: "/household_selection",
|
||||
LoginRedirect { HouseholdSelection {} }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -341,15 +341,15 @@ pub fn RecipeCreator(cx: Scope) -> Element {
|
|||
}
|
||||
div {
|
||||
h2 { "Steps" }
|
||||
div {class: "text-start",
|
||||
textarea {
|
||||
class: "form-control",
|
||||
id: "steps-area",
|
||||
value: "{steps}",
|
||||
rows: "10",
|
||||
oninput: move |e| steps.set(e.value.clone())
|
||||
div { class: "text-start",
|
||||
textarea {
|
||||
class: "form-control",
|
||||
id: "steps-area",
|
||||
value: "{steps}",
|
||||
rows: "10",
|
||||
oninput: move |e| steps.set(e.value.clone())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
hr {}
|
||||
ModalToggleButton { class: "btn btn-lg btn-primary", modal_id: "newRcpModal", "Create Recipe" }
|
||||
|
|
|
|||
|
|
@ -700,9 +700,7 @@ fn RecipeViewer(cx: Scope<RecipeViewerProps>) -> Element {
|
|||
hr {}
|
||||
div { class: "text-start",
|
||||
h2 { "Steps" }
|
||||
div {
|
||||
dangerous_inner_html: "{steps_rendered}"
|
||||
}
|
||||
div { dangerous_inner_html: "{steps_rendered}" }
|
||||
EditSteps {
|
||||
recipe: cx.props.id,
|
||||
refresh: cx.props.refresh.clone(),
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue