Skip to content

Commit

Permalink
Added user avatar to logged_in_user route and adjusted frontend
Browse files Browse the repository at this point in the history
  • Loading branch information
rubenthoms committed Sep 21, 2023
1 parent faa08d0 commit bae51f0
Show file tree
Hide file tree
Showing 6 changed files with 56 additions and 4 deletions.
4 changes: 4 additions & 0 deletions backend/src/backend/auth/auth_helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,9 @@ def get_authenticated_user(
# print("-------------------------------------------------")
smda_token = token_dict.get("access_token") if token_dict else None

token_dict = cca.acquire_token_silent(scopes=config.GRAPH_SCOPES, account=accounts[0])
graph_token = token_dict.get("access_token") if token_dict else None

# print(f" get tokens {timer.lap_ms():.1f}ms")

_save_token_cache_in_session(request_with_session, token_cache)
Expand All @@ -187,6 +190,7 @@ def get_authenticated_user(
authenticated_user = AuthenticatedUser(
user_id=user_id,
username=user_name,
graph_access_token=graph_token,
sumo_access_token=sumo_token,
smda_access_token=smda_token,
pdm_access_token=None,
Expand Down
19 changes: 16 additions & 3 deletions backend/src/backend/primary/routers/general.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,11 @@

import starsessions
from starlette.responses import StreamingResponse
from fastapi import APIRouter, HTTPException, Request, status, Depends
from fastapi import APIRouter, HTTPException, Request, status, Depends, Query
from pydantic import BaseModel
# Using the same http client as sumo
import httpx
import base64

from src.backend.auth.auth_helper import AuthHelper, AuthenticatedUser
from src.backend.primary.user_session_proxy import proxy_to_user_session
Expand All @@ -14,9 +17,12 @@

class UserInfo(BaseModel):
username: str
avatar_b64str: str | None
has_sumo_access: bool
has_smda_access: bool

class UserAvatar(BaseModel):
avatar: bytes

router = APIRouter()

Expand All @@ -34,7 +40,7 @@ def alive_protected() -> str:


@router.get("/logged_in_user", response_model=UserInfo)
async def logged_in_user(request: Request) -> UserInfo:
async def logged_in_user(request: Request, includeAvatar: bool = Query(False, description="Set to true to include user avatar from Microsoft GRAPH Api")) -> UserInfo:
print("entering logged_in_user route")

await starsessions.load_session(request)
Expand All @@ -47,12 +53,19 @@ async def logged_in_user(request: Request) -> UserInfo:

user_info = UserInfo(
username=authenticated_user.get_username(),
avatar_b64str=None,
has_sumo_access=authenticated_user.has_sumo_access_token(),
has_smda_access=authenticated_user.has_smda_access_token(),
)

return user_info
if includeAvatar:
headers = { "Authorization": f"Bearer {authenticated_user.get_graph_access_token()}" }
async with httpx.AsyncClient() as client:
result = await client.get("https://graph.microsoft.com/v1.0/me/photo/$value", headers=headers)
if result.status_code == 200:
user_info.avatar_b64str = base64.b64encode(result.content)

return user_info

@router.get("/user_session_container")
async def user_session_container(
Expand Down
15 changes: 15 additions & 0 deletions backend/src/services/utils/authenticated_user.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,15 @@ def __init__(
self,
user_id: str,
username: str,
graph_access_token: Optional[str],
sumo_access_token: Optional[str],
smda_access_token: Optional[str],
pdm_access_token: Optional[str],
ssdl_access_token: Optional[str],
) -> None:
self._user_id = user_id
self._username = username
self._graph_access_token = graph_access_token
self._sumo_access_token = sumo_access_token
self._smda_access_token = smda_access_token
self._pdm_access_token = pdm_access_token
Expand All @@ -28,6 +30,19 @@ def __eq__(self, other: Any) -> bool:

def get_username(self) -> str:
return self._username

def get_graph_access_token(self) -> str:
if isinstance(self._graph_access_token, str) and len(self._graph_access_token) > 0:
return self._graph_access_token

raise ValueError("User has no graph access token")

def has_graph_access_token(self) -> bool:
try:
self.get_graph_access_token()
return True
except:
return False

def get_sumo_access_token(self) -> str:
if isinstance(self._sumo_access_token, str) and len(self._sumo_access_token) > 0:
Expand Down
1 change: 1 addition & 0 deletions frontend/src/api/models/UserInfo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

export type UserInfo = {
username: string;
avatar_b64str: (string | null);
has_sumo_access: boolean;
has_smda_access: boolean;
};
Expand Down
11 changes: 10 additions & 1 deletion frontend/src/api/services/DefaultService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,13 +69,22 @@ export class DefaultService {

/**
* Logged In User
* @param includeAvatar Set to true to include user avatar from Microsoft GRAPH Api
* @returns UserInfo Successful Response
* @throws ApiError
*/
public loggedInUser(): CancelablePromise<UserInfo> {
public loggedInUser(
includeAvatar: boolean = false,
): CancelablePromise<UserInfo> {
return this.httpRequest.request({
method: 'GET',
url: '/logged_in_user',
query: {
'includeAvatar': includeAvatar,
},
errors: {
422: `Validation Error`,
},
});
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,16 @@ export const LoginButton: React.FC<LoginButtonProps> = (props) => {

function makeIcon() {
if (authState === AuthState.LoggedIn) {
if (userInfo?.avatar_b64str) {
return (
<img
src={`data:image/png;base64,${userInfo.avatar_b64str}`}
alt="Avatar"
className="w-4 h-4 rounded-full"
/>
);
}

return <UserIcon className="w-5 h-5 mr-1" />;
} else if (authState === AuthState.NotLoggedIn) {
return <ArrowLeftOnRectangleIcon className="w-5 h-5 mr-1" />;
Expand Down

0 comments on commit bae51f0

Please sign in to comment.