Google Workspace OAuth2 적용

This commit is contained in:
Hyojin Ahn 2026-06-23 16:19:13 -04:00
parent 7d7e6607f8
commit d71cec4709
3 changed files with 68 additions and 4 deletions

View File

@ -1,8 +1,15 @@
import { useState } from 'react' import { useEffect, useState } from 'react'
import { fetchWithCred } from '../../lib/api' import { fetchWithCred } from '../../lib/api'
import { authUrl } from '../../lib/endpoints' import { authUrl } from '../../lib/endpoints'
import { useAuth } from '../../app/providers/AuthProvider' import { useAuth } from '../../app/providers/AuthProvider'
// OAuth2 ?login_error= .
const LOGIN_ERROR_MESSAGES = {
not_registered: '등록되지 않은 계정입니다. 관리자에게 문의하세요.',
domain_not_allowed: 'greenoilinc.com 계정으로만 로그인할 수 있습니다.',
inactive: '비활성화된 계정입니다. 관리자에게 문의하세요.',
}
export default function Login() { export default function Login() {
const { login } = useAuth() const { login } = useAuth()
@ -13,6 +20,19 @@ export default function Login() {
const [loading, setLoading] = useState(false) const [loading, setLoading] = useState(false)
const [error, setError] = useState(null) const [error, setError] = useState(null)
// OAuth
useEffect(() => {
const params = new URLSearchParams(window.location.search)
const code = params.get('login_error')
if (code) {
setError(LOGIN_ERROR_MESSAGES[code] ?? '로그인에 실패했습니다.')
// ( )
params.delete('login_error')
const clean = window.location.pathname + (params.toString() ? `?${params}` : '')
window.history.replaceState({}, '', clean)
}
}, [])
const handleChange = (e) => { const handleChange = (e) => {
setForm({ setForm({
...form, ...form,
@ -48,6 +68,11 @@ export default function Login() {
} }
} }
const handleGoogleLogin = () => {
// fetch ! (Google )
window.location.href = authUrl('/oauth2/authorization/google')
}
return ( return (
<form className="login-box" onSubmit={handleLogin}> <form className="login-box" onSubmit={handleLogin}>
<h2 className="login-title">GREEN FIELD</h2> <h2 className="login-title">GREEN FIELD</h2>
@ -80,6 +105,17 @@ export default function Login() {
> >
{loading ? 'Signing in...' : 'Login'} {loading ? 'Signing in...' : 'Login'}
</button> </button>
<div className="login-divider"><span>또는</span></div>
<button
type="button"
className="login-button login-google"
onClick={handleGoogleLogin}
disabled={loading}
>
Google로 로그인
</button>
</form> </form>
) )
} }

View File

@ -146,8 +146,8 @@ export default function CsHomePage() {
{custErr && <span className="cshome-section-note">고객 요약 미연동 · {custErr}</span>} {custErr && <span className="cshome-section-note">고객 요약 미연동 · {custErr}</span>}
</div> </div>
<div className="cshome-grid cshome-grid-2"> <div className="cshome-grid cshome-grid-2">
<Card label="전체 고객 수" value={fmt(cust?.total)} unit="" /> <Card label="전체 고객 수" value={fmt(cust?.total)} unit="" />
<Card label="오늘 추가된 고객" value={fmt(cust?.today)} unit="" accent /> <Card label="오늘 추가된 고객" value={fmt(cust?.today)} unit="" accent />
</div> </div>
</div> </div>

View File

@ -120,3 +120,31 @@ button {
text-align: center; text-align: center;
} }
/* google login */
.login-divider {
display: flex;
align-items: center;
text-align: center;
color: #9aa0a6;
font-size: 12px;
}
.login-divider::before,
.login-divider::after {
content: '';
flex: 1;
border-bottom: 1px solid #e3e5e8;
}
.login-divider span {
padding: 0 10px;
}
.login-google {
background: white;
color: #3c4043;
border: 1px solid #dadce0;
}
.login-google:disabled {
background: #f5f5f5;
color: #9aa0a6;
cursor: not-allowed;
}