Sign-in (로그인)
Endpoint
POST /client/auth/sign-in — API Reference
SDK 호출
import { ClientAuth } from '@classum/learning-sdk'
const res = await ClientAuth.signIn({
body: {
tenantKey: 'classum', // user-facing tenant 식별자
userId: 'windy', // tenant 안 user id
password: 'pw-12345678',
},
})
if (res.error) {
// 401 InvalidCredentials — tenant / userId / password 어떤 조합이든 401 통일 (info leak 방지)
throw new Error('로그인 실패')
}
const { accessToken, accessTokenExpiresAt, user } = res.data!
hooks.setAccessToken(accessToken, accessTokenExpiresAt)
// refresh token 은 자동으로 Set-Cookie 로 처리 (HttpOnly, SameSite=Strict, Domain=.classum.io).
응답 shape
{
accessToken: string // JWT, 15분 만료
accessTokenExpiresAt: string // ISO 만료 시각 — proactive refresh 에 활용
user: {
id, tenantId, userId, email, emailVerifiedAt,
displayName, role, source, lastLoginAt, createdAt, updatedAt
}
}
Set-Cookie (자동 처리, frontend code 가 직접 만질 일 없음):
Set-Cookie: service_refresh=<secret>;
HttpOnly; Secure; SameSite=Strict;
Path=/client/auth; Domain=.classum.io;
Max-Age=2592000 (30일)
에러 코드
| status | code | 의미 |
|---|---|---|
| 400 | (zod) | request body 형식 오류 |
| 401 | auth/InvalidCredentials |
tenant / userId / password 불일치. tenant 없음 / soft-delete / userId 없음 / 비번 틀림 모두 같은 응답 (info leak 방지). |
React 폼 예시
function SignInForm() {
const navigate = useNavigate()
const { message } = App.useApp()
const mutation = useMutation({
mutationFn: async (input: { tenantKey: string; userId: string; password: string }) => {
const res = await ClientAuth.signIn({ body: input })
if (res.error) throw new Error(res.error.message ?? 'sign-in failed')
return res.data!
},
onSuccess: (data) => {
hooks.setAccessToken(data.accessToken, data.accessTokenExpiresAt)
navigate('/')
},
onError: () => message.error('로그인 실패. 정보를 다시 확인해주세요.'),
})
return (
<Form onFinish={(v) => mutation.mutate(v)}>
<Form.Item name="tenantKey" rules={[{ required: true }]}><Input placeholder="회사 ID" /></Form.Item>
<Form.Item name="userId" rules={[{ required: true }]}><Input placeholder="사용자 ID" /></Form.Item>
<Form.Item name="password" rules={[{ required: true }]}><Input.Password /></Form.Item>
<Button htmlType="submit" loading={mutation.isPending}>로그인</Button>
</Form>
)
}
후속 흐름
- access token 만료 시 SDK 가 자동 refresh — Refresh 가이드
- 이메일 미인증 user (
source: sign_up) 는 일부 endpoint 차단 — Sign-up + verify 가이드 - 로그아웃 —
ClientAuth.signOut({})호출 + sessionStorage clear