Soonlife DOCS
API 레퍼런스

인증 API

로그인, 세션 갱신, 로그아웃, 사용자 정보 조회

개요

SCH Web Portal의 인증은 HttpOnly 쿠키 기반 세션 방식을 사용합니다.
로그인 성공 시 SCH 서버로부터 획득한 jsessionid, ecd_key 등을 AES-256-GCM으로 암호화한 토큰을 sch_session 쿠키로 발급합니다.

Authorization 헤더가 없습니다. 모든 인증은 sch_session 쿠키를 통해 이루어지며, 브라우저가 자동으로 쿠키를 포함합니다.

엔드포인트 목록

메서드경로인증 필요설명
POST/api/auth/login/아니오SCH 로그인 + 세션 쿠키 발급
POST/api/auth/refresh/쿠키세션 만료시간 연장
POST/api/auth/logout/아니오쿠키 삭제
GET/api/auth/me/쿠키세션 복원용 사용자 정보
GET/api/auth/turnstile/아니오로그인 화면용 Turnstile 공개 설정

POST /api/auth/login/

SCH 통합정보시스템에 인증하고, 세션을 암호화하여 HttpOnly 쿠키로 발급합니다.

요청

Body (application/json)

{
  "std_no": "20201234",
  "password": "your_password",
  "turnstile_token": "0.abc123..."
}
필드타입필수설명
std_nostringO학번 (공백 자동 제거)
passwordstringO비밀번호
turnstile_tokenstring조건부Cloudflare Turnstile 위젯이 발급한 토큰. 서버에 Turnstile 키가 설정된 경우에만 필수

Turnstile 검증은 자격증명을 SCH 서버로 릴레이하기 전에 수행됩니다. 검증을 뒤로 미루면 봇이 던진 시도까지 학교 서버에 그대로 전달되기 때문입니다.

응답

200 OK

{
  "user": {
    "std_no": "20201234",
    "name": "홍길동"
  },
  "expires_at_ms": 1700007200000
}

동시에 아래 쿠키가 설정됩니다:

Set-Cookie: sch_session=<token>; Path=/; HttpOnly; SameSite=Lax; Max-Age=7200
응답 필드타입설명
user.std_nostring학번
user.namestring한국어 이름
expires_at_msnumber세션 만료 시각 (Unix milliseconds)

세션 TTL은 **7200초 (2시간)**이며, 쿠키는 HttpOnly이므로 JavaScript에서 읽을 수 없습니다.

에러 응답

에러 코드HTTP설명
MISSING_FIELDS400std_no 또는 password 누락 혹은 빈 값
CAPTCHA_FAILED403Turnstile 토큰이 없거나 위조·만료·재사용됨
INVALID_CREDENTIALS401학번 또는 비밀번호 오류
2FA_REQUIRED401이중인증(2FA) 계정 — 현재 미지원
AUTH_FAILED500기타 SCH 인증 오류
CAPTCHA_UNAVAILABLE503Cloudflare 검증 서버에 연결 실패 (TURNSTILE_FAIL_OPEN=true면 검증을 건너뛰고 로그인 진행)
SCH_UNAVAILABLE503SCH 서버 연결 실패
// 예: 학번/비밀번호 오류
{
  "error": "INVALID_CREDENTIALS",
  "message": "학번 또는 비밀번호가 올바르지 않습니다."
}

POST /api/auth/refresh/

현재 세션을 갱신하여 만료 시간을 현재 시각 기준 +2시간으로 연장합니다.

요청

Body 없음. 쿠키 sch_session이 포함되어야 합니다.

응답

200 OK

{
  "expires_at_ms": 1700014400000
}

갱신된 토큰으로 sch_session 쿠키가 덮어씌워집니다.

에러 응답

에러 코드HTTP설명
INVALID_TOKEN401쿠키 없음, 만료, 또는 복호화 실패

세션 쿠키는 발급 시점의 클라이언트 IP와 User-Agent에 바인딩됩니다. 다른 IP나 브라우저에서 재사용하면 복호화가 실패하여 INVALID_TOKEN이 반환됩니다.


POST /api/auth/logout/

sch_session 쿠키를 만료 처리합니다.

요청

Body 없음.

응답

200 OK

{
  "ok": true
}
Set-Cookie: sch_session=; Path=/; expires=Thu, 01 Jan 1970 00:00:00 GMT

쿠키가 없어도 200을 반환합니다. SCH 서버에 별도로 로그아웃 요청을 보내지 않습니다.


GET /api/auth/me/

현재 로그인된 사용자 정보와 세션 만료 시간을 반환합니다.
페이지 재방문 시 로컬 상태를 복원할 때 사용합니다.

요청

쿠키 sch_session 포함.

응답

200 OK

{
  "std_no": "20201234",
  "name": "홍길동",
  "expires_at_ms": 1700007200000
}
필드타입설명
std_nostring학번
namestring이름
expires_at_msnumber세션 만료 시각 (Unix ms)

에러 응답

에러 코드HTTP설명
UNAUTHORIZED401세션 없음 또는 만료

GET /api/auth/turnstile/

로그인 화면이 Turnstile 위젯을 그리는 데 필요한 공개 설정을 반환합니다. 인증이 필요 없습니다.

응답

200 OK

{
  "enabled": true,
  "site_key": "0x4AAAAAAA..."
}
필드타입설명
enabledboolean서버에 사이트키·시크릿키가 모두 설정됐는지. false면 로그인에 turnstile_token이 필요 없습니다
site_keystring위젯 렌더링용 공개 사이트키. enabled가 false면 빈 문자열

사이트키를 프론트엔드 빌드에 박지 않고 여기서 내려줍니다. 키 관리 지점을 서버 .env 한 곳으로 유지해, 빌드 재배포 없이 키를 교체할 수 있게 하기 위함입니다.

키가 없는 로컬 개발 환경에서는 enabled: false가 되어 Turnstile 없이 로그인이 그대로 동작합니다.


세션 토큰 내부 구조

[Binary Layout]
version(1 byte) || kid(1 byte) || nonce(12 bytes) || ciphertext+GCM_tag(N+16 bytes)

Base64URL 인코딩하여 쿠키에 저장

암호화 방식: AES-256-GCM
키 파생: HKDF-SHA256 (salt="sch-portal-v1", info="session-v1")
AAD (Additional Authenticated Data): HMAC-SHA256("ctx-binding", ip + "|" + user_agent)

AAD에 IP와 User-Agent가 바인딩되어 있어, 다른 환경에서 토큰을 재사용하면 GCM 인증이 실패합니다.

세션 페이로드 구조:

{
  "std_no":       "20201234",      # 학번
  "user_no":      "U000001234",    # SCH 내부 사용자 번호
  "name":         "홍길동",         # 이름
  "jsessionid":   "ABC123...",     # SCH 서버 세션 ID
  "ecd_key":      "ENCKEY...",     # TRANSACTION_ECDKEY
  "dept_cd":      "CS",            # 학과 코드 (없으면 "")
  "password":     "...",           # 웹메일 SSO 전용 (아래 참고)
  "mail_cookies": {},              # 웹메일 세션 쿠키 (최초엔 빈 dict)
  "expired":      1700007200       # 만료 Unix timestamp
}

이 페이로드는 절대 클라이언트에 노출되지 않으며, 서버에서만 복호화합니다.

페이로드에 비밀번호가 있는 이유

웹메일(sso.sch.ac.kr)은 종합정보시스템과 인증 체계가 달라 별도 로그인 체인이 필요한데, 매 요청 새로 발급되는 RSA 키로 자격증명을 암호화하는 방식이라 재사용 가능한 토큰이 없습니다. 체인이 왕복 3번이라 로그인 응답을 그만큼 늦추지 않으려고, 로그인 시점에 미리 태우지 않고 메일 페이지 첫 진입 때 세션 속 비밀번호로 SSO를 수립합니다.

비밀번호는 이 페이로드에만 존재합니다. AES-256-GCM으로 암호화되고 IP + User-Agent에 AAD 바인딩된 HttpOnly 쿠키 안이며, 서버 DB·로그에는 남지 않습니다.

자세한 흐름은 웹메일 API 문서를 참고하세요.

페이로드를 재암호화하는 곳

세션 값을 늘리면 아래 세 곳을 모두 확인해야 합니다.

위치시점
views/auth.py LoginView로그인
views/auth.py RefreshView세션 연장
views/mail.py _renew_session()메일 쿠키 갱신

mail.py는 expired를 그대로 두고 남은 시간만큼만 쿠키 수명을 줍니다. 여기서 TTL을 새로 주면 세션 연장이 아닌데도 연장돼 버립니다.

On this page