Implement client-side logout

This commit is contained in:
Valentin Tolmer 2021-05-23 17:07:02 +02:00
parent 054f970f91
commit e07efc9585
3 changed files with 52 additions and 13 deletions

View File

@ -83,4 +83,23 @@ impl HostService {
.body(Json(&request))?; .body(Json(&request))?;
FetchService::fetch_with_options(request, get_default_options(), handler.into()) FetchService::fetch_with_options(request, get_default_options(), handler.into())
} }
pub fn logout(callback: Callback<Result<()>>) -> Result<FetchTask> {
let url = "/auth/logout";
let handler = move |response: Response<Result<String>>| {
let (meta, maybe_data) = response.into_parts();
let message = maybe_data
.map_err(|e| anyhow!("Could not reach authentication server: {}", e))
.and_then(|data| {
if meta.status.is_success() {
Ok(())
} else {
Err(anyhow!("Could not logout: [{}]: {}", meta.status, data))
}
});
callback.emit(message)
};
let request = Request::post(url).body(yew::format::Nothing)?;
FetchService::fetch_with_options(request, get_default_options(), handler.into())
}
} }

View File

@ -23,8 +23,11 @@ pub fn set_cookie(cookie_name: &str, value: &str, expiration: &DateTime<Utc>) ->
d.dyn_into::<web_sys::HtmlDocument>() d.dyn_into::<web_sys::HtmlDocument>()
.map_err(|_| anyhow!("Document is not an HTMLDocument")) .map_err(|_| anyhow!("Document is not an HTMLDocument"))
})?; })?;
doc.set_cookie(&format!("{}={};expires={};sameSite=Strict", cookie_name, value, expiration)) doc.set_cookie(&format!(
.map_err(|_| anyhow!("Could not set cookie")) "{}={};expires={};sameSite=Strict",
cookie_name, value, expiration
))
.map_err(|_| anyhow!("Could not set cookie"))
} }
pub fn get_cookie(cookie_name: &str) -> Result<Option<String>> { pub fn get_cookie(cookie_name: &str) -> Result<Option<String>> {

View File

@ -1,10 +1,13 @@
use crate::cookies::delete_cookie; use crate::{api::HostService, cookies::delete_cookie};
use anyhow::Result;
use yew::prelude::*; use yew::prelude::*;
use yew::services::ConsoleService; use yew::services::{fetch::FetchTask, ConsoleService};
pub struct LogoutButton { pub struct LogoutButton {
link: ComponentLink<Self>, link: ComponentLink<Self>,
on_logged_out: Callback<()>, on_logged_out: Callback<()>,
// Used to keep the request alive long enough.
_task: Option<FetchTask>,
} }
#[derive(Clone, PartialEq, Properties)] #[derive(Clone, PartialEq, Properties)]
@ -13,7 +16,8 @@ pub struct Props {
} }
pub enum Msg { pub enum Msg {
Logout, LogoutRequested,
LogoutCompleted(Result<()>),
} }
impl Component for LogoutButton { impl Component for LogoutButton {
@ -24,21 +28,34 @@ impl Component for LogoutButton {
LogoutButton { LogoutButton {
link: link.clone(), link: link.clone(),
on_logged_out: props.on_logged_out, on_logged_out: props.on_logged_out,
_task: None,
} }
} }
fn update(&mut self, msg: Self::Message) -> ShouldRender { fn update(&mut self, msg: Self::Message) -> ShouldRender {
match msg { match msg {
Msg::Logout => match delete_cookie("user_id") { Msg::LogoutRequested => {
Err(e) => { match HostService::logout(self.link.callback(Msg::LogoutCompleted)) {
Ok(task) => self._task = Some(task),
Err(e) => ConsoleService::error(&e.to_string()),
};
false
}
Msg::LogoutCompleted(res) => {
if let Err(e) = res {
ConsoleService::error(&e.to_string()); ConsoleService::error(&e.to_string());
false
} }
Ok(()) => { match delete_cookie("user_id") {
self.on_logged_out.emit(()); Err(e) => {
true ConsoleService::error(&e.to_string());
false
}
Ok(()) => {
self.on_logged_out.emit(());
true
}
} }
}, }
} }
} }
@ -48,7 +65,7 @@ impl Component for LogoutButton {
fn view(&self) -> Html { fn view(&self) -> Html {
html! { html! {
<button onclick=self.link.callback(|_| { Msg::Logout })>{"Logout"}</button> <button onclick=self.link.callback(|_| { Msg::LogoutRequested })>{"Logout"}</button>
} }
} }
} }