Skip to content

Commit df34cc3

Browse files
authored
Merge pull request #255 from CSE-Shaco/develop
test: add google login test
2 parents cd2767c + 95cf8ca commit df34cc3

File tree

3 files changed

+124
-7
lines changed

3 files changed

+124
-7
lines changed

src/app/test/login_test/page.jsx

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
'use client';
2+
3+
import { useMemo, useState } from 'react';
4+
import { useAuthenticatedApi } from '@/hooks/useAuthenticatedApi';
5+
import { useAuth } from '@/hooks/useAuth';
6+
import { GoogleLogin } from '@/services/auth/signin/google/GoogleLogin';
7+
8+
const NEXT_PATH = '/test/login_test';
9+
10+
export default function LoginTestPage() {
11+
const { apiClient } = useAuthenticatedApi();
12+
const { accessToken } = useAuth();
13+
const { handleGoogleLogin } = GoogleLogin();
14+
15+
const isLoggedIn = Boolean(accessToken);
16+
const maskedToken = useMemo(() => {
17+
if (!accessToken) return '-';
18+
const head = String(accessToken).slice(0, 8);
19+
const tail = String(accessToken).slice(-6);
20+
return `${head}...${tail}`;
21+
}, [accessToken]);
22+
23+
const [loading, setLoading] = useState(false);
24+
const [httpStatus, setHttpStatus] = useState(null);
25+
const [payload, setPayload] = useState(null);
26+
const [errorText, setErrorText] = useState('');
27+
28+
const handleCallTestApi = async () => {
29+
setLoading(true);
30+
setErrorText('');
31+
setPayload(null);
32+
setHttpStatus(null);
33+
try {
34+
const res = await apiClient.get('/test/login_test');
35+
setHttpStatus(res.status);
36+
setPayload(res.data);
37+
} catch (err) {
38+
const status = err?.response?.status ?? 'NETWORK_ERROR';
39+
setHttpStatus(status);
40+
if (err?.response?.data) setPayload(err.response.data);
41+
setErrorText(String(err?.message || 'request failed'));
42+
} finally {
43+
setLoading(false);
44+
}
45+
};
46+
47+
return (
48+
<div className='min-h-screen w-full px-6 py-10 text-white'>
49+
<h1 className='text-2xl font-bold mb-6'>Google Login Test</h1>
50+
51+
<section className='mb-6'>
52+
<div className='mb-1'>Current login state</div>
53+
<div className={`inline-block rounded px-3 py-1 ${isLoggedIn ? 'bg-green-600' : 'bg-red-600'}`}>
54+
{isLoggedIn ? 'Logged in' : 'Not logged in'}
55+
</div>
56+
<div className='mt-2 text-sm text-gray-300'>Access token: {maskedToken}</div>
57+
</section>
58+
59+
<section className='mb-6 flex flex-wrap gap-3'>
60+
<button
61+
onClick={() => handleGoogleLogin({ next: NEXT_PATH })}
62+
className='rounded px-4 py-2 bg-emerald-600 hover:bg-emerald-500'
63+
>
64+
Start Google Login
65+
</button>
66+
<button
67+
onClick={handleCallTestApi}
68+
disabled={loading}
69+
className={`rounded px-4 py-2 ${loading ? 'bg-gray-500' : 'bg-blue-600 hover:bg-blue-500'}`}
70+
>
71+
{loading ? 'Requesting...' : 'Call Test API (/api/v1/test/login_test)'}
72+
</button>
73+
</section>
74+
75+
<section>
76+
<div className='mb-2 font-semibold'>Response</div>
77+
<div className='mb-1 text-sm'>HTTP Status: {httpStatus ?? '-'}</div>
78+
{errorText && (
79+
<div className='mb-2 text-red-400 text-sm'>Error: {errorText}</div>
80+
)}
81+
<pre className='bg-black/40 rounded p-3 overflow-auto max-h-[50vh] text-sm'>
82+
{payload ? JSON.stringify(payload, null, 2) : '-'}
83+
</pre>
84+
</section>
85+
</div>
86+
);
87+
}

src/services/auth/signin/google/GoogleAuth.js

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,29 @@ export const GoogleAuthComponent = () => {
2222
}
2323

2424
const code = encodeURIComponent(decodedCode);
25+
const rawState = searchParams.get('state');
26+
let nextPath = null;
27+
if (rawState) {
28+
try {
29+
const decodedState = decodeURIComponent(rawState);
30+
if (decodedState.startsWith('/')) {
31+
nextPath = decodedState;
32+
}
33+
} catch {
34+
if (rawState.startsWith('/')) {
35+
nextPath = rawState;
36+
}
37+
}
38+
}
2539

2640
exchangeGoogleToken(code)
2741
.then((res) => {
28-
const { exists, access_token, email, name } = res.data.data;
42+
const data = res?.data?.data || {};
43+
const exists = typeof data.exists === 'boolean' ? data.exists : data.isExists;
44+
const { access_token, email, name } = data;
2945
if (exists) {
3046
setAccessToken(access_token);
31-
router.push('/main');
47+
router.push(nextPath || '/main');
3248
} else {
3349
alert('회원 정보가 없습니다. 회원가입을 완료해주세요.');
3450
sessionStorage.setItem('signup_email', email);
@@ -53,4 +69,4 @@ export const GoogleAuth = () => (
5369
<Suspense fallback={<Loader isLoading={true} />}>
5470
<GoogleAuthComponent />
5571
</Suspense>
56-
);
72+
);
Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,25 @@
11
const clientId = process.env.NEXT_PUBLIC_GOOGLE_REDIRECT_CLIENT_ID;
22
const redirectUri = process.env.NEXT_PUBLIC_GOOGLE_REDIRECT_URI;
3-
const googleLoginUrl = `https://accounts.google.com/o/oauth2/auth?client_id=${clientId}&redirect_uri=${redirectUri}&response_type=code&scope=email profile`;
3+
const googleLoginBaseUrl = `https://accounts.google.com/o/oauth2/auth?client_id=${clientId}&redirect_uri=${redirectUri}&response_type=code&scope=email profile`;
4+
5+
const buildGoogleLoginUrl = (nextPath) => {
6+
if (!nextPath || typeof nextPath !== 'string') {
7+
return googleLoginBaseUrl;
8+
}
9+
10+
const trimmed = nextPath.trim();
11+
if (!trimmed.startsWith('/')) {
12+
return googleLoginBaseUrl;
13+
}
14+
15+
const stateParam = encodeURIComponent(trimmed);
16+
return `${googleLoginBaseUrl}&state=${stateParam}`;
17+
};
418

519
export const GoogleLogin = () => {
6-
const handleGoogleLogin = () => {
7-
window.location.href = googleLoginUrl;
20+
const handleGoogleLogin = ({ next } = {}) => {
21+
window.location.href = buildGoogleLoginUrl(next);
822
};
923

1024
return {handleGoogleLogin};
11-
};
25+
};

0 commit comments

Comments
 (0)