Skip to content

Commit

Permalink
Start simple auth addition to web
Browse files Browse the repository at this point in the history
  • Loading branch information
koredefashokun committed Sep 5, 2024
1 parent 56188c0 commit 6cd789a
Show file tree
Hide file tree
Showing 5 changed files with 112 additions and 17 deletions.
11 changes: 10 additions & 1 deletion apps/web/src/app/login/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import { zodResolver } from '@hookform/resolvers/zod';
import Link from 'next/link';
import { useRouter } from 'next/router';
import { useForm } from 'react-hook-form';
import { useMutation, gql } from 'urql';
import { z } from 'zod';
Expand All @@ -16,13 +17,16 @@ import {
FormMessage
} from '@/components/ui/form';
import { Input } from '@/components/ui/input';
import { useAuthContext } from '@/contexts/AuthContext';

const loginSchema = z.object({
email: z.string().email(),
password: z.string().min(8)
});

const LoginPage = () => {
const router = useRouter();

const LOGIN_MUTATION = gql`
mutation Login($input: AuthenticateInput!) {
authenticate(input: $input) {
Expand All @@ -41,11 +45,16 @@ const LoginPage = () => {
}
});

const { onLogin } = useAuthContext();

const onSubmit = async (values: z.infer<typeof loginSchema>) => {
const { data } = await login({
input: { email: values.email, password: values.password }
});
console.log(data);

onLogin(data.authenticate.accessToken, data.authenticate.userId);
// TODO: Handle non-home origin routes.
router.push('/');
};

return (
Expand Down
19 changes: 15 additions & 4 deletions apps/web/src/app/providers.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,27 @@
'use client';

import React from 'react';
import { Provider } from 'urql';

import client from '@/config/client';
import { generateClient } from '@/config/client';
import { AuthProvider, useAuthContext } from '@/contexts/AuthContext';

// TODO: Fix any type
type ProvidersProps = {
children: any;
children: React.ReactNode;
};

const Providers: React.FC<ProvidersProps> = ({ children }) => {
return <Provider value={client}>{children}</Provider>;
const { accessToken } = useAuthContext();

const client = React.useMemo(() => {
return generateClient(accessToken);
}, [accessToken]);

return (
<AuthProvider>
<Provider value={client}>{children}</Provider>
</AuthProvider>
);
};

export default Providers;
17 changes: 11 additions & 6 deletions apps/web/src/components/home/Header.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import Link from 'next/link';

import { Button } from '@/components/ui/button';
import { useAuthContext } from '@/contexts/AuthContext';

const Header = () => {
const { userId } = useAuthContext();

return (
<nav
style={{
Expand All @@ -15,12 +18,14 @@ const Header = () => {
<Link href='/'>
<p style={{ fontWeight: '500', fontSize: '1.1rem' }}>Habiti</p>
</Link>
<div style={{ display: 'flex', gap: 16, alignItems: 'center' }}>
<Link href='/login'>Login</Link>
<Link href='/register'>
<Button>Register</Button>
</Link>
</div>
{!userId && (
<div style={{ display: 'flex', gap: 16, alignItems: 'center' }}>
<Link href='/login'>Login</Link>
<Link href='/register'>
<Button>Register</Button>
</Link>
</div>
)}
</nav>
);
};
Expand Down
17 changes: 11 additions & 6 deletions apps/web/src/config/client.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
import { createClient, cacheExchange, fetchExchange } from 'urql';

const client = createClient({
url: process.env.NEXT_PUBLIC_API_URL || 'http://localhost:4000/graphql',
exchanges: [cacheExchange, fetchExchange]
});

export default client;
export const generateClient = (accessToken?: string) => {
return createClient({
url: process.env.NEXT_PUBLIC_API_URL || 'http://localhost:4000/graphql',
exchanges: [cacheExchange, fetchExchange],
fetchOptions: {
headers: {
Authorization: `Bearer ${accessToken}`
}
}
});
};
65 changes: 65 additions & 0 deletions apps/web/src/contexts/AuthContext.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import React from 'react';

interface AuthContextType {
accessToken?: string;
userId?: string;
onLogin: (accessToken: string, userId: string) => void;
}

const AuthContext = React.createContext<AuthContextType>({
accessToken: undefined,
userId: undefined,
onLogin: () => {}
// isLoggedIn: false,
// onLogout: () => {},
// onLogin: (email, password) => {}
});

export const AuthProvider: React.FC<{ children: React.ReactNode }> = ({
children
}) => {
const [accessToken, setAccessToken] = React.useState<string>();
const [userId, setUserId] = React.useState<string>();

React.useEffect(() => {
const accessToken = window.localStorage.getItem('accessToken');
const userId = window.localStorage.getItem('userId');

if (accessToken && userId) {
setAccessToken(accessToken);
setUserId(userId);
}
}, []);

const handleLogin = (accessToken: string, userId: string) => {
setAccessToken(accessToken);
setUserId(userId);
window.localStorage.setItem('accessToken', accessToken);
window.localStorage.setItem('userId', userId);
};

return (
<AuthContext.Provider
value={{
accessToken,
userId,
onLogin: handleLogin
// isLoggedIn: false,
// onLogout: () => {},
// onLogin: (email, password) => {}
}}
>
{children}
</AuthContext.Provider>
);
};

export const useAuthContext = () => {
const context = React.useContext(AuthContext);

if (!context) {
throw new Error('useAuthContext must be used within an AuthProvider');
}

return context;
};

0 comments on commit 6cd789a

Please sign in to comment.