diff --git a/front/src/App.tsx b/front/src/App.tsx index 188ac2aac..6ae929665 100644 --- a/front/src/App.tsx +++ b/front/src/App.tsx @@ -9,11 +9,13 @@ import RequireAuth from './components/auth/RequireAuth'; import Opportunities from './pages/opportunities/Opportunities'; import { User, mapUser } from './interfaces/user.interface'; import { useGetCurrentUserQuery } from './services/users'; +import { getUserIdFromToken } from './services/AuthService'; function App() { const [user, setUser] = useState(undefined); - const { data } = useGetCurrentUserQuery(); + const userIdFromToken = getUserIdFromToken(); + const { data } = useGetCurrentUserQuery(userIdFromToken); useEffect(() => { if (data?.users[0]) { diff --git a/front/src/__stories__/App.stories.tsx b/front/src/__stories__/App.stories.tsx index 2c1964016..f36c02834 100644 --- a/front/src/__stories__/App.stories.tsx +++ b/front/src/__stories__/App.stories.tsx @@ -10,12 +10,18 @@ const component = { }; localStorage.setItem('refreshToken', 'xxx-refresh'); -localStorage.setItem('accessToken', 'xxx-access'); +localStorage.setItem( + 'accessToken', + 'eyJhbGciOiJIUzI1NiJ9.eyJodHRwczovL2hhc3VyYS5pby9qd3QvY2xhaW1zIjp7IngtaGFzdXJhLXdvcmtzcGFjZS1pZCI6IjdlZDlkMjEyLTFjMjUtNGQwMi1iZjI1LTZhZWNjZjdlYTQxOSIsIngtaGFzdXJhLWFsbG93ZWQtcm9sZXMiOlsibWUiLCJ1c2VyIl0sIngtaGFzdXJhLWRlZmF1bHQtcm9sZSI6InVzZXIiLCJ4LWhhc3VyYS11c2VyLWlkIjoiMTY1MDZiYTgtMTk2Yy00YzEzLWE0YTctYTIyY2I1ZWNjZmExIiwieC1oYXN1cmEtdXNlci1pcy1hbm9ueW1vdXMiOiJmYWxzZSJ9LCJzdWIiOiIxNjUwNmJhOC0xOTZjLTRjMTMtYTRhNy1hMjJjYjVlY2NmYTEiLCJpYXQiOjE2ODM4NzM5NzIsImV4cCI6MTY4Mzg3NDg3MiwiaXNzIjoiaGFzdXJhLWF1dGgifQ.C_OynseOprgU-SdLBLzMdfg_441eopI7LYg8yB86g3c', +); const mocks = [ { request: { query: GET_CURRENT_USER, + variables: { + uuid: '16506ba8-196c-4c13-a4a7-a22cb5eccfa1', + }, }, result: { data: { diff --git a/front/src/layout/navbar/WorkspaceContainer.tsx b/front/src/layout/navbar/WorkspaceContainer.tsx index 9b7f870b3..01a97916a 100644 --- a/front/src/layout/navbar/WorkspaceContainer.tsx +++ b/front/src/layout/navbar/WorkspaceContainer.tsx @@ -7,7 +7,6 @@ type OwnProps = { const StyledContainer = styled.button` display: inline-flex; - width: min-content; height: 34px; align-items: center; cursor: pointer; @@ -17,6 +16,7 @@ const StyledContainer = styled.button` border-radius: ${(props) => props.theme.spacing(1)}; padding: ${(props) => props.theme.spacing(2)}; margin-left: ${(props) => props.theme.spacing(1)}; + align-self: flex-start; `; type StyledLogoProps = { @@ -36,7 +36,6 @@ const StyledName = styled.div` font-family: 'Inter'; font-weight: 500; font-size: ${(props) => props.theme.fontSizeLarge}; - font-color: ${(props) => props.theme.text0}; `; function WorkspaceContainer({ workspace }: OwnProps) { diff --git a/front/src/services/AuthService.ts b/front/src/services/AuthService.ts index 0031ee17a..0f53d4d06 100644 --- a/front/src/services/AuthService.ts +++ b/front/src/services/AuthService.ts @@ -1,9 +1,24 @@ +import jwt from 'jwt-decode'; + export const hasAccessToken = () => { const accessToken = localStorage.getItem('accessToken'); return accessToken ? true : false; }; +export const getUserIdFromToken: () => string | null = () => { + const accessToken = localStorage.getItem('accessToken'); + if (!accessToken) { + return null; + } + + try { + return jwt<{ sub: string }>(accessToken).sub; + } catch (error) { + return null; + } +}; + export const hasRefreshToken = () => { const refreshToken = localStorage.getItem('refreshToken'); diff --git a/front/src/services/__tests__/AuthService.test.tsx b/front/src/services/__tests__/AuthService.test.tsx index 3256ae414..c1ab3e221 100644 --- a/front/src/services/__tests__/AuthService.test.tsx +++ b/front/src/services/__tests__/AuthService.test.tsx @@ -3,6 +3,7 @@ import { hasAccessToken, hasRefreshToken, refreshAccessToken, + getUserIdFromToken, } from '../AuthService'; const mockFetch = async ( @@ -63,6 +64,26 @@ it('refreshToken refreshes the token if refresh token is valid', async () => { }); }); +it('getUserIdFromToken returns null when the token is not present', async () => { + const userId = getUserIdFromToken(); + expect(userId).toBeNull(); +}); + +it('getUserIdFromToken returns null when the token is not valid', async () => { + localStorage.setItem('accessToken', 'xxx-invalid-access'); + const userId = getUserIdFromToken(); + expect(userId).toBeNull(); +}); + +it('getUserIdFromToken returns the right userId when the token is valid', async () => { + localStorage.setItem( + 'accessToken', + 'eyJhbGciOiJIUzI1NiJ9.eyJodHRwczovL2hhc3VyYS5pby9qd3QvY2xhaW1zIjp7IngtaGFzdXJhLXdvcmtzcGFjZS1pZCI6IjdlZDlkMjEyLTFjMjUtNGQwMi1iZjI1LTZhZWNjZjdlYTQxOSIsIngtaGFzdXJhLWFsbG93ZWQtcm9sZXMiOlsibWUiLCJ1c2VyIl0sIngtaGFzdXJhLWRlZmF1bHQtcm9sZSI6InVzZXIiLCJ4LWhhc3VyYS11c2VyLWlkIjoiMTY1MDZiYTgtMTk2Yy00YzEzLWE0YTctYTIyY2I1ZWNjZmExIiwieC1oYXN1cmEtdXNlci1pcy1hbm9ueW1vdXMiOiJmYWxzZSJ9LCJzdWIiOiIxNjUwNmJhOC0xOTZjLTRjMTMtYTRhNy1hMjJjYjVlY2NmYTEiLCJpYXQiOjE2ODM4NzM5NzIsImV4cCI6MTY4Mzg3NDg3MiwiaXNzIjoiaGFzdXJhLWF1dGgifQ.C_OynseOprgU-SdLBLzMdfg_441eopI7LYg8yB86g3c', + ); + const userId = getUserIdFromToken(); + expect(userId).toBe('16506ba8-196c-4c13-a4a7-a22cb5eccfa1'); +}); + afterEach(() => { localStorage.clear(); }); diff --git a/front/src/services/users/index.tsx b/front/src/services/users/index.tsx index 744b202ec..eb8d6fe82 100644 --- a/front/src/services/users/index.tsx +++ b/front/src/services/users/index.tsx @@ -2,8 +2,8 @@ import { QueryResult, gql, useQuery } from '@apollo/client'; import { GraphqlQueryUser } from '../../interfaces/user.interface'; export const GET_CURRENT_USER = gql` - query GetCurrentUser { - users { + query GetCurrentUser($uuid: uuid) { + users(where: { id: { _eq: $uuid } }) { id email displayName @@ -19,8 +19,12 @@ export const GET_CURRENT_USER = gql` } `; -export function useGetCurrentUserQuery(): QueryResult<{ +export function useGetCurrentUserQuery(userId: string | null): QueryResult<{ users: GraphqlQueryUser[]; }> { - return useQuery<{ users: GraphqlQueryUser[] }>(GET_CURRENT_USER); + return useQuery<{ users: GraphqlQueryUser[] }>(GET_CURRENT_USER, { + variables: { + uuid: userId, + }, + }); } diff --git a/hasura/metadata/databases/default/tables/auth_users.yaml b/hasura/metadata/databases/default/tables/auth_users.yaml index 94f7892f6..d160fb53d 100644 --- a/hasura/metadata/databases/default/tables/auth_users.yaml +++ b/hasura/metadata/databases/default/tables/auth_users.yaml @@ -133,10 +133,35 @@ select_permissions: - role: user permission: columns: + - disabled + - email_verified + - is_anonymous + - phone_number_verified + - locale + - metadata + - active_mfa_type + - avatar_url + - default_role - display_name + - otp_hash + - otp_method_last_used + - password_hash + - phone_number + - ticket + - totp_secret + - webauthn_current_challenge + - created_at + - last_seen + - otp_hash_expires_at + - ticket_expires_at + - updated_at - email + - new_email - id - filter: {} + filter: + workspace_member: + workspace_id: + _eq: X-Hasura-Workspace-Id event_triggers: - name: user-created definition: