app: Implement login refresh

This commit is contained in:
Valentin Tolmer 2022-05-11 16:35:22 +02:00 committed by nitnelave
parent ebffc1c086
commit b54fe9128d
4 changed files with 112 additions and 59 deletions

View File

@ -85,7 +85,7 @@ impl Component for App {
} }
if self.user_info.is_none() { if self.user_info.is_none() {
self.route_dispatcher self.route_dispatcher
.send(RouteRequest::ReplaceRoute(Route::new_no_state("/login"))); .send(RouteRequest::ReplaceRoute(Route::from(AppRoute::Login)));
} }
true true
} }
@ -138,7 +138,7 @@ impl App {
match &self.user_info { match &self.user_info {
None => { None => {
self.route_dispatcher self.route_dispatcher
.send(RouteRequest::ReplaceRoute(Route::new_no_state("/login"))); .send(RouteRequest::ReplaceRoute(Route::from(AppRoute::Login)));
} }
Some((user_name, is_admin)) => match &self.redirect_to { Some((user_name, is_admin)) => match &self.redirect_to {
Some(url) => { Some(url) => {
@ -148,7 +148,7 @@ impl App {
None => { None => {
if *is_admin { if *is_admin {
self.route_dispatcher self.route_dispatcher
.send(RouteRequest::ReplaceRoute(Route::new_no_state("/users"))); .send(RouteRequest::ReplaceRoute(Route::from(AppRoute::ListUsers)));
} else { } else {
self.route_dispatcher self.route_dispatcher
.send(RouteRequest::ReplaceRoute(Route::from( .send(RouteRequest::ReplaceRoute(Route::from(

View File

@ -15,6 +15,7 @@ use yew_form_derive::Model;
pub struct LoginForm { pub struct LoginForm {
common: CommonComponentParts<Self>, common: CommonComponentParts<Self>,
form: Form<FormModel>, form: Form<FormModel>,
refreshing: bool,
} }
/// The fields of the form, with the constraints. /// The fields of the form, with the constraints.
@ -34,6 +35,7 @@ pub struct Props {
pub enum Msg { pub enum Msg {
Update, Update,
Submit, Submit,
AuthenticationRefreshResponse(Result<(String, bool)>),
AuthenticationStartResponse( AuthenticationStartResponse(
( (
opaque::client::login::ClientLogin, opaque::client::login::ClientLogin,
@ -99,6 +101,14 @@ impl CommonComponent<LoginForm> for LoginForm {
.emit(user_info.context("Could not log in")?); .emit(user_info.context("Could not log in")?);
Ok(true) Ok(true)
} }
Msg::AuthenticationRefreshResponse(user_info) => {
self.refreshing = false;
self.common.cancel_task();
if let Ok(user_info) = user_info {
self.common.on_logged_in.emit(user_info);
}
Ok(true)
}
} }
} }
@ -112,10 +122,19 @@ impl Component for LoginForm {
type Properties = Props; type Properties = Props;
fn create(props: Self::Properties, link: ComponentLink<Self>) -> Self { fn create(props: Self::Properties, link: ComponentLink<Self>) -> Self {
LoginForm { let mut app = LoginForm {
common: CommonComponentParts::<Self>::create(props, link), common: CommonComponentParts::<Self>::create(props, link),
form: Form::<FormModel>::new(FormModel::default()), form: Form::<FormModel>::new(FormModel::default()),
refreshing: true,
};
if let Err(e) =
app.common
.call_backend(HostService::refresh, (), Msg::AuthenticationRefreshResponse)
{
ConsoleService::debug(&format!("Could not refresh auth: {}", e));
app.refreshing = false;
} }
app
} }
fn update(&mut self, msg: Self::Message) -> ShouldRender { fn update(&mut self, msg: Self::Message) -> ShouldRender {
@ -128,6 +147,13 @@ impl Component for LoginForm {
fn view(&self) -> Html { fn view(&self) -> Html {
type Field = yew_form::Field<FormModel>; type Field = yew_form::Field<FormModel>;
if self.refreshing {
html! {
<div>
<img src={"spinner.gif"} alt={"Loading"} />
</div>
}
} else {
html! { html! {
<form <form
class="form center-block col-sm-4 col-offset-4"> class="form center-block col-sm-4 col-offset-4">
@ -187,4 +213,5 @@ impl Component for LoginForm {
</form> </form>
} }
} }
}
} }

View File

@ -227,6 +227,32 @@ impl HostService {
) )
} }
pub fn refresh(_request: (), callback: Callback<Result<(String, bool)>>) -> Result<FetchTask> {
let set_cookies = |jwt_claims: JWTClaims| {
let is_admin = jwt_claims.groups.contains("lldap_admin");
set_cookie("user_id", &jwt_claims.user, &jwt_claims.exp)
.map(|_| set_cookie("is_admin", &is_admin.to_string(), &jwt_claims.exp))
.map(|_| (jwt_claims.user.clone(), is_admin))
.context("Error clearing cookie")
};
let parse_token = move |data: String| {
serde_json::from_str::<login::ServerLoginResponse>(&data)
.context("Could not parse response")
.and_then(|r| {
get_claims_from_jwt(r.token.as_str())
.context("Could not parse response")
.and_then(set_cookies)
})
};
call_server(
"/auth/refresh",
yew::format::Nothing,
callback,
"Could not start authentication: ",
parse_token,
)
}
// The `_request` parameter is to make it the same shape as the other functions. // The `_request` parameter is to make it the same shape as the other functions.
pub fn logout(_request: (), callback: Callback<Result<()>>) -> Result<FetchTask> { pub fn logout(_request: (), callback: Callback<Result<()>>) -> Result<FetchTask> {
call_server_empty_response_with_error_message( call_server_empty_response_with_error_message(

BIN
app/static/spinner.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB