Allow to login with OIDC

This commit is contained in:
traxys 2023-07-27 00:06:36 +02:00
parent f556fec3bb
commit 7a2ff7ad1d
12 changed files with 782 additions and 29 deletions

View file

@ -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"

View file

@ -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 {} }
}

View file

@ -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" }

View file

@ -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(),