Learning Docs 0.1.0 버전

Sign-in (로그인)

Endpoint

POST /client/auth/sign-inAPI 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