Log the user out if the API returns a 401
Diff
Cargo.lock | 150 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------------------
chartered-frontend/src/index.tsx | 12 +++++++++---
chartered-frontend/src/useAuth.tsx | 31 ++++++++++++++++++++-----------
chartered-frontend/src/util.tsx | 20 ++++++++++++++------
chartered-web/src/main.rs | 3 ++-
chartered-web/src/endpoints/mod.rs | 6 ++++--
chartered-web/src/middleware/auth.rs | 17 ++++++++++++-----
chartered-web/src/endpoints/web_api/organisations/crud.rs | 2 +-
8 files changed, 134 insertions(+), 107 deletions(-)
@@ -32,9 +32,9 @@
[[package]]
name = "anyhow"
version = "1.0.43"
version = "1.0.44"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "28ae2b3dec75a406790005a200b1bd89785afc02517a00ca99ecfe093ee9e6cf"
checksum = "61604a8f862e1d5c3229fdd78f8b02c68dcf73a4c4b05fd636d12240aaa242c1"
[[package]]
name = "async-trait"
@@ -66,9 +66,9 @@
[[package]]
name = "axum"
version = "0.2.3"
version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4e2423522684032529c51d209740c77d57cc56bfef7b9a18630f919a974d2616"
checksum = "b6519a24c07bab4effe38e226c447faef56869f99aa66aa92502aba7ad47b168"
dependencies = [
"async-trait",
"bitflags",
@@ -100,9 +100,9 @@
[[package]]
name = "base64ct"
version = "1.0.1"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a32fd6af2b5827bce66c29053ba0e7c42b9dcab01835835058558c10851a46b"
checksum = "40a96587c05c810ddbb79e2674d519cff1379517e7b91d166dff7a7cc0e9af6e"
[[package]]
name = "bcrypt-pbkdf"
@@ -179,9 +179,9 @@
[[package]]
name = "cc"
version = "1.0.69"
version = "1.0.70"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e70cc2f62c6ce1868963827bd677764c62d07c3d9a3e1fb1177ee1a9ab199eb2"
checksum = "d26a6ce4b6a484fa3edb70f7efa6fc430fd2b87285fe8b84304fd0936faa0dc0"
[[package]]
name = "cfg-if"
@@ -388,9 +388,9 @@
[[package]]
name = "diesel"
version = "1.4.7"
version = "1.4.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bba51ca66f57261fd17cadf8b73e4775cc307d0521d855de3f5de91a8f074e0e"
checksum = "b28135ecf6b7d446b43e27e225622a038cc4e2930a1022f51cdb97ada19b8e4d"
dependencies = [
"byteorder",
"chrono",
@@ -487,9 +487,9 @@
[[package]]
name = "flate2"
version = "1.0.20"
version = "1.0.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cd3aec53de10fe96d7d8c565eb17f2c687bb5518a2ec453b5b1252964526abe0"
checksum = "1e6988e897c1c9c485f43b47a529cef42fde0547f9d8d41a7062518f1d8fc53f"
dependencies = [
"cfg-if",
"crc32fast",
@@ -537,9 +537,9 @@
[[package]]
name = "futures"
version = "0.3.16"
version = "0.3.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1adc00f486adfc9ce99f77d717836f0c5aa84965eb0b4f051f4e83f7cab53f8b"
checksum = "a12aa0eb539080d55c3f2d45a67c3b58b6b0773c1a3ca2dfec66d58c97fd66ca"
dependencies = [
"futures-channel",
"futures-core",
@@ -552,9 +552,9 @@
[[package]]
name = "futures-channel"
version = "0.3.16"
version = "0.3.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "74ed2411805f6e4e3d9bc904c95d5d423b89b3b25dc0250aa74729de20629ff9"
checksum = "5da6ba8c3bb3c165d3c7319fc1cc8304facf1fb8db99c5de877183c08a273888"
dependencies = [
"futures-core",
"futures-sink",
@@ -562,15 +562,15 @@
[[package]]
name = "futures-core"
version = "0.3.16"
version = "0.3.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af51b1b4a7fdff033703db39de8802c673eb91855f2e0d47dcf3bf2c0ef01f99"
checksum = "88d1c26957f23603395cd326b0ffe64124b818f4449552f960d815cfba83a53d"
[[package]]
name = "futures-executor"
version = "0.3.16"
version = "0.3.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4d0d535a57b87e1ae31437b892713aee90cd2d7b0ee48727cd11fc72ef54761c"
checksum = "45025be030969d763025784f7f355043dc6bc74093e4ecc5000ca4dc50d8745c"
dependencies = [
"futures-core",
"futures-task",
@@ -579,15 +579,15 @@
[[package]]
name = "futures-io"
version = "0.3.16"
version = "0.3.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b0e06c393068f3a6ef246c75cdca793d6a46347e75286933e5e75fd2fd11582"
checksum = "522de2a0fe3e380f1bc577ba0474108faf3f6b18321dbf60b3b9c39a75073377"
[[package]]
name = "futures-macro"
version = "0.3.16"
version = "0.3.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c54913bae956fb8df7f4dc6fc90362aa72e69148e3f39041fbe8742d21e0ac57"
checksum = "18e4a4b95cea4b4ccbcf1c5675ca7c4ee4e9e75eb79944d07defde18068f79bb"
dependencies = [
"autocfg",
"proc-macro-hack",
@@ -598,21 +598,21 @@
[[package]]
name = "futures-sink"
version = "0.3.16"
version = "0.3.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c0f30aaa67363d119812743aa5f33c201a7a66329f97d1a887022971feea4b53"
checksum = "36ea153c13024fe480590b3e3d4cad89a0cfacecc24577b68f86c6ced9c2bc11"
[[package]]
name = "futures-task"
version = "0.3.16"
version = "0.3.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbe54a98670017f3be909561f6ad13e810d9a51f3f061b902062ca3da80799f2"
checksum = "1d3d00f4eddb73e498a54394f228cd55853bdf059259e8e7bc6e69d408892e99"
[[package]]
name = "futures-util"
version = "0.3.16"
version = "0.3.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "67eb846bfd58e44a8481a00049e82c43e0ccb5d61f8dc071057cb19249dd4d78"
checksum = "36568465210a3a6ee45e1f165136d68671471a501e632e9a98d96872222b5481"
dependencies = [
"autocfg",
"futures-channel",
@@ -711,9 +711,9 @@
[[package]]
name = "http"
version = "0.2.4"
version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "527e8c9ac747e28542699a951517aa9a6945af506cd1f2e1b53a576c17b6cc11"
checksum = "1323096b05d41827dadeaee54c9981958c0f94e670bc94ed80037d1a7b8b186b"
dependencies = [
"bytes",
"fnv",
@@ -751,9 +751,9 @@
[[package]]
name = "hyper"
version = "0.14.12"
version = "0.14.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13f67199e765030fa08fe0bd581af683f0d5bc04ea09c2b1102012c5fb90e7fd"
checksum = "15d1cfb9e4f68655fa04c01f59edb405b6074a0f7118ea881e5026e4a1cd8593"
dependencies = [
"bytes",
"futures-channel",
@@ -783,9 +783,9 @@
[[package]]
name = "instant"
version = "0.1.10"
version = "0.1.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bee0328b1209d157ef001c94dd85b4f8f64139adb0eac2659f4b08382b2f474d"
checksum = "716d3d89f35ac6a34fd0eed635395f4c3b76fa889338a4632e5231a8684216bd"
dependencies = [
"cfg-if",
]
@@ -813,9 +813,9 @@
[[package]]
name = "libc"
version = "0.2.101"
version = "0.2.103"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3cb00336871be5ed2c8ed44b60ae9959dc5b9f08539422ed43f09e34ecaeba21"
checksum = "dd8f7255a17a627354f321ef0055d63b898c6fb27eff628af4d1b66b7331edf6"
[[package]]
name = "libsodium-sys"
@@ -883,9 +883,9 @@
[[package]]
name = "minimal-lexical"
version = "0.1.2"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6595bb28ed34f43c3fe088e48f6cfb2e033cab45f25a5384d5fdf564fbc8c4b2"
checksum = "0c835948974f68e0bd58636fc6c5b1fbff7b297e3046f11b3b3c18bbac012c6d"
[[package]]
name = "miniz_oxide"
@@ -941,9 +941,9 @@
[[package]]
name = "num-bigint"
version = "0.4.0"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4e0d047c1062aa51e256408c560894e5251f08925980e53cf1aa5bd00eec6512"
checksum = "74e768dff5fb39a41b3bcd30bb25cf989706c90d028d1ad71971987aa309d535"
dependencies = [
"autocfg",
"num-integer",
@@ -1090,9 +1090,9 @@
[[package]]
name = "pkg-config"
version = "0.3.19"
version = "0.3.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3831453b3449ceb48b6d9c7ad7c96d5ea673e9b470a1dc578c2ce6521230884c"
checksum = "7c9b1041b4387893b91ee6746cddfc28516aff326a3519fb2adf820932c5e6cb"
[[package]]
name = "ppv-lite86"
@@ -1114,9 +1114,9 @@
[[package]]
name = "proc-macro2"
version = "1.0.28"
version = "1.0.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c7ed8b8c7b886ea3ed7dde405212185f423ab44682667c8c6dd14aa1d9f6612"
checksum = "b9f5105d4fdaab20335ca9565e106a5d9b82b6219b5ba735731124ac6711d23d"
dependencies = [
"unicode-xid",
]
@@ -1249,18 +1249,18 @@
[[package]]
name = "serde"
version = "1.0.129"
version = "1.0.130"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d1f72836d2aa753853178eda473a3b9d8e4eefdaf20523b919677e6de489f8f1"
checksum = "f12d06de37cf59146fbdecab66aa99f9fe4f78722e3607577a5375d66bd0c913"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.129"
version = "1.0.130"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e57ae87ad533d9a56427558b516d0adac283614e347abf85b0dc0cbbf0a249f3"
checksum = "d7bc1a1ab1961464eae040d96713baa5a724a8152c1222492465b54322ec508b"
dependencies = [
"proc-macro2",
"quote",
@@ -1269,9 +1269,9 @@
[[package]]
name = "serde_json"
version = "1.0.67"
version = "1.0.68"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a7f9e390c27c3c0ce8bc5d725f6e4d30a29d26659494aa4b17535f7522c5c950"
checksum = "0f690853975602e1bfe1ccbf50504d67174e3bcf340f23b5ea9992e0587a52d8"
dependencies = [
"itoa",
"ryu",
@@ -1305,9 +1305,9 @@
[[package]]
name = "sha2"
version = "0.9.6"
version = "0.9.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9204c41a1597a8c5af23c82d1c921cb01ec0a4c59e07a9c7306062829a3903f3"
checksum = "b69f9a4c9740d74c5baa3fd2e547f9525fa8088a8a958e0ca2409a514e33f5fa"
dependencies = [
"block-buffer",
"cfg-if",
@@ -1345,9 +1345,9 @@
[[package]]
name = "socket2"
version = "0.4.1"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "765f090f0e423d2b55843402a07915add955e7d60657db13707a159727326cad"
checksum = "5dc90fe6c7be1a323296982db1836d1ea9e47b6839496dde9a541bc496df3516"
dependencies = [
"libc",
"winapi",
@@ -1361,9 +1361,9 @@
[[package]]
name = "syn"
version = "1.0.75"
version = "1.0.77"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b7f58f7e8eaa0009c5fec437aabf511bd9933e4b2d7407bd05273c01a8906ea7"
checksum = "5239bc68e0fef57495900cfea4e8dc75596d9a319d7e16b1e0a440d24e6fe0a0"
dependencies = [
"proc-macro2",
"quote",
@@ -1387,18 +1387,18 @@
[[package]]
name = "thiserror"
version = "1.0.26"
version = "1.0.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "93119e4feac1cbe6c798c34d3a53ea0026b0b1de6a120deef895137c0529bfe2"
checksum = "602eca064b2d83369e2b2f34b09c70b605402801927c65c11071ac911d299b88"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.26"
version = "1.0.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "060d69a0afe7796bf42e9e2ff91f5ee691fb15c53d38b4b62a9a53eb23164745"
checksum = "bad553cc2c78e8de258400763a647e80e6d1b31ee237275d756f6836d204494c"
dependencies = [
"proc-macro2",
"quote",
@@ -1484,9 +1484,9 @@
[[package]]
name = "tokio"
version = "1.10.1"
version = "1.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "92036be488bb6594459f2e03b60e42df6f937fe6ca5c5ffdcb539c6b84dc40f5"
checksum = "c2c2416fdedca8443ae44b4527de1ea633af61d8f7169ffa6e72c5b53d24efcc"
dependencies = [
"autocfg",
"bytes",
@@ -1526,9 +1526,9 @@
[[package]]
name = "tokio-util"
version = "0.6.7"
version = "0.6.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1caa0b0c8d94a049db56b5acf8cba99dc0623aab1b26d5b5f5e2d945846b3592"
checksum = "08d3725d3efa29485e87311c5b699de63cde14b00ed4d256b8318aa30ca452cd"
dependencies = [
"bytes",
"futures-core",
@@ -1573,7 +1573,7 @@
[[package]]
name = "tower-http"
version = "0.1.1"
source = "git+https://github.com/tower-rs/tower-http?branch=cors#c07f4582491f8eb667dca5440b0870bcd68b47a4"
source = "git+https://github.com/tower-rs/tower-http?branch=cors#9f09fc55bf74b5b9a8bd79cbede7e73d37a3e79b"
dependencies = [
"bytes",
"futures-core",
@@ -1600,9 +1600,9 @@
[[package]]
name = "tracing"
version = "0.1.26"
version = "0.1.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09adeb8c97449311ccd28a427f96fb563e7fd31aabf994189879d9da2394b89d"
checksum = "84f96e095c0c82419687c20ddf5cb3eadb61f4e1405923c9dc8e53a1adacbda8"
dependencies = [
"cfg-if",
"log",
@@ -1613,9 +1613,9 @@
[[package]]
name = "tracing-attributes"
version = "0.1.15"
version = "0.1.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c42e6fa53307c8a17e4ccd4dc81cf5ec38db9209f59b222210375b54ee40d1e2"
checksum = "98863d0dd09fa59a1b79c6750ad80dbda6b75f4e71c437a6a1a8cb91a8bcbd77"
dependencies = [
"proc-macro2",
"quote",
@@ -1624,9 +1624,9 @@
[[package]]
name = "tracing-core"
version = "0.1.19"
version = "0.1.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2ca517f43f0fb96e0c3072ed5c275fe5eece87e8cb52f4a77b69226d3b1c9df8"
checksum = "46125608c26121c81b0c6d693eab5a420e416da7e43c426d2e8f7df8da8a3acf"
dependencies = [
"lazy_static",
]
@@ -1639,9 +1639,9 @@
[[package]]
name = "typenum"
version = "1.13.0"
version = "1.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "879f6906492a7cd215bfa4cf595b600146ccfac0c79bcbd1f3000162af5e8b06"
checksum = "b63708a265f51345575b27fe43f9500ad611579e764c79edbc2037b1121959ec"
[[package]]
name = "unicode-segmentation"
@@ -117,8 +117,7 @@
if (
!unauthedOnly ||
!auth ||
!auth.authKey ||
auth.expires < new Date()
!auth?.getAuthKey()
) {
return <Component {...props} />;
} else {
@@ -150,12 +149,19 @@
} & { [r: string]: any }) {
const auth = useAuth();
const isAuthenticated = auth?.getAuthKey();
React.useEffect(() => {
if (!isAuthenticated) {
auth.logout();
}
}, [isAuthenticated]);
return (
<Route
{...rest}
render={(props) => {
if (auth && auth?.authKey && auth.expires > new Date()) {
if (auth && isAuthenticated) {
return <Component {...props} />;
} else {
return (
@@ -1,12 +1,11 @@
import React = require("react");
import { useState, useEffect, useContext, createContext } from "react";
import { unauthenticatedEndpoint } from "./util";
export interface AuthContext {
authKey?: string;
expires?: Date;
login: (username: string, password: string) => Promise<void>;
logout: () => Promise<void>;
getAuthKey: () => Promise<string | null>;
}
const authContext = createContext<AuthContext | null>(null);
@@ -21,15 +20,17 @@
};
function useProvideAuth(): AuthContext {
const [authKey, setAuthKey] = useState(() => getAuthStorage().authKey);
const [expires, setExpires] = useState(() => getAuthStorage().expires);
const [auth, setAuth] = useState(() => {
let authStorage = getAuthStorage();
return [authStorage.authKey, authStorage.expires];
});
useEffect(() => {
localStorage.setItem(
"charteredAuthentication",
JSON.stringify({ authKey, expires })
JSON.stringify({ authKey: auth?.[0], expires: auth?.[1] })
);
}, [authKey, expires]);
}, [auth]);
const login = async (username: string, password: string) => {
let res = await fetch(unauthenticatedEndpoint("login"), {
@@ -46,22 +47,26 @@
throw new Error(json.error);
}
setExpires(new Date(json.expires));
setAuthKey(json.key);
setAuth([json.key, new Date(json.expires)]);
};
const logout = async () => {
localStorage.removeItem("charteredAuthentication");
setExpires(null);
setAuthKey(null);
setAuth(null);
};
const getAuthKey = () => {
if (auth?.[1] > new Date()) {
return auth[0];
} else if (auth) {
return null;
}
};
return {
authKey,
expires,
login,
logout,
getAuthKey,
};
}
@@ -12,7 +12,7 @@
auth: AuthContext,
endpoint: string
): string {
return `${BASE_URL}/a/${auth.authKey}/web/v1/${endpoint}`;
return `${BASE_URL}/a/${auth.getAuthKey()}/web/v1/${endpoint}`;
}
export function useAuthenticatedRequest<S>(
@@ -24,13 +24,19 @@
React.useEffect(async () => {
try {
let req = await fetch(authenticatedEndpoint(auth, endpoint));
let res = await req.json();
if (res.error) {
setError(res.error);
let res = await fetch(authenticatedEndpoint(auth, endpoint));
if (res.status == 401) {
await auth.logout();
return null;
}
let jsonRes = await res.json();
if (jsonRes.error) {
setError(jsonRes.error);
} else {
setResponse(res);
setResponse(jsonRes);
}
} catch (e) {
setError(e.message);
@@ -6,7 +6,7 @@
use axum::{
handler::{delete, get, patch, post, put},
http::Method,
http::{Method, header},
AddExtensionLayer, Router,
};
use tower::ServiceBuilder;
@@ -149,6 +149,7 @@
Method::PUT,
Method::OPTIONS,
])
.allow_headers(vec![header::CONTENT_TYPE, header::USER_AGENT])
.allow_origin(Any)
.allow_credentials(false),
)
@@ -1,6 +1,8 @@
use std::borrow::Cow;
#[derive(serde::Serialize)]
pub struct ErrorResponse {
error: Option<String>,
pub error: Option<Cow<'static, str>>,
}
macro_rules! define_error_response {
@@ -13,7 +15,7 @@
fn into_response(self) -> axum::http::Response<Self::Body> {
let body = serde_json::to_vec(&crate::endpoints::ErrorResponse {
error: Some(self.to_string()),
error: Some(self.to_string().into()),
})
.unwrap();
@@ -1,4 +1,5 @@
use axum::{
body::{box_body, Body, BoxBody},
extract::{self, FromRequest, RequestParts},
http::{Request, Response, StatusCode},
};
@@ -10,15 +11,16 @@
};
use tower::Service;
use crate::endpoints::ErrorResponse;
#[derive(Clone)]
pub struct AuthMiddleware<S>(pub S);
impl<S, ReqBody, ResBody> Service<Request<ReqBody>> for AuthMiddleware<S>
impl<S, ReqBody> Service<Request<ReqBody>> for AuthMiddleware<S>
where
S: Service<Request<ReqBody>, Response = Response<ResBody>> + Clone + Send + 'static,
S: Service<Request<ReqBody>, Response = Response<BoxBody>> + Clone + Send + 'static,
S::Future: Send + 'static,
ReqBody: Send + 'static,
ResBody: Default + Send + 'static,
{
type Response = S::Response;
type Error = S::Error;
@@ -58,14 +60,19 @@
None => {
return Ok(Response::builder()
.status(StatusCode::UNAUTHORIZED)
.body(ResBody::default())
.body(box_body(Body::from(
serde_json::to_vec(&ErrorResponse {
error: Some("Expired auth token".into()),
})
.unwrap(),
)))
.unwrap())
}
};
req.extensions_mut().unwrap().insert(user);
let response: Response<ResBody> = inner.call(req.try_into_request().unwrap()).await?;
let response: Response<BoxBody> = inner.call(req.try_into_request().unwrap()).await?;
Ok(response)
})
@@ -34,6 +34,6 @@
extract::Json(req): extract::Json<PutRequest>,
) -> Result<Json<ErrorResponse>, Error> {
Organisation::create(db, req.name, req.description, user.id).await?;
Ok(Json(ErrorResponse { error: None }))
}