From aae650877f16b744e509057eb75d2ec3324f2e94 Mon Sep 17 00:00:00 2001 From: Jordan Doyle Date: Sat, 17 Sep 2022 04:38:49 +0100 Subject: [PATCH] Use bearer tokens instead of embedded URL credentials on the web API --- chartered-web/src/main.rs | 19 +++++++++++++------ chartered-frontend/src/stores/auth.ts | 22 +++++++++++++--------- chartered-web/src/middleware/auth.rs | 89 -------------------------------------------------------------------------------- chartered-web/src/middleware/cargo_auth.rs | 101 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ chartered-web/src/middleware/mod.rs | 3 ++- chartered-web/src/middleware/web_auth.rs | 104 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ chartered-web/src/endpoints/web_api/ssh_key.rs | 2 +- chartered-frontend/src/routes/(authed)/ssh-keys/CreateKeyForm.svelte | 4 +++- chartered-frontend/src/routes/(authed)/ssh-keys/DeleteSshKeyModal.svelte | 6 +++++- chartered-web/src/endpoints/web_api/crates/info.rs | 2 +- chartered-web/src/endpoints/web_api/crates/members.rs | 8 ++++---- chartered-web/src/endpoints/web_api/organisations/info.rs | 2 +- chartered-web/src/endpoints/web_api/organisations/members.rs | 6 +++--- chartered-web/src/endpoints/web_api/users/heatmap.rs | 2 +- chartered-web/src/endpoints/web_api/users/info.rs | 2 +- chartered-frontend/src/routes/(authed)/crates/[organisation]/Member.svelte | 8 +++++--- chartered-frontend/src/routes/(authed)/organisations/create/+page.svelte | 4 +++- chartered-frontend/src/routes/(authed)/sessions/list/DeleteSessionModal.svelte | 4 +++- 18 files changed, 264 insertions(+), 124 deletions(-) diff --git a/chartered-web/src/main.rs b/chartered-web/src/main.rs index edfb60e..42dae1b 100644 --- a/chartered-web/src/main.rs +++ a/chartered-web/src/main.rs @@ -71,19 +71,22 @@ let app = Router::new() .route("/", get(hello_world)) .nest( - "/a/:key/web/v1", + "/web/v1", endpoints::web_api::authenticated_routes().layer( ServiceBuilder::new() - .layer_fn(crate::middleware::auth::AuthMiddleware) + .layer_fn(crate::middleware::web_auth::WebAuthMiddleware) .into_inner(), ), ) - .nest("/a/-/web/v1", endpoints::web_api::unauthenticated_routes()) .nest( + "/web/v1/public", + endpoints::web_api::unauthenticated_routes(), + ) + .nest( "/a/:key/o/:organisation/api/v1", endpoints::cargo_api::routes().layer( ServiceBuilder::new() - .layer_fn(crate::middleware::auth::AuthMiddleware) + .layer_fn(crate::middleware::cargo_auth::CargoAuthMiddleware) .into_inner(), ), ) @@ -97,8 +100,12 @@ Method::DELETE, Method::PUT, Method::OPTIONS, + ]) + .allow_headers(vec![ + header::CONTENT_TYPE, + header::USER_AGENT, + header::AUTHORIZATION, ]) - .allow_headers(vec![header::CONTENT_TYPE, header::USER_AGENT]) .allow_origin(AllowOrigin::predicate({ let config = config.clone(); move |url, _| { @@ -109,7 +116,7 @@ .unwrap_or_default() } })) - .allow_credentials(false), + .allow_credentials(true), ) .layer(Extension(pool)) .layer(Extension(Arc::new(config.create_oidc_clients().await?))) diff --git a/chartered-frontend/src/stores/auth.ts b/chartered-frontend/src/stores/auth.ts index 15474d5..77e6295 100644 --- a/chartered-frontend/src/stores/auth.ts +++ a/chartered-frontend/src/stores/auth.ts @@ -67,8 +67,7 @@ try { // call chartered-web to attempt to extend the session - const result = await fetch(`${BASE_URL}/a/${currentAuth.auth_key}/web/v1/auth/extend`); - const json: ExtendResult = await result.json(); + const json = await request(`/web/v1/auth/extend`); // backend returned an error, nothing we can do here if (json.error) { @@ -124,7 +123,7 @@ */ export async function login(username: string, password: string) { // call the backend and attempt the authentication - const result = await fetch(`${BASE_URL}/a/-/web/v1/auth/login/password`, { + const result = await fetch(`${BASE_URL}/web/v1/public/auth/login/password`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ username, password }), @@ -153,7 +152,7 @@ */ export async function handleOAuthCallback(params: string) { // call the backend and attempt the authentication - const result = await fetch(`${BASE_URL}/a/-/web/v1/auth/login/oauth/complete${params}`); + const result = await fetch(`${BASE_URL}/web/v1/public/auth/login/oauth/complete${params}`); const json: LoginResult = await result.json(); // server returned an error, forward it on - there's nothing else we @@ -192,7 +191,12 @@ throw new Error('Not authenticated'); } - const result = await fetch(`${BASE_URL}/a/${token}${url}`); + const result = await fetch(`${BASE_URL}${url}`, { + headers: { + Authorization: `Bearer ${token}`, + }, + credentials: 'include', + }); const json: T & Error = await result.json(); // TODO: handle 404s @@ -209,7 +213,7 @@ * @param provider OAuth provider as configured on the backend to grab an auth link for */ export async function loginOAuth(provider: string) { - const result = await fetch(`${BASE_URL}/a/-/web/v1/auth/login/oauth/${provider}/begin`); + const result = await fetch(`${BASE_URL}/web/v1/public/auth/login/oauth/${provider}/begin`); const json: LoginOAuthResult = await result.json(); if (json.error) { @@ -231,7 +235,7 @@ const authKey = get(auth)?.auth_key; if (authKey) { - await fetch(`${BASE_URL}/a/${authKey}/web/v1/auth/logout`); + await request(`/web/v1/auth/logout`); } } catch (e) { console.error('Failed to fully log user out of session', e); @@ -253,7 +257,7 @@ * Grab all the possible authentication methods from the backend. */ export async function fetchOAuthProviders(): Promise { - const result = await fetch(`${BASE_URL}/a/-/web/v1/auth/login/oauth/providers`); + const result = await fetch(`${BASE_URL}/web/v1/public/auth/login/oauth/providers`); return await result.json(); } @@ -274,7 +278,7 @@ */ export async function register(username: string, password: string) { // send register request to backend - const result = await fetch(`${BASE_URL}/a/-/web/v1/auth/register/password`, { + const result = await fetch(`${BASE_URL}/web/v1/public/auth/register/password`, { method: 'POST', headers: { 'Content-Type': 'application/json', diff --git a/chartered-web/src/middleware/auth.rs b/chartered-web/src/middleware/auth.rs deleted file mode 100644 index 98e6aad..0000000 100644 --- a/chartered-web/src/middleware/auth.rs +++ /dev/null @@ -1,89 +1,0 @@ -//! Check the API key embedded in the path is valid otherwise returns a 401 for authenticated -//! endpoints. - -use axum::{ - body::{boxed, Body, BoxBody}, - extract::{self, FromRequest, RequestParts}, - http::{Request, Response, StatusCode}, -}; -use chartered_db::users::User; -use chartered_db::ConnectionPool; -use futures::future::BoxFuture; -use std::sync::Arc; -use std::{ - collections::HashMap, - task::{Context, Poll}, -}; -use tower::Service; - -use crate::endpoints::ErrorResponse; - -#[derive(Clone)] -pub struct AuthMiddleware(pub S); - -impl Service> for AuthMiddleware -where - S: Service, Response = Response> + Clone + Send + 'static, - S::Future: Send + 'static, - ReqBody: Send + 'static, -{ - type Response = S::Response; - type Error = S::Error; - type Future = BoxFuture<'static, Result>; - - fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { - self.0.poll_ready(cx) - } - - fn call(&mut self, req: Request) -> Self::Future { - // best practice is to clone the inner service like this - // see https://github.com/tower-rs/tower/issues/547 for details - let clone = self.0.clone(); - let mut inner = std::mem::replace(&mut self.0, clone); - - Box::pin(async move { - let mut req = RequestParts::new(req); - - // extracts all parameters from the path so we can get the API key which should - // always be named key - let params = extract::Path::>::from_request(&mut req) - .await - .unwrap(); - let key = params.get("key").map(String::as_str).unwrap_or_default(); - - // grab the ConnectionPool from the extensions created when we initialised the - // server - let db = req.extensions().get::().unwrap().clone(); - - // grab the UserSession that's currently being used for this request and the User that - // owns the key, otherwise return a 401 if the key doesn't exist - let (session, user) = match User::find_by_session_key(db, String::from(key)) - .await - .unwrap() - { - Some((session, user)) => (Arc::new(session), Arc::new(user)), - None => { - return Ok(Response::builder() - .status(StatusCode::UNAUTHORIZED) - .body(boxed(Body::from( - serde_json::to_vec(&ErrorResponse { - error: Some("Expired auth token".into()), - }) - .unwrap(), - ))) - .unwrap()) - } - }; - - // insert both the user and the session into extensions so handlers can - // get their hands on them - req.extensions_mut().insert(user); - req.extensions_mut().insert(session); - - // calls handlers/other middleware and drives the request to response - let response: Response = inner.call(req.try_into_request().unwrap()).await?; - - Ok(response) - }) - } -} diff --git a/chartered-web/src/middleware/cargo_auth.rs b/chartered-web/src/middleware/cargo_auth.rs new file mode 100644 index 0000000..38c1c15 100644 --- /dev/null +++ a/chartered-web/src/middleware/cargo_auth.rs @@ -1,0 +1,101 @@ +//! Check the API key embedded in the path is valid otherwise returns a 401 for authenticated +//! endpoints. + +use axum::{ + body::{boxed, Body, BoxBody}, + extract::{self, FromRequest, RequestParts}, + http::{Request, Response, StatusCode}, +}; +use chartered_db::{users::User, ConnectionPool}; +use futures::future::BoxFuture; +use std::{ + collections::HashMap, + sync::Arc, + task::{Context, Poll}, +}; +use tower::Service; + +use crate::endpoints::ErrorResponse; + +#[derive(Clone)] +pub struct CargoAuthMiddleware(pub S); + +impl Service> for CargoAuthMiddleware +where + S: Service, Response = Response> + Clone + Send + 'static, + S::Future: Send + 'static, + ReqBody: Send + 'static, +{ + type Response = S::Response; + type Error = S::Error; + type Future = BoxFuture<'static, Result>; + + fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { + self.0.poll_ready(cx) + } + + fn call(&mut self, req: Request) -> Self::Future { + // best practice is to clone the inner service like this + // see https://github.com/tower-rs/tower/issues/547 for details + let clone = self.0.clone(); + let mut inner = std::mem::replace(&mut self.0, clone); + + Box::pin(async move { + let mut req = RequestParts::new(req); + + // extracts all parameters from the path so we can get the API key which should + // always be named key + let params = extract::Path::>::from_request(&mut req) + .await + .unwrap(); + let key = params.get("key").map(String::as_str).unwrap_or_default(); + + // grab the ConnectionPool from the extensions created when we initialised the + // server + let db = req.extensions().get::().unwrap().clone(); + + // grab the UserSession that's currently being used for this request and the User that + // owns the key, otherwise return a 401 if the key doesn't exist + let (session, user) = match User::find_by_session_key(db, String::from(key)) + .await + .unwrap() + { + Some((session, user)) => (Arc::new(session), Arc::new(user)), + None => { + return Ok(Response::builder() + .status(StatusCode::UNAUTHORIZED) + .body(boxed(Body::from( + serde_json::to_vec(&ErrorResponse { + error: Some("Expired auth token".into()), + }) + .unwrap(), + ))) + .unwrap()) + } + }; + + if session.user_ssh_key_id.is_none() { + // Web sessions can't be used for the Cargo API + return Ok(Response::builder() + .status(StatusCode::UNAUTHORIZED) + .body(boxed(Body::from( + serde_json::to_vec(&ErrorResponse { + error: Some("Invalid auth token".into()), + }) + .unwrap(), + ))) + .unwrap()); + } + + // insert both the user and the session into extensions so handlers can + // get their hands on them + req.extensions_mut().insert(user); + req.extensions_mut().insert(session); + + // calls handlers/other middleware and drives the request to response + let response: Response = inner.call(req.try_into_request().unwrap()).await?; + + Ok(response) + }) + } +} diff --git a/chartered-web/src/middleware/mod.rs b/chartered-web/src/middleware/mod.rs index 7eaa040..d452bf7 100644 --- a/chartered-web/src/middleware/mod.rs +++ a/chartered-web/src/middleware/mod.rs @@ -1,2 +1,3 @@ -pub mod auth; +pub mod cargo_auth; pub mod logging; +pub mod web_auth; diff --git a/chartered-web/src/middleware/web_auth.rs b/chartered-web/src/middleware/web_auth.rs new file mode 100644 index 0000000..ad64804 100644 --- /dev/null +++ a/chartered-web/src/middleware/web_auth.rs @@ -1,0 +1,104 @@ +//! Check the API key in the authorization header is valid otherwise returns a 401 for authenticated +//! endpoints. + +use axum::{ + body::{boxed, Body, BoxBody}, + extract::{self, FromRequest, RequestParts}, + http::{Request, Response, StatusCode}, + response::IntoResponse, + TypedHeader, +}; +use chartered_db::{users::User, ConnectionPool}; +use futures::future::BoxFuture; +use headers::{authorization::Bearer, Authorization}; +use std::{ + sync::Arc, + task::{Context, Poll}, +}; +use tower::Service; + +use crate::endpoints::ErrorResponse; + +#[derive(Clone)] +pub struct WebAuthMiddleware(pub S); + +impl Service> for WebAuthMiddleware +where + S: Service, Response = Response> + Clone + Send + 'static, + S::Future: Send + 'static, + ReqBody: Send + 'static, +{ + type Response = S::Response; + type Error = S::Error; + type Future = BoxFuture<'static, Result>; + + fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { + self.0.poll_ready(cx) + } + + fn call(&mut self, req: Request) -> Self::Future { + // best practice is to clone the inner service like this + // see https://github.com/tower-rs/tower/issues/547 for details + let clone = self.0.clone(); + let mut inner = std::mem::replace(&mut self.0, clone); + + Box::pin(async move { + let mut req = RequestParts::new(req); + + // extract the authorization header + let authorization: Authorization = + match extract::TypedHeader::from_request(&mut req).await { + Ok(TypedHeader(v)) => v, + Err(e) => return Ok(e.into_response()), + }; + + // grab the ConnectionPool from the extensions created when we initialised the + // server + let db = req.extensions().get::().unwrap().clone(); + + // grab the UserSession that's currently being used for this request and the User that + // owns the key, otherwise return a 401 if the key doesn't exist + let (session, user) = + match User::find_by_session_key(db, String::from(authorization.0.token())) + .await + .unwrap() + { + Some((session, user)) => (Arc::new(session), Arc::new(user)), + None => { + return Ok(Response::builder() + .status(StatusCode::UNAUTHORIZED) + .body(boxed(Body::from( + serde_json::to_vec(&ErrorResponse { + error: Some("Expired auth token".into()), + }) + .unwrap(), + ))) + .unwrap()) + } + }; + + if session.user_ssh_key_id.is_some() { + // SSH sessions can't be used for the web API + return Ok(Response::builder() + .status(StatusCode::UNAUTHORIZED) + .body(boxed(Body::from( + serde_json::to_vec(&ErrorResponse { + error: Some("Invalid auth token".into()), + }) + .unwrap(), + ))) + .unwrap()); + } + + // insert both the user and the session into extensions so handlers can + // get their hands on them + req.extensions_mut().insert(user); + req.extensions_mut().insert(session); + + // calls handlers/other middleware and drives the request to response + let response: Response = inner.call(req.try_into_request().unwrap()).await?; + + Ok(response) + }) + } +} diff --git a/chartered-web/src/endpoints/web_api/ssh_key.rs b/chartered-web/src/endpoints/web_api/ssh_key.rs index e67b9b9..65d83e2 100644 --- a/chartered-web/src/endpoints/web_api/ssh_key.rs +++ a/chartered-web/src/endpoints/web_api/ssh_key.rs @@ -53,7 +53,7 @@ pub async fn handle_delete( extract::Extension(db): extract::Extension, extract::Extension(user): extract::Extension>, - extract::Path((_session_key, ssh_key_id)): extract::Path<(String, Uuid)>, + extract::Path(ssh_key_id): extract::Path, ) -> Result, Error> { let deleted = user.delete_user_ssh_key_by_uuid(db, ssh_key_id).await?; diff --git a/chartered-frontend/src/routes/(authed)/ssh-keys/CreateKeyForm.svelte b/chartered-frontend/src/routes/(authed)/ssh-keys/CreateKeyForm.svelte index 07e3e6a..ff91025 100644 --- a/chartered-frontend/src/routes/(authed)/ssh-keys/CreateKeyForm.svelte +++ a/chartered-frontend/src/routes/(authed)/ssh-keys/CreateKeyForm.svelte @@ -41,12 +41,14 @@ try { // submit the key to the backend - let result = await fetch(`${BASE_URL}/a/${$auth?.auth_key}/web/v1/ssh-key`, { + let result = await fetch(`${BASE_URL}/web/v1/ssh-key`, { method: 'PUT', headers: { 'Content-Type': 'application/json', + Authorization: `Bearer ${$auth?.auth_key}`, }, body: JSON.stringify({ key: sshKey }), + credentials: 'include', }); let json: AddSshKeyResult = await result.json(); diff --git a/chartered-frontend/src/routes/(authed)/ssh-keys/DeleteSshKeyModal.svelte b/chartered-frontend/src/routes/(authed)/ssh-keys/DeleteSshKeyModal.svelte index 9e34b34..e59a56b 100644 --- a/chartered-frontend/src/routes/(authed)/ssh-keys/DeleteSshKeyModal.svelte +++ a/chartered-frontend/src/routes/(authed)/ssh-keys/DeleteSshKeyModal.svelte @@ -49,8 +49,12 @@ try { // submit deletion request to backend - let res = await fetch(`${BASE_URL}/a/${$auth?.auth_key}/web/v1/ssh-key/${deleting.uuid}`, { + let res = await fetch(`${BASE_URL}/web/v1/ssh-key/${deleting.uuid}`, { method: 'DELETE', + headers: { + Authorization: `Bearer ${$auth?.auth_key}`, + }, + credentials: 'include', }); let json: DeleteSshKeyResult = await res.json(); diff --git a/chartered-web/src/endpoints/web_api/crates/info.rs b/chartered-web/src/endpoints/web_api/crates/info.rs index 59ed0e2..eb6eaf3 100644 --- a/chartered-web/src/endpoints/web_api/crates/info.rs +++ a/chartered-web/src/endpoints/web_api/crates/info.rs @@ -14,7 +14,7 @@ use thiserror::Error; pub async fn handle( - extract::Path((_session_key, organisation, name)): extract::Path<(String, String, String)>, + extract::Path((organisation, name)): extract::Path<(String, String)>, extract::Extension(db): extract::Extension, extract::Extension(user): extract::Extension>, ) -> Result { diff --git a/chartered-web/src/endpoints/web_api/crates/members.rs b/chartered-web/src/endpoints/web_api/crates/members.rs index f84969a..a4acedb 100644 --- a/chartered-web/src/endpoints/web_api/crates/members.rs +++ a/chartered-web/src/endpoints/web_api/crates/members.rs @@ -16,7 +16,7 @@ /// /// These members could be specific to the crate or they could be overrides ontop of the org. pub async fn handle_get( - extract::Path((_session_key, organisation, name)): extract::Path<(String, String, String)>, + extract::Path((organisation, name)): extract::Path<(String, String)>, extract::Extension(db): extract::Extension, extract::Extension(user): extract::Extension>, ) -> Result, Error> { @@ -44,7 +44,7 @@ /// Updates a crate member's permissions pub async fn handle_patch( - extract::Path((_session_key, organisation, name)): extract::Path<(String, String, String)>, + extract::Path((organisation, name)): extract::Path<(String, String)>, extract::Extension(db): extract::Extension, extract::Extension(user): extract::Extension>, extract::Json(req): extract::Json, @@ -68,7 +68,7 @@ /// Inserts an permissions override for this crate for a specific user pub async fn handle_put( - extract::Path((_session_key, organisation, name)): extract::Path<(String, String, String)>, + extract::Path((organisation, name)): extract::Path<(String, String)>, extract::Extension(db): extract::Extension, extract::Extension(user): extract::Extension>, extract::Json(req): extract::Json, @@ -89,7 +89,7 @@ /// Deletes a member override from this crate pub async fn handle_delete( - extract::Path((_session_key, organisation, name)): extract::Path<(String, String, String)>, + extract::Path((organisation, name)): extract::Path<(String, String)>, extract::Extension(db): extract::Extension, extract::Extension(user): extract::Extension>, extract::Json(req): extract::Json, diff --git a/chartered-web/src/endpoints/web_api/organisations/info.rs b/chartered-web/src/endpoints/web_api/organisations/info.rs index af2fdac..003e68c 100644 --- a/chartered-web/src/endpoints/web_api/organisations/info.rs +++ a/chartered-web/src/endpoints/web_api/organisations/info.rs @@ -11,7 +11,7 @@ use thiserror::Error; pub async fn handle_get( - extract::Path((_session_key, organisation)): extract::Path<(String, String)>, + extract::Path(organisation): extract::Path, extract::Extension(db): extract::Extension, extract::Extension(user): extract::Extension>, ) -> Result, Error> { diff --git a/chartered-web/src/endpoints/web_api/organisations/members.rs b/chartered-web/src/endpoints/web_api/organisations/members.rs index 60c4b78..97cb8cd 100644 --- a/chartered-web/src/endpoints/web_api/organisations/members.rs +++ a/chartered-web/src/endpoints/web_api/organisations/members.rs @@ -13,7 +13,7 @@ /// Updates an organisation member's permissions pub async fn handle_patch( - extract::Path((_session_key, organisation)): extract::Path<(String, String)>, + extract::Path(organisation): extract::Path, extract::Extension(db): extract::Extension, extract::Extension(user): extract::Extension>, extract::Json(req): extract::Json, @@ -37,7 +37,7 @@ /// Adds a new member to the organisation with a given set of permissions. pub async fn handle_put( - extract::Path((_session_key, organisation)): extract::Path<(String, String)>, + extract::Path(organisation): extract::Path, extract::Extension(db): extract::Extension, extract::Extension(user): extract::Extension>, extract::Json(req): extract::Json, @@ -58,7 +58,7 @@ /// Deletes a member from the organisation entirely pub async fn handle_delete( - extract::Path((_session_key, organisation)): extract::Path<(String, String)>, + extract::Path(organisation): extract::Path, extract::Extension(db): extract::Extension, extract::Extension(user): extract::Extension>, extract::Json(req): extract::Json, diff --git a/chartered-web/src/endpoints/web_api/users/heatmap.rs b/chartered-web/src/endpoints/web_api/users/heatmap.rs index c97ac4a..60fcebf 100644 --- a/chartered-web/src/endpoints/web_api/users/heatmap.rs +++ a/chartered-web/src/endpoints/web_api/users/heatmap.rs @@ -6,7 +6,7 @@ use thiserror::Error; pub async fn handle( - extract::Path((_session_key, uuid)): extract::Path<(String, chartered_db::uuid::Uuid)>, + extract::Path(uuid): extract::Path, extract::Extension(db): extract::Extension, ) -> Result, Error> { let user = User::find_by_uuid(db.clone(), uuid) diff --git a/chartered-web/src/endpoints/web_api/users/info.rs b/chartered-web/src/endpoints/web_api/users/info.rs index 9fd6658..36bfbaf 100644 --- a/chartered-web/src/endpoints/web_api/users/info.rs +++ a/chartered-web/src/endpoints/web_api/users/info.rs @@ -9,7 +9,7 @@ use thiserror::Error; pub async fn handle( - extract::Path((_session_key, uuid)): extract::Path<(String, chartered_db::uuid::Uuid)>, + extract::Path(uuid): extract::Path, extract::Extension(db): extract::Extension, ) -> Result, Error> { let user = User::find_by_uuid(db, uuid).await?.ok_or(Error::NotFound)?; diff --git a/chartered-frontend/src/routes/(authed)/crates/[organisation]/Member.svelte b/chartered-frontend/src/routes/(authed)/crates/[organisation]/Member.svelte index f4e5ba0..5f44422 100644 --- a/chartered-frontend/src/routes/(authed)/crates/[organisation]/Member.svelte +++ a/chartered-frontend/src/routes/(authed)/crates/[organisation]/Member.svelte @@ -106,22 +106,24 @@ // out which one we need to persist the changes to... let url; if (crate) { - url = `web/v1/crates/${organisation}/${crate}`; + url = `crates/${organisation}/${crate}`; } else { - url = `web/v1/organisations/${organisation}`; + url = `organisations/${organisation}`; } // send the membership update to the backend - let result = await fetch(`${BASE_URL}/a/${$auth?.auth_key}/${url}/members`, { + let result = await fetch(`${BASE_URL}/web/v1/${url}/members`, { method, headers: { Accept: 'application/json', 'Content-Type': 'application/json', + Authorization: `Bearer ${$auth?.auth_key}`, }, body: JSON.stringify({ user_uuid: member.uuid, permissions: newPermissions, }), + credentials: 'include', }); let json = await result.json(); diff --git a/chartered-frontend/src/routes/(authed)/organisations/create/+page.svelte b/chartered-frontend/src/routes/(authed)/organisations/create/+page.svelte index 67d0020..59a303d 100644 --- a/chartered-frontend/src/routes/(authed)/organisations/create/+page.svelte +++ a/chartered-frontend/src/routes/(authed)/organisations/create/+page.svelte @@ -44,12 +44,14 @@ try { // attempt the actual creation of the organisation - let result = await fetch(`${BASE_URL}/a/${$auth?.auth_key}/web/v1/organisations`, { + let result = await fetch(`${BASE_URL}/web/v1/organisations`, { method: 'PUT', headers: { 'Content-Type': 'application/json', + Authorization: `Bearer ${$auth?.auth_key}`, }, body: JSON.stringify({ name, description, public: isPublic }), + credentials: 'include', }); let json = await result.json(); diff --git a/chartered-frontend/src/routes/(authed)/sessions/list/DeleteSessionModal.svelte b/chartered-frontend/src/routes/(authed)/sessions/list/DeleteSessionModal.svelte index a39ca69..36024fa 100644 --- a/chartered-frontend/src/routes/(authed)/sessions/list/DeleteSessionModal.svelte +++ a/chartered-frontend/src/routes/(authed)/sessions/list/DeleteSessionModal.svelte @@ -48,12 +48,14 @@ try { // submit deletion request to backend - let res = await fetch(`${BASE_URL}/a/${$auth?.auth_key}/web/v1/sessions`, { + let res = await fetch(`${BASE_URL}/web/v1/sessions`, { method: 'DELETE', headers: { 'Content-Type': 'application/json', + Authorization: `Bearer ${$auth?.auth_key}`, }, body: JSON.stringify({ uuid: deleting.uuid }), + credentials: 'include', }); let json: { error?: string } = await res.json(); -- rgit 0.1.3