Skip to content

Commit

Permalink
started to add more features from the backend, auth is more clear, po…
Browse files Browse the repository at this point in the history
…tential bugfixes
  • Loading branch information
AmeerArsala committed Jun 6, 2024
1 parent afee510 commit 3bc66d4
Show file tree
Hide file tree
Showing 9 changed files with 120 additions and 67 deletions.
24 changes: 12 additions & 12 deletions app/core/clients.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,10 +47,10 @@ def as_api_client(self) -> KindeApiClient:
return api_client


def read_user_client(user_id) -> KindeApiClient:
def read_user_client(user_auth_id: str) -> KindeApiClient:
global user_clients

retrieved_serialized_data = user_clients.get(user_id)
retrieved_serialized_data = user_clients.get(user_auth_id)
retrieved_deserialized_client_data: Dict = json.loads(retrieved_serialized_data)

user_api_client_data: KindeApiClientData = KindeApiClientData(
Expand All @@ -63,7 +63,7 @@ def read_user_client(user_id) -> KindeApiClient:
return user_api_client


def write_user_client(user_id, client: KindeApiClient):
def write_user_client(user_auth_id: str, client: KindeApiClient):
global user_clients

# print(f"Writing user client {user_id}...")
Expand All @@ -79,37 +79,37 @@ def write_user_client(user_id, client: KindeApiClient):

serialized_user_api_client_data = json.dumps(user_api_client_data_dict)
user_clients.set(
user_id,
user_auth_id,
serialized_user_api_client_data,
# Expires in X seconds
ex=user_api_client_data_dict["access_token_obj"]["expires_in"],
)


def delete_user_client(user_id):
def delete_user_client(user_auth_id: str):
global user_clients

user_clients.delete(user_id)
user_clients.delete(user_auth_id)


# Will expire as soon as it is read
# Otherwise, has a short expiration time
def readex_user_id(state: str) -> str:
def readex_user_auth_id(state: str) -> str:
global user_clients

# Have it expire as soon as it is read
user_id: str = user_clients.getex(state, px=10) # expire after 10 ms
user_auth_id: str = user_clients.getex(state, px=10) # expire after 10 ms
# retrieved_serialized_data = user_clients.getex(state, ex=0)
# retrieved_deserialized_client_data: Dict = json.loads(retrieved_serialized_data)

return user_id
return user_auth_id


def write_user_id(state: str, user_id: str):
def write_user_auth_id(state: str, user_auth_id: str):
global user_clients

# Short expiration; 15 secs
user_clients.set(state, user_id, ex=15)
user_clients.set(state, user_auth_id, ex=15)


def get_user_session_client(request: Request, manifest: bool = False) -> KindeApiClient:
Expand Down Expand Up @@ -151,5 +151,5 @@ def get_user_session_client(request: Request, manifest: bool = False) -> KindeAp
# Dependency to get the current user's KindeApiClient instance
# Like an ID to a party or something except this party requires being an authenticated user
# We will use dependency injection to GATEKEEP our routes! LOL!!!
def get_kinde_client(request: Request) -> KindeApiClient:
def get_user_kinde_client(request: Request) -> KindeApiClient:
return get_user_session_client(request, manifest=True)
72 changes: 27 additions & 45 deletions app/core/routes/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,64 +85,40 @@ async def callback(request: Request):

print(f"user: {user}")

user_id: str = user.get("id")
user_auth_id: str = user.get("id")

# Store in session
request.session["user_id"] = user_id
request.session["user_id"] = user_auth_id

# Find out whether it is the first time the user is requesting (via this heuristic)
is_first_time: bool = clients.user_clients.get(user_id) is None
is_first_time: bool = clients.user_clients.get(user_auth_id) is None

# Update the user_clients cache with the client session
clients.write_user_client(user_id, kinde_client)
clients.write_user_client(user_auth_id, kinde_client)

redirect_url: str = ""
if is_first_time:
redirect_url = config.FIRST_TIME_POST_CALLBACK_REDIRECT_URL
else:
redirect_url = config.POST_CALLBACK_REDIRECT_URL

# js_code: str = f"""
# localStorage.setItem('userId', '{user_id}');
# console.log("DONE");
# """
#
# html_content: str = f"""
# <!DOCTYPE html>
# <html>
# <head>
# <meta http-equiv="refresh" content="0; URL='{redirect_url}'" />
# <script>
# {js_code}
# </script>
# </head>
# </html>
# """

# return RedirectResponse(app_global.url_path_for("read_root"))
# return RedirectResponse(redirect_url)
# return HTMLResponse(content=html_content, status_code=200)

# Custom response that redirects with a value
# response = Response(content=user_id, media="text/plain")
# response.status_code = HTTP_301_MOVED_PERMANENTLY
# response.headers["Location"] = redirect_url

# Write state just in case
state: str = request.query_params["state"]
clients.write_user_id(state, user_id)
clients.write_user_auth_id(state, user_auth_id)

print(f"Redirecting to: {redirect_url}")

response = RedirectResponse(redirect_url)
response.set_cookie(
key="user_id",
value=user_id,
value=user_auth_id,
domain=f".{config.SITE_DOMAIN}",
secure=config.USING_HTTPS,
httponly=True,
samesite="none",
)

# print(f"user_id: {user_id}")
# print(f"user_auth_id: {user_auth_id}")

return response

Expand All @@ -152,18 +128,20 @@ async def callback(request: Request):
async def logout(request: Request):
print("LOGGING OUT...")

# First, let's get the user_id
user_id = request.session.get("user_id")
# First, let's get the user_auth_id
user_auth_id = request.session.get("user_id")

# Now, let's log this mf out
if (user_id is not None) and (clients.user_clients.get(user_id) is not None):
kinde_user_client = clients.read_user_client(user_id)
if (user_auth_id is not None) and (
clients.user_clients.get(user_auth_id) is not None
):
kinde_user_client = clients.read_user_client(user_auth_id)

# LOG EM OUT
logout_url = kinde_user_client.logout(redirect_to=config.LOGOUT_REDIRECT_URL)

# REMOVE ALL TRACES OF 'EM!!!
clients.delete_user_client(user_id)
clients.delete_user_client(user_auth_id)
request.session.pop("user_id", None)

# LOG EM OUT
Expand All @@ -177,10 +155,14 @@ async def logout(request: Request):


@router.get("/get_user_id")
async def get_user_id(state: str) -> str:
user_id: str = clients.readex_user_id(state)
async def get_user_auth_id(state: str) -> str:
if clients.user_clients.get(state) is None:
print("User Auth ID Not Found.")
return "NULL"

user_auth_id: str = clients.readex_user_auth_id(state)

return user_id
return user_auth_id


@router.get("/is_authenticated")
Expand All @@ -189,14 +171,14 @@ async def get_is_authenticated(request: Request) -> bool:
# print(request.query_params)

# First, get the user_id
user_id: str = request.query_params.get("user_id")
user_auth_id: str = request.query_params.get("user_id")

if (user_id is None) or (clients.user_clients.get(user_id) is None):
print(f"User ID not found or supplied. User ID: {user_id}")
if (user_auth_id is None) or (clients.user_clients.get(user_auth_id) is None):
print(f"User ID not found or supplied. User ID: {user_auth_id}")
return False

# Next, get the corresponding client
user_client: KindeApiClient = clients.read_user_client(user_id)
user_client: KindeApiClient = clients.read_user_client(user_auth_id)

# Finally, check the authentication state
is_authenticated: bool = user_client.is_authenticated()
Expand Down
27 changes: 24 additions & 3 deletions app/core/routes/user.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,14 @@
from pydantic import BaseModel, Field

from app.core import clients, api_auth
from app.core.schemas.users import UserDetails

from kinde_sdk.kinde_api_client import KindeApiClient


router = APIRouter(
tags=["user stuff (not relating to the simulation itself)"],
dependencies=[Depends(clients.get_kinde_client)],
dependencies=[Depends(clients.get_user_kinde_client)],
)


Expand All @@ -19,9 +20,29 @@ async def create_api_key(request: Request) -> str:
kinde_client.fetch_token(authorization_response=str(request.url))

user = kinde_client.get_user_details()
user_id = user.get("id")
user_auth_id = user.get("id")

# Make api key and store it
api_key: str = api_auth.create_api_key(user_id)
api_key: str = api_auth.create_api_key(user_auth_id)

return api_key


@router.get("/view_details")
async def view_user_details(user_auth_id: str) -> UserDetails:
kinde_client: KindeApiClient = clients.read_user_client(user_auth_id)

user_details_dict: Dict[str, str] = kinde_client.get_user_details()
return UserDetails(**user_details_dict)


class ManifestUserParams(BaseModel):
user_auth_id: str


@router.post("/manifest")
async def manifest_user(params: ManifestUserParams) -> int:
"""Returns the user_id"""
# Get the corresponding user_id

# Check the DB to see if the user
10 changes: 10 additions & 0 deletions app/core/schemas/users.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
from typing import List, Dict
from pydantic import BaseModel, Field


class UserDetails(BaseModel):
given_name: str
id: str
family_name: str
email: str
picture: str # a link to the image
4 changes: 2 additions & 2 deletions app/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@
)

# For now, allow all
origin_whitelist = ["*", config.SITE_URL]
origin_whitelist = ["*", config.SITE_URL, "http://localhost:4321"]


app.add_middleware(
Expand Down Expand Up @@ -74,7 +74,7 @@ async def read_root():

@app.get("/gatekeep")
async def gatekeeping_test(
kinde_client: KindeApiClient = Depends(clients.get_kinde_client),
kinde_client: KindeApiClient = Depends(clients.get_user_kinde_client),
):
return "You have entered the gate"

Expand Down
1 change: 1 addition & 0 deletions frontend/src/components/AuthForms/AuthRedirect.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
authState.set(state);
// Faciliate the redirect
// Do not store this in browser history, hence why we use replace and not just do `window.location.href = url;`
window.location.replace(url);
}).catch((error) => {
console.log("Error getting auth");
Expand Down
13 changes: 12 additions & 1 deletion frontend/src/components/NavBar/ActionButtons.svelte
Original file line number Diff line number Diff line change
@@ -1,15 +1,26 @@
<script lang="ts">
import { onMount } from "svelte";
import * as Dialog from "@components/ui/dialog/index.js";
import SignInDialog from "@components/AuthForms/SignInDialog.svelte";
import SignUpDialog from "@components/AuthForms/SignUpDialog.svelte";
import { BACKEND_URL } from "$lib/data/envconfig";
import { authentication } from "$lib/data/stores";
let authenticated: boolean = authentication.isAuthenticated();
onMount(() => {
authentication.getAtom().subscribe((value, oldValue) => {
// set authenticated to the parsed boolean
authenticated = (value === 'true');
})
});
</script>

<div class="space-x-2 flex flex-row">
{#if !authentication.isAuthenticated()}
{#if !authenticated}
<!--Login Button-->
<Dialog.Root>
<Dialog.Trigger>
Expand Down
14 changes: 14 additions & 0 deletions frontend/src/data/stores.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,21 @@ export const authentication = {
getAtom: () => authenticated
};

// if user is registered with the core DB (Postgres)
const userRegistered = persistentAtom("is_core_registered");

export const coreRegistration = {
isUserRegistered: (): boolean => (userRegistered.get() === 'true'),
activateRegistration: () => {
userRegistered.set('true');
}
};


// App Stores

// this is not to be confused with the user_id in the Postgres DB; this is for auth
// you can use this to derive the Postgres one
export const userID = persistentAtom("user_id"); //, null);

// Core stores
Expand Down
22 changes: 18 additions & 4 deletions frontend/src/scripts/updatestate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import Cookies from 'js-cookie';

import { REPO_URL } from "$lib/data/constants";
import { BACKEND_URL } from "$lib/data/envconfig";
import { githubStars, authState, authentication, userID } from "$lib/data/stores";
import { githubStars, authState, authentication, coreRegistration, userID } from "$lib/data/stores";

// when to update all major values
export default async function update() {
Expand Down Expand Up @@ -66,13 +66,27 @@ export async function updateAuthentication() {
console.log("Ok, checking user_id via state");
let state: string = authState.get();

if (idNotFound(state)) {
console.log("No state exists. Aborting.");
return;
}

function onUserIDNotFound() {
console.log("No userID found");
authentication.setIsAuthenticated(false);
}

try {
const response = await axios.get(`${BACKEND_URL}/auth/get_user_id?state=${state}`);

user_id = response.data;
if (response.data === "NULL") {
onUserIDNotFound();
return;
} else {
user_id = response.data;
}
} catch(error) {
console.log("No userID found");
authentication.setIsAuthenticated(false);
onUserIDNotFound();
return;
}
}
Expand Down

0 comments on commit 3bc66d4

Please sign in to comment.