Skip to content

Commit

Permalink
fix(auth): final fixes to temp/osm auth across frontends
Browse files Browse the repository at this point in the history
  • Loading branch information
spwoodcock committed Dec 11, 2024
1 parent 1b0ed1b commit 4efd798
Show file tree
Hide file tree
Showing 8 changed files with 49 additions and 50 deletions.
32 changes: 27 additions & 5 deletions src/backend/app/auth/auth_deps.py
Original file line number Diff line number Diff line change
Expand Up @@ -235,7 +235,27 @@ async def refresh_cookies(
# frontend header. For the mapper frontend this is enough, but for the
# management frontend we instead use the return from /auth/me
response = JSONResponse(status_code=HTTPStatus.OK, content=access_token_data)
return set_cookies(response, new_access_token, new_refresh_token)
response_plus_cookies = set_cookies(response, new_access_token, new_refresh_token)

# Invalidate any temp cookies from mapper frontend
for cookie_name in [
f"{settings.cookie_name}_temp",
f"{settings.cookie_name}_temp_refresh",
]:
log.debug(f"Resetting cookie in response named '{cookie_name}'")
response_plus_cookies.set_cookie(
key=cookie_name,
value="",
max_age=0, # Set to expire immediately
expires=0, # Set to expire immediately
path="/",
domain=settings.FMTM_DOMAIN,
secure=False if settings.DEBUG else True,
httponly=True,
samesite="lax",
)

return response_plus_cookies


### Endpoint Dependencies ###
Expand All @@ -248,11 +268,12 @@ async def login_required(
if settings.DEBUG:
return AuthUser(sub="fmtm|1", username="localadmin", role=UserRole.ADMIN)

# Extract access token only from the OSM cookie
# Extract access token only from the FMTM cookie
extracted_token = access_token or get_cookie_value(
request,
settings.cookie_name, # OSM cookie
settings.cookie_name, # FMTM cookie
)
print("manage")
return await _authenticate_user(extracted_token)


Expand All @@ -263,15 +284,16 @@ async def mapper_login_required(
if settings.DEBUG:
return AuthUser(sub="fmtm|1", username="localadmin", role=UserRole.ADMIN)

# Extract access token from OSM cookie, fallback to temp auth cookie
# Extract access token from FMTM cookie, fallback to temp auth cookie
extracted_token = access_token or get_cookie_value(
request,
settings.cookie_name, # OSM cookie
settings.cookie_name, # FMTM cookie
f"{settings.cookie_name}_temp", # Temp cookie
)

# Verify login and continue
if extracted_token:
print("mapper")
return await _authenticate_user(extracted_token)

# Else user has no token, so we provide login data automatically
Expand Down
24 changes: 2 additions & 22 deletions src/backend/app/auth/auth_routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -223,33 +223,13 @@ async def refresh_management_cookies(
NOTE this endpoint has no db calls and returns in ~2ms.
"""
response = await refresh_cookies(
return await refresh_cookies(
request,
current_user,
settings.cookie_name,
f"{settings.cookie_name}_refresh",
)

# Invalidate any temp cookies from mapper frontend
for cookie_name in [
f"{settings.cookie_name}_temp",
f"{settings.cookie_name}_temp_refresh",
]:
log.debug(f"Resetting cookie in response named '{cookie_name}'")
response.set_cookie(
key=cookie_name,
value="",
max_age=0, # Set to expire immediately
expires=0, # Set to expire immediately
path="/",
domain=settings.FMTM_DOMAIN,
secure=False if settings.DEBUG else True,
httponly=True,
samesite="lax",
)

return response


@router.get("/refresh/mapper", response_model=Optional[FMTMUser])
async def refresh_mapper_token(
Expand Down Expand Up @@ -291,7 +271,7 @@ async def refresh_mapper_token(
# NOTE be sure to not append content=current_user.model_dump() to this JSONResponse
# as we want the login state on the frontend to remain empty (allowing the user to
# log in via OSM instead / override)
response = JSONResponse(status_code=HTTPStatus.OK, content={})
response = JSONResponse(status_code=HTTPStatus.OK, content=temp_jwt_details)
return set_cookies(
response,
fmtm_token,
Expand Down
3 changes: 0 additions & 3 deletions src/frontend/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,6 @@ const RefreshUserCookies = () => {
const apiUser = await getUserDetailsFromApi();
if (apiUser) {
dispatch(LoginActions.setAuthDetails(apiUser));
// To prevent calls to /auth/me in future (on mapper frontend)
// We still require this here to retrieve role info for the user
localStorage.setItem('fmtm-user-exists', 'true');
} else {
console.error('Failed to fetch user details after cookie refresh.');
}
Expand Down
2 changes: 0 additions & 2 deletions src/frontend/src/views/OsmAuth.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,6 @@ function OsmAuth() {
const apiUser = await getUserDetailsFromApi();
if (apiUser) {
dispatch(LoginActions.setAuthDetails(apiUser));
// To prevent calls to /auth/me in future
localStorage.setItem('fmtm-user-exists', 'true');
} else {
console.error('Failed to fetch user details after cookie refresh.');
}
Expand Down
4 changes: 2 additions & 2 deletions src/mapper/src/lib/components/header.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
</div>
<div class="flex items-center gap-4">
<!-- profile image and username display -->
{#if loginStore?.getAuthDetails.username}
{#if loginStore?.getAuthDetails?.username}
<div class="flex items-center gap-2">
{#if !loginStore?.getAuthDetails?.picture}
<hot-icon
Expand Down Expand Up @@ -100,7 +100,7 @@
class="hover:text-red-600 cursor-pointer duration-200 decoration-none text-black">{menu.name}</a
>
{/each}
{#if loginStore?.getAuthDetails.username}
{#if loginStore?.getAuthDetails?.username}
<sl-button
class="primary rounded"
variant="primary"
Expand Down
8 changes: 4 additions & 4 deletions src/mapper/src/lib/utils/login.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
// The /auth/me endpoint does an UPSERT in the database, ensuring the user
// exists in the FMTM DB
export const getUserDetailsFromApi = async () => {
export const getUserDetailsFromApi = async (fetchClient = fetch) => {
try {
const response = await fetch(`${import.meta.env.VITE_API_URL}/auth/me`, {
const response = await fetchClient(`${import.meta.env.VITE_API_URL}/auth/me`, {
credentials: 'include',
});

Expand All @@ -26,9 +26,9 @@ export const osmLoginRedirect = async () => {
} catch (error) {}
};

export const refreshCookies = async () => {
export const refreshCookies = async (fetchClient = fetch) => {
try {
const response = await fetch(`${import.meta.env.VITE_API_URL}/auth/refresh/mapper`, {
const response = await fetchClient(`${import.meta.env.VITE_API_URL}/auth/refresh/mapper`, {
credentials: 'include',
});

Expand Down
19 changes: 9 additions & 10 deletions src/mapper/src/routes/[projectId]/+page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,16 @@ export const load: PageLoad = async ({ parent, params, fetch }) => {
/*
Login + user details
*/
let apiUser = await refreshCookies();
const fmtmUserExists = localStorage.getItem('fmtm-user-exists');
if (!fmtmUserExists) {
let apiUser = await refreshCookies(fetch);
if (apiUser?.username !== 'svcfmtm') {
// Call /auth/me to populate the user details in the header
apiUser = await getUserDetailsFromApi();
}
if (!apiUser) {
loginStore.signOut();
throw error(401, { message: `You must log in first` });
} else {
loginStore.setAuthDetails(apiUser);
apiUser = await getUserDetailsFromApi(fetch);
if (!apiUser) {
loginStore.signOut();
throw error(401, { message: `You must log in first` });
} else {
loginStore.setAuthDetails(apiUser);
}
}

/*
Expand Down
7 changes: 5 additions & 2 deletions src/mapper/src/store/login.svelte.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,14 @@ type authDetailsType = {
// orgs_managed: number[];
};

import { refreshCookies } from '$lib/utils/login';

let authDetails: authDetailsType | null = $state(null);
let isLoginModalOpen: boolean = $state(false);

function getLoginStore() {
return {
get getAuthDetails() {
console.log(authDetails);
return authDetails;
},
get isLoginModalOpen() {
Expand All @@ -28,8 +29,10 @@ function getLoginStore() {
toggleLoginModal: (status: boolean) => {
isLoginModalOpen = status;
},
signOut: () => {
signOut: async () => {
authDetails = null;
// Re-add temp auth cookies
await refreshCookies();
},
};
}
Expand Down

0 comments on commit 4efd798

Please sign in to comment.