Skip to content

Commit b714dc5

Browse files
feat: add google oauht feature
1 parent f7cb251 commit b714dc5

File tree

12 files changed

+807
-17
lines changed

12 files changed

+807
-17
lines changed

.env.local.example

+8-2
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,8 @@
1-
REACT_APP_API_BASE_URL=https://example.com
2-
REACT_APP_TOKEN_KEY=EXAMPLE_TOKEN_KEY
1+
REACT_APP_API_BASE_URL=...
2+
REACT_APP_TOKEN_KEY=...
3+
REACT_APP_FIREBASE_API_KEY=...
4+
REACT_APP_FIREBASE_AUTH_DOMAIN=...
5+
REACT_APP_FIREBASE_PROJECT_ID=...
6+
REACT_APP_FIREBASE_STORAGE_BUCKET=...
7+
REACT_APP_FIREBASE_MESSAGING_SENDER_ID=...
8+
REACT_APP_FIREBASE_APP_ID=...

README.md

+8-2
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,14 @@ yarn install
2626
Set up environment variable
2727
- Create .env.local file
2828
```
29-
REACT_APP_API_BASE_URL=https://example.com
30-
REACT_APP_TOKEN_KEY=EXAMPLE_TOKEN_KEY
29+
REACT_APP_API_BASE_URL=...
30+
REACT_APP_TOKEN_KEY=...
31+
REACT_APP_FIREBASE_API_KEY=...
32+
REACT_APP_FIREBASE_AUTH_DOMAIN=...
33+
REACT_APP_FIREBASE_PROJECT_ID=...
34+
REACT_APP_FIREBASE_STORAGE_BUCKET=...
35+
REACT_APP_FIREBASE_MESSAGING_SENDER_ID=...
36+
REACT_APP_FIREBASE_APP_ID=...
3137
```
3238

3339
Finally, run the development server.

package.json

+1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
"private": true,
55
"dependencies": {
66
"@reduxjs/toolkit": "^1.8.2",
7+
"firebase": "^9.9.1",
78
"react": "^18.1.0",
89
"react-dom": "^18.1.0",
910
"react-redux": "^8.0.2",

src/components/icons/GoogleIcon.js

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
export default function GoogleIcon({ className }) {
2+
return (
3+
<svg className={className} viewBox="0 0 48 48" xmlns="http://www.w3.org/2000/svg">
4+
<path
5+
d="M43.611,20.083H42V20H24v8h11.303c-1.649,4.657-6.08,8-11.303,8c-6.627,0-12-5.373-12-12c0-6.627,5.373-12,12-12c3.059,0,5.842,1.154,7.961,3.039l5.657-5.657C34.046,6.053,29.268,4,24,4C12.955,4,4,12.955,4,24c0,11.045,8.955,20,20,20c11.045,0,20-8.955,20-20C44,22.659,43.862,21.35,43.611,20.083z"
6+
fill="#FFC107"
7+
/>
8+
<path
9+
d="M6.306,14.691l6.571,4.819C14.655,15.108,18.961,12,24,12c3.059,0,5.842,1.154,7.961,3.039l5.657-5.657C34.046,6.053,29.268,4,24,4C16.318,4,9.656,8.337,6.306,14.691z"
10+
fill="#FF3D00"
11+
/>
12+
<path
13+
d="M24,44c5.166,0,9.86-1.977,13.409-5.192l-6.19-5.238C29.211,35.091,26.715,36,24,36c-5.202,0-9.619-3.317-11.283-7.946l-6.522,5.025C9.505,39.556,16.227,44,24,44z"
14+
fill="#4CAF50"
15+
/>
16+
<path
17+
d="M43.611,20.083H42V20H24v8h11.303c-0.792,2.237-2.231,4.166-4.087,5.571c0.001-0.001,0.002-0.001,0.003-0.002l6.19,5.238C36.971,39.205,44,34,44,24C44,22.659,43.862,21.35,43.611,20.083z"
18+
fill="#1976D2"
19+
/>
20+
</svg>
21+
);
22+
}

src/components/icons/index.js

+2
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import DollarSignIcon from './DollarSignIcon';
1616
import SignInIcon from './SignInIcon';
1717
import MenuIcon from './MenuIcon';
1818
import LogInIcon from './LogInIcon';
19+
import GoogleIcon from './GoogleIcon';
1920

2021
export {
2122
HeartIcon,
@@ -36,4 +37,5 @@ export {
3637
SignInIcon,
3738
MenuIcon,
3839
LogInIcon,
40+
GoogleIcon,
3941
};

src/constants/environment.js

+6
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,8 @@
11
export const apiBaseURL = process.env.REACT_APP_API_BASE_URL;
22
export const localStorageTokenKey = process.env.REACT_APP_TOKEN_KEY;
3+
export const firebaseApiKey = process.env.REACT_APP_FIREBASE_API_KEY;
4+
export const firebaseAuthDomain = process.env.REACT_APP_FIREBASE_AUTH_DOMAIN;
5+
export const firebaseProjectId = process.env.REACT_APP_FIREBASE_PROJECT_ID;
6+
export const firebaseStorageBucket = process.env.REACT_APP_FIREBASE_STORAGE_BUCKET;
7+
export const firebaseMessagingSenderId = process.env.REACT_APP_FIREBASE_MESSAGING_SENDER_ID;
8+
export const firebaseFirebaseAppId = process.env.REACT_APP_FIREBASE_APP_ID;

src/hooks/dependent/useAuth.js

+36-3
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { localStorageTokenKey } from '../../constants/environment';
55
import { SET_ALERT } from '../../redux/slice/alert';
66
import { CHANGE_AUTH, SET_TOKEN } from '../../redux/slice/auth';
77
import { loginService, registerService } from '../../services/api/auth';
8+
import { getUserDataWithGoogle } from '../../services/firebase/auth';
89
import useError from './useError';
910

1011
export default function useAuth() {
@@ -15,7 +16,7 @@ export default function useAuth() {
1516
const [loading, setLoading] = useState({ register: false, login: false });
1617
const errorHandler = useError();
1718

18-
async function register(name, email, password) {
19+
async function register(name, email, password, autoLogin) {
1920
setLoading({ ...loading, register: true });
2021
try {
2122
const res = await registerService(name, email, password);
@@ -26,7 +27,7 @@ export default function useAuth() {
2627

2728
dispatch(SET_ALERT([{ status: 'success', message: res.msg }]));
2829

29-
navigate('/login');
30+
!autoLogin ? navigate('/login') : login(email, password);
3031
} catch (error) {
3132
errorHandler(error);
3233
} finally {
@@ -88,5 +89,37 @@ export default function useAuth() {
8889
dispatch(CHANGE_AUTH(true));
8990
}, [dispatch, isAuthenticated, pathname]);
9091

91-
return { loading, register, login, logout, checkToken };
92+
async function registerWithGoogle() {
93+
setLoading({ ...loading, register: true });
94+
try {
95+
const res = await getUserDataWithGoogle();
96+
97+
const isError = errorHandler(res);
98+
99+
if (isError) return;
100+
101+
register(res.displayName, res.email, res.uid, true);
102+
} catch (error) {
103+
errorHandler(error);
104+
setLoading({ ...loading, register: false });
105+
}
106+
}
107+
108+
async function loginWithGoogle() {
109+
setLoading({ ...loading, login: true });
110+
try {
111+
const res = await getUserDataWithGoogle();
112+
113+
const isError = errorHandler(res);
114+
115+
if (isError) return;
116+
117+
login(res.email, res.uid);
118+
} catch (error) {
119+
errorHandler(error);
120+
setLoading({ ...loading, login: false });
121+
}
122+
}
123+
124+
return { loading, register, login, logout, checkToken, registerWithGoogle, loginWithGoogle };
92125
}

src/pages/Login.js

+11-1
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
import { useState } from 'react';
22
import { Link } from 'react-router-dom';
33
import { PrimaryButton, BackButton } from '../components/buttons';
4+
import { GoogleIcon } from '../components/icons';
45
import { LabelPasswordInput, LabelTextInput } from '../components/inputs';
56
import { AuthLayout } from '../components/layouts';
67
import useAuth from '../hooks/dependent/useAuth';
78
import AuthenticationRoute from '../routes/AuthenticationRoute';
89

910
export default function Login() {
10-
const { login, loading } = useAuth();
11+
const { login, loading, loginWithGoogle } = useAuth();
1112
const [form, setForm] = useState({ email: '', password: '' });
1213

1314
const setFormInput = (e) => setForm({ ...form, [e.target.name]: e.target.value });
@@ -50,6 +51,15 @@ export default function Login() {
5051
>
5152
Masuk
5253
</PrimaryButton>
54+
<button
55+
className="mt-6 flex w-full items-center justify-center space-x-2 rounded-2xl py-2 shadow-low"
56+
disabled={loading.login}
57+
onClick={loginWithGoogle}
58+
type="button"
59+
>
60+
<span className="text-body-14">Masuk dengan</span>
61+
<GoogleIcon className="h-8 w-8" />
62+
</button>
5363
</form>
5464
<div className="absolute bottom-6 left-0 flex w-full items-center justify-center space-x-2 lg:static lg:mt-10">
5565
<p className="text-body-14 text-[#151515] lg:text-[#000]">Belum punya akun?</p>

src/pages/Register.js

+14-7
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,12 @@ import { Link } from 'react-router-dom';
33
import { BackButton, PrimaryButton } from '../components/buttons';
44
import { LabelPasswordInput, LabelTextInput } from '../components/inputs';
55
import { AuthLayout } from '../components/layouts';
6+
import { GoogleIcon } from '../components/icons';
67
import useAuth from '../hooks/dependent/useAuth';
78
import AuthenticationRoute from '../routes/AuthenticationRoute';
89

910
export default function Register() {
10-
const { register, loading } = useAuth();
11+
const { register, loading, registerWithGoogle } = useAuth();
1112
const [form, setForm] = useState({ name: '', email: '', password: '' });
1213

1314
const setFormInput = (e) => setForm({ ...form, [e.target.name]: e.target.value });
@@ -17,12 +18,7 @@ export default function Register() {
1718
<AuthLayout>
1819
<BackButton className="absolute top-[14px] left-4 lg:hidden" />
1920
<h1 className="text-heading-24 font-bold">Daftar</h1>
20-
<form
21-
onSubmit={(e) => {
22-
e.preventDefault();
23-
register(form.name, form.email, form.password);
24-
}}
25-
>
21+
<form onSubmit={(e) => e.preventDefault()}>
2622
<LabelTextInput
2723
autoFocus
2824
id="name"
@@ -55,10 +51,21 @@ export default function Register() {
5551
className="mt-6 w-full"
5652
data-testid="register-button"
5753
isDisable={loading.register}
54+
onClick={() => register(form.name, form.email, form.password)}
5855
type="submit"
5956
>
6057
Daftar
6158
</PrimaryButton>
59+
60+
<button
61+
className="mt-6 flex w-full items-center justify-center space-x-2 rounded-2xl py-2 shadow-low"
62+
disabled={loading.register}
63+
onClick={registerWithGoogle}
64+
type="button"
65+
>
66+
<span className="text-body-14">Daftar dengan</span>
67+
<GoogleIcon className="h-8 w-8" />
68+
</button>
6269
</form>
6370
<div className="absolute bottom-6 left-0 flex w-full items-center justify-center space-x-2 lg:static lg:mt-10">
6471
<p className="text-body-14 text-[#151515] lg:text-[#000]">Sudah punya akun?</p>

src/services/firebase/auth.js

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import { signInWithPopup, GoogleAuthProvider, getAuth } from 'firebase/auth';
2+
import app from '.';
3+
4+
export const getUserDataWithGoogle = () => {
5+
return new Promise((resolve, reject) => {
6+
signInWithPopup(getAuth(app), new GoogleAuthProvider())
7+
.then((result) => resolve(result.user))
8+
.catch((error) => reject(error.message));
9+
});
10+
};

src/services/firebase/index.js

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// Import the functions you need from the SDKs you need
2+
import { initializeApp } from 'firebase/app';
3+
import {
4+
firebaseApiKey,
5+
firebaseAuthDomain,
6+
firebaseFirebaseAppId,
7+
firebaseMessagingSenderId,
8+
firebaseProjectId,
9+
firebaseStorageBucket,
10+
} from '../../constants/environment';
11+
// TODO: Add SDKs for Firebase products that you want to use
12+
// https://firebase.google.com/docs/web/setup#available-libraries
13+
14+
// Your web app's Firebase configuration
15+
const firebaseConfig = {
16+
apiKey: firebaseApiKey,
17+
authDomain: firebaseAuthDomain,
18+
projectId: firebaseProjectId,
19+
storageBucket: firebaseStorageBucket,
20+
messagingSenderId: firebaseMessagingSenderId,
21+
appId: firebaseFirebaseAppId,
22+
};
23+
24+
// Initialize Firebase
25+
const app = initializeApp(firebaseConfig);
26+
27+
export default app;

0 commit comments

Comments
 (0)