모바일 로컬 HTTP 로그인
기준일: 2026-09-05. SDK 0.1.10 발행·설치 검증, Auth/Console 63cf46f 배포와 JWT self-check가 완료됐다. 기존 등록 HTTPS origin의 CORS·challenge 생성과 미등록 LAN origin 거절을 확인했다. 실제 LAN callback 저장과 등록 후 검증, 소비 앱 설정 및 실기기 로그인은 아직 완료되지 않았다.
기본 권장은 HTTPS다. 같은 LAN의 휴대폰에서 개발 PC의 HTTP 앱에 접속하는 경로도 아래 제한 안에서 지원한다. 서버 구현은 배포됐지만 소비 앱의 정확한 callback 등록과 개발 옵션 설정이 필요하며, 실제 LAN 로그인 완료는 별도로 검증해야 한다. 이 문서의 새 옵션을 기존 설치 버전이 지원한다고 가정하지 않는다.
지원 조건
SHA-256 JavaScript 대체 경로는 다음 조건을 모두 만족할 때만 허용한다.
- SDK 설정의
development.allowInsecureHttp를 명시적으로true로 켠다. 생략하거나false이면 예외를 적용하지 않는다. - Auth
environment가test다. 프런트엔드 개발 모드와 Auth의test/live환경은 별도 설정이다. - 실제 브라우저 페이지가
http:이며, host가 RFC1918 private IPv4 또는 기존 loopback이다. 설정된 callback만 보고 판단하지 않는다. - RFC1918 범위는
10.0.0.0/8,172.16.0.0/12,192.168.0.0/16이다. 기존 loopback은localhost,127.0.0.1,[::1]만이며 휴대폰에서 개발 PC를 가리키는 주소는 아니다..localhost하위 도메인이나127.0.0.2를 새로 허용하지 않는다. redirectUri는 실제 페이지와 scheme, host, port가 같은 origin이어야 한다. 로그인 호출의 일회성options.redirectUri에도 같은 조건이 적용된다.- 브라우저의 안전한 난수 생성 기능이 있어야 한다. 난수 생성은
crypto.getRandomValues()를 유지하고 PKCE는 계속S256을 사용한다. Web Crypto digest를 사용할 수 없을 때 SHA-256 계산만 SDK의 JavaScript 구현으로 대체한다.
LAN HTTP 로그인은 native digest 유무와 관계없이 위 opt-in/test/local/same-origin 조건을 요구한다. native digest 함수가 존재하지만 실행 중 실패한 경우에는 JavaScript fallback으로 오류를 숨기지 않는다.
Math.random(), 고정 verifier, PKCE plain, state 검증 생략은 지원 방식이 아니다. 대체 SHA-256은 HTTP를 암호화하지 않으며, 일반 배포와 실사용은 HTTPS를 권장한다. HTTP public IP/일반 도메인, live, 다른 origin callback은 이 개발 예외에서 지원하지 않는다. .local 이름이나 사설 IP로 해석되는 도메인을 RFC1918 IPv4 literal과 동일하게 취급하지 않는다.
HTTPS와 기존 PC loopback 로그인은 이 개발 예외와 별도로 기존 동작을 유지해야 한다. 기존 localhost에서 native Web Crypto를 사용하는 로그인에 새 opt-in을 필수로 추가하지 않는다. HTTPS에서 digest를 쓸 수 없다는 이유만으로 HTTP용 대체 경로를 켜지 않는다. same-origin 조건은 새 insecure HTTP fallback에 한정하며, 기존 HTTPS의 별도 origin callback 지원과 검증 규칙은 유지한다.
브라우저 배경: crypto.subtle은 secure context 기능이지만 getRandomValues()는 insecure context에서도 제공될 수 있다. MDN Crypto, MDN getRandomValues.
주소 세 가지 구분
설정 · 예시 또는 기준 · 담당과 의미
앱 App/Web Callback · http://192.168.0.10:5174/auth/callback · 앱이 결과를 받는 전체 URL. Auth test의 앱 client에 정확히 등록하고 SDK redirectUri와 일치시킨다.
Auth API CORS origin · http://192.168.0.10:5174 · 브라우저가 https://auth.spectra.kr를 호출할 때의 scheme/host/port. 활성 Test App Client에 등록한 웹 callback에서 자동 반영한다.
Google/Apple provider callback · Console이 제공하는 기존 HTTPS broker callback · Google/Apple이 결과를 돌려주는 Platform/Keycloak 주소. 앱 LAN callback으로 바꾸지 않는다.
192.168.0.10과 포트 5174는 Modo 로컬 설명용 예시다. 실제 PC IP, 활성 포트, 등록 완료된 주소라는 뜻이 아니다. 기기의 브라우저 주소에 맞춰 치환한다.
iPhone의 로컬 앱 -> HTTPS Auth API challenge -> Google/Apple 로그인
-> 기존 HTTPS broker provider callback -> 등록한 앱 callback
-> relaySpectraAuthCallback() -> HTTPS Auth API exchange -> 앱 세션
Auth의 승인된 변경은 test에서 정확히 등록한 private IPv4 HTTP callback을 허용하고, 앱 client 설정을 Keycloak의 redirectUris와 webOrigins에 반영하는 것이다. wildcard LAN callback이나 모든 사설 origin을 허용하는 계약이 아니다. live와 provider 등록 callback 정책으로 이 예외를 확장하지 않는다.
Keycloak webOrigins와 Auth API CORS는 서로 다른 처리다. 배포된 Auth 구현은 등록 callback을 DB에서 조회해 Auth SDK 요청의 CORS에도 연결한다. AUTH_SDK_ALLOWED_ORIGINS 환경 변수를 자동 편집하는 방식은 아니며, 일반 Test LAN callback 추가에 별도 수동 CORS 등록을 요구하지 않는다. SDK flag만 켜서는 서버의 callback 등록이나 provider 설정이 바뀌지 않는다.
기존 App/Web Callback, CORS origin, Google/Apple broker callback은 보존한다. 필요한 LAN 항목을 추가하고, 기존 배열이나 환경 변수 전체를 새 예시 한 개로 덮어쓰지 않는다. 기존 PC 주소 예시는 다음과 같다.
http://localhost:5174/auth/callbackhttp://127.0.0.1:5174/auth/callback- 기존 CORS 기록:
https://console.spectra.kr,http://localhost:5173,http://localhost:5174,http://127.0.0.1:5173,http://127.0.0.1:5174
위 CORS 목록은 기존 문서의 2026-08-02 검증 기록이며 현재 운영 상태를 다시 조회한 결과는 아니다. 이번 문서 작업에서는 LAN origin을 운영에 추가하지 않았다.
Console Test 개발 설정
아래 Console UI는 배포됐으며 주 담당이 공개 화면에서 Test 환경, 기존 callback 목록과 LAN callback 초안 추가까지 확인했다. 초안 추가는 저장 완료가 아니다. 역할 20의 저장·취소 검증은 로컬 mock API 결과이며, 실제 운영 저장 상태는 릴리즈와 운영 확인을 본다.
1. Console에서 대상 Project의 Auth 설정을 연다. Auth 환경이 Test인지 먼저 확인한다. Secret Key 환경이나 앱의 HTTPS 주소로 판단하지 않는다.
2. 이미 Test인 App Client는 그대로 사용한다. 실제 Live client라면 기존 설정을 Test로 덮지 말고 별도 Test Project/client/provider 구성을 준비한다. 이번 모두캠프는 이미 Test이므로 이 분기가 필요 없다.
3. 상단 Test 개발 Callback 설정을 누르면 App/Web Callback URL 목록으로 이동한다. 현재 Auth 환경과 등록 origin의 자동 CORS 연동 안내를 확인한다. App Client가 active여야 적용된다.
4. 현재 전체 callback 목록을 확인하고 Callback URL 추가로 실제 개발 PC의 LAN callback을 추가한다. 예시 IP를 그대로 쓰지 않으며 기존 localhost/127.0.0.1/HTTPS와 내부 callback을 제거하지 않는다. 폰에서 앱과 callback이 모두 열리고 SDK redirectUri와 전체 URL이 일치해야 한다.
5. Callback URL 목록 저장을 누른다. 전체 목록이 저장되므로 기존 항목이 남아 있는지 확인하고, 상태 새로고침 후 App Client active와 저장 목록을 다시 확인한다. Auth service는 Test에서 활성화돼 있어야 한다. Google/Apple 로그인에는 각각 해당 Test provider의 active/valid 상태도 필요하다.
6. 저장 전에 변경을 버리려면 Callback 변경 취소를 누른다. 서버 API를 호출하지 않고 이전 목록으로 복원하므로 CORS 등록·삭제도 발생하지 않는다.
7. 개발 주소가 더 이상 필요하지 않으면 해당 항목만 제거한 전체 목록을 저장하고 새로고침한다. 같은 origin의 다른 callback이 남는지 확인한 뒤 OPTIONS와 실제 SDK 요청의 CORS 결과를 확인한다.
모두캠프는 확인 당시 사용자 화면의 callback 4개와 내부 callback 1개, 총 5개가 있었다. localhost literal은 없었으며 기존 로컬 값은 127.0.0.1이었다. 이 목록을 LAN 예시 하나로 대체하지 않는다. 다른 프로젝트는 자신의 현재 목록을 기준으로 보존한다. Test 개발을 위해 Secret Key 발급·재발급이나 Live Secret Key 교체는 필요 없다.
callback 등록과 CORS 반영
자동 허용 대상은 활성 Project의 현재 public client에 연결된 활성 Test App Client다. Auth service의 Test 활성화가 필요하며, disabled/deleted client는 제외한다. 등록 callback 중 허용되는 HTTP loopback/RFC1918 IPv4 또는 HTTPS URL의 정확한 origin만 사용한다. wildcard, 모든 사설 IP, 미등록 origin을 일괄 허용하지 않는다. Provider 준비 상태는 CORS 허용 근거가 아니며 실제 로그인에서 별도로 검증한다.
서버는 동적 origin을 캐시하지 않고 OPTIONS와 실제 SDK 응답에서 현재 등록 상태를 다시 조회한다. 저장이 반영되고 App Client가 active이면 다음 요청부터 대상이 되고, 마지막 허용 근거를 제거하면 다음 요청의 CORS 허용 헤더가 제거된다. 브라우저의 OPTIONS 결과는 최대 600초 캐시될 수 있지만 실제 응답에서도 재조회한다. 따라서 이전 preflight 성공을 삭제 후 접근 가능 증거로 쓰지 않는다. CORS 헤더 제거를 서버 세션 폐기나 요청 처리 취소로 해석하지 않는다.
허용 origin은 합집합이다. 같은 origin의 다른 활성 Test callback이 남거나 기존 static AUTH_SDK_ALLOWED_ORIGINS/loopback 허용 근거가 있으면 한 callback을 삭제해도 CORS는 계속 허용될 수 있다. DB 또는 Project 상태 확인이 실패하면 동적 근거로 허용하지 않는다. 기존 static/loopback 호환 동작은 독립적으로 유지하며 이번 기능으로 기존 live/localhost 설정을 덮지 않는다.
CORS preflight에는 Project나 environment 본문이 없으므로 이 합집합은 tenant별 권한 부여가 아니다. 실제 요청의 project/client/PKCE/session/token 검증은 유지된다. SDK용 CORS 자동 연동을 Console owner API의 CORS 확대, 미등록 redirectUri 허용 또는 provider callback 변경으로 해석하지 않는다.
SDK 0.1.10 설정
설정 형태는 development?: { allowInsecureHttp?: boolean }이다. @spectra-platform/auth-sdk@0.1.10은 GitHub Packages 발행과 새 캐시 설치 검증을 마쳤다. 소비 환경의 registry/read 권한을 준비하고 정확한 버전을 설치한다. 기존 버전에서 타입 오류가 나면 강제 형변환으로 숨기지 않는다.
npm install @spectra-platform/auth-sdk@0.1.10
먼저 Console에서 해당 Project의 Auth App Client 환경과 Google/Apple Provider 상태를 확인한다. 이미 test이고 활성화돼 있다면 기존 Test 구성을 그대로 사용한다. Secret Key의 환경이나 앱의 production HTTPS 주소만으로 Auth App Client가 live라고 판단하지 않는다. Auth App Client와 Secret Key의 환경은 별개다.
App Client가 실제로 live라면 SDK의 environment만 test로 바꿔 live publicClient/provider를 재사용하지 않는다. 현재 동일 public client ID에는 App Client 환경 하나만 등록할 수 있으므로 기존 Live 구성을 보존하고 별도 Test Project/client/provider를 준비하는 분기가 필요하다. 아래 예시의 projectId, publicClientId와 provider는 모두 확인된 Test 구성에 대응해야 한다. 개발 테스트를 위해 Secret Key를 재발급하거나 기존 live key를 교체할 필요는 없다.
2026-09-05 모두캠프 확인 사례에서는 Auth App Client가 이미 test active이고 Google/Apple 설정이 active/valid였다. 따라서 이번에는 환경 전환이나 새 Test Project가 필요 없다. 기존 127.0.0.1:5174와 production HTTPS의 root/callback 4개 및 내부 callback 1개를 보존하고 실제 LAN callback을 추가한다. 이 사례는 모든 소비 프로젝트의 기본 설정을 뜻하지 않으며, provider 설정의 valid 상태는 실제 모바일 로그인 E2E 완료와 구분한다. 내부 Console callback은 소비 앱 SDK의 redirectUri 예시로 복사하지 않는다.
import {
SpectraAuthClient,
createLocalStorageSessionStore,
} from "@spectra-platform/auth-sdk";
const auth = new SpectraAuthClient({
projectId: import.meta.env.VITE_SPECTRA_PROJECT_ID,
// Use the active App Client registered in the test environment.
publicClientId: import.meta.env.VITE_SPECTRA_PUBLIC_CLIENT_ID,
environment: "test",
redirectUri: new URL("/auth/callback", window.location.origin).href,
development: { allowInsecureHttp: import.meta.env.DEV },
sessionStore: createLocalStorageSessionStore(),
});
환경 변수 이름은 예시다. 소비 앱이 쓰는 기존 public 설정과 프런트엔드 local dev flag/env를 유지하고 연결한다. SDK는 소비 앱이 개발 빌드인지 자동 검증하지 않는다. SDK가 검사하는 것은 명시 opt-in, Auth test, 실제 local HTTP 및 fallback callback 조건이다. 예제의 import.meta.env.DEV가 Vite production build에서 옵션을 false로 만들며, 이 값이 Auth 환경을 자동으로 test로 바꾸지는 않는다. publicClientId는 현재 SDK 호환 필드다. Auth API 기본 endpoint는 SDK가 소유하는 https://auth.spectra.kr를 사용한다.
이 브라우저 로그인 경로에는 Secret Key가 필요 없다. Google client secret, Apple private key, Keycloak client secret도 앱 bundle이나 VITE_* 변수에 넣지 않는다.
소비 앱에 SDK를 감싼 adapter가 있다면 옵션이 실제 SpectraAuthClient 생성까지 전달되는지 확인한다. redirectUri를 window.location.origin으로 만드는 코드가 있어도 기존 환경 변수가 127.0.0.1 주소로 덮어쓰면 휴대폰의 LAN origin과 달라진다. 개발 설정에서 실제 callback과 일치하도록 선택하되 기존 PC·production 설정은 보존한다. 별도 타입 선언이나 테스트 stub이 있다면 0.1.10의 공개 타입과 맞추고, 타입 강제 변환으로 옵션 누락을 숨기지 않는다. 로컬 SDK 소스를 선택하는 앱은 설치된 패키지 버전과 실행 중인 소스 버전을 따로 확인한다.
로그인 버튼의 클릭 핸들러에서 signInWithGoogle() 또는 signInWithApple()을 바로 호출한다. 0.1.10은 SDK가 동기 preflight 후 popup을 확보하므로 Modo가 별도 선행 popup을 만들 필요가 없다. 클릭과 SDK 호출 사이에 불필요한 비동기 준비를 끼우지 않는다. 이번 범위는 모바일 popup 보강이며 full-page redirect API를 추가하지 않는다.
0.1.10의 추가 public API는 auth.preflightSignIn(options?: SpectraSignInOptions): void다. 네트워크 요청이나 popup 없이 동기 검사하며 실패 시 SpectraAuthError를 throw한다. 앱이 자체 popup 흐름을 유지하는 경우에만 popup을 만들기 전에 호출한다. 일반적인 SDK 직접 로그인에는 별도 호출이 필요 없다. preflight 성공은 CORS, provider, popup 허용 또는 전체 로그인 성공을 보장하지 않는다.
// Only for an app that retains its own popup flow.
auth.preflightSignIn({ redirectUri }); // Synchronous; throws on failure.
// Continue the app's existing popup flow only after this returns.
preflight에 준 일회성 callback과 실제 sign-in의 옵션을 동일하게 유지한다. 기존 popup handle을 SDK에 주입하는 새 API는 제공하지 않는다. 이 API와 popup 확보 순서는 발행한 동일 tarball의 설치·mock browser 검증을 통과했다. 실제 iPhone 검증은 별도다.
실제 /auth/callback 페이지는 직접 접근해도 렌더링되어야 하며 아래 helper를 실행한다. callback의 query를 읽기 전에 다른 route로 이동시키지 않는다.
import { relaySpectraAuthCallback } from "@spectra-platform/auth-sdk";
relaySpectraAuthCallback();
popup, opener, callback source/origin, state 검증은 SDK 흐름을 유지한다. 소비 앱에서 postMessage 대상 origin을 *로 바꾸거나 임의 exchange를 구현하지 않는다. 성공 후 getSession()과 getAccessToken()으로 실제 세션을 확인하되 token 원문을 기록하지 않는다.
공개 API의 반환 형태는 getSession(): SpectraAuthSession | null인 동기 snapshot과
getAccessToken(options?: SpectraGetAccessTokenOptions): Promise<string>인 비동기 token 조회로 구분한다.
세션이 없으면 getAccessToken()은 SESSION_UNAVAILABLE로 reject한다. 아래 코드는 로그인 완료 후 실행한다.
const session = auth.getSession();
if (session) {
const accessToken = await auth.getAccessToken();
console.info({ hasSession: true, hasAccessToken: Boolean(accessToken) });
}
Modo 로컬 Backend 연결
휴대폰에서 http://192.168.0.10:5174로 앱을 열어도 브라우저 코드의 http://localhost:<port>는 휴대폰 자신을 가리킨다. Google/Apple 로그인이 성공한 뒤 앱 Backend 요청만 실패한다면 Auth 로그인 실패와 분리한다.
프런트엔드 dev server를 LAN에서 접근 가능하게 실행하고 같은 네트워크에서 앱과 callback이 열리는지 먼저 확인한다. 0.0.0.0은 서버의 listen 설정이지 브라우저 주소나 callback 등록값이 아니다. 기존 프런트엔드 local dev flag/env를 유지하면서 앱 Backend 요청은 상대 경로로 보내고, 개발 PC의 dev proxy가 로컬 Backend로 전달하도록 구성한다.
iPhone 브라우저: http://192.168.0.10:5174/api/...
개발 PC dev proxy: /api/... -> http://127.0.0.1:<BACKEND_PORT>/api/...
Vite 설정 예시다. MODO_BACKEND_ORIGIN은 설명용 서버 측 환경 변수 이름이며 기존 앱의 변수명을 우선한다. Backend 포트와 path rewrite 필요 여부는 앱의 실제 계약으로 정한다.
import { defineConfig, loadEnv } from "vite";
export default defineConfig(({ mode }) => {
const env = loadEnv(mode, process.cwd(), "");
return {
server: {
host: "0.0.0.0",
port: 5174,
strictPort: true,
proxy: {
"/api": {
target: env.MODO_BACKEND_ORIGIN,
changeOrigin: true,
},
},
},
};
});
기존 Vite plugins와 설정을 보존하며 필요한 server 항목만 병합한다. MODO_BACKEND_ORIGIN은 dev server 실행 전에 실제 Backend 주소로 설정하고 브라우저에 노출하지 않는다. 이 proxy는 소비 앱 Backend를 위한 구성이다. Auth SDK의 공개 endpoint와 Auth callback/CORS 등록을 대신하지 않는다. Vite server options.
새 유료 서비스나 외부 tunnel은 이 개발 경로의 필수 조건이 아니다. LAN 연결과 기존 공개 HTTPS Auth/provider를 사용한다. 개발 PC의 IP나 포트가 바뀌면 실제 앱 callback을 추가·저장하고, 새 origin의 CORS 반영을 확인한다. 이전 등록값 제거는 별도 판단한다.
실기기 QA 체크리스트
아래 항목은 실행 계획이며 아직 통과 기록이 아니다. 실제 iPhone Safari와 iPhone Chrome에서 각각 Google/Apple을 확인한다. 각 기록에는 날짜, iOS/브라우저 버전, SDK 버전/commit, Auth 배포 commit, 앱 origin/callback, provider, 기대/실제 결과를 포함한다. credential 미준비는 실패와 구분해 blocked로 기록한다.
상태 · 시나리오 · 확인 기준
미검증 · test 설정 준비 · test App Client 및 해당 Google/Apple Provider 활성화 확인. live 설정과 key를 보존하고 환경 문자열 변경만으로 재사용하지 않음.
미검증 · LAN 앱 및 callback 직접 열기 · 예시가 아닌 실제 PC IP/포트로 둘 다 열림. callback route에서 앱 초기화 오류가 없음.
미검증 · Google completion, Safari/Chrome 각각 · 계정 선택에 그치지 않고 동의, broker, 앱 callback, exchange, 세션 저장까지 완료.
미검증 · Apple completion, Safari/Chrome 각각 · 준비된 provider로 같은 전체 경로 완료. credential 미준비 시 blocked와 담당을 기록.
미검증 · Google/Apple cancel · provider 취소 또는 popup 닫기를 각각 수행. 새 성공 세션이 생기지 않고 재시도 가능.
미검증 · popup blocked · 두 브라우저에서 차단 상황을 확인. 무한 로딩 없이 오류 처리 후 사용자 동작으로 재시도 가능.
미검증 · provider/network fail · Google/Apple 오류, 네트워크 단절, exchange 오류를 구분. 실패를 성공으로 표시하지 않음.
미검증 · callback/state · 정상 relay 성공. 잘못된 state, 다른 origin/source, 재사용 callback으로 세션이 생성되지 않음.
미검증 · callback 등록 불일치 · 미등록 URL, 다른 포트/path가 거절됨. 기존 callback 목록이 보존됨.
미검증 · Console 변경 취소 · 추가·삭제 후 저장 전 취소하면 API 호출 없이 기존 목록 복원. 기존 CORS 상태 유지.
미검증 · callback 등록·삭제 CORS · 등록 후 허용, 마지막 동적 origin 근거 삭제 후 불허. 동일 origin의 다른 callback/static/loopback 근거가 남는 경우와 구분.
미검증 · SDK HTTP 제한 · flag 생략/false, live, public HTTP, 다른 origin callback, 난수 기능 없음에서 대체 경로가 사용되지 않음.
미검증 · CORS · 실제 LAN origin에서 OPTIONS와 challenge/exchange/refresh/revoke 본요청을 함께 확인. service token 사용 시 해당 요청도 확인.
미검증 · 앱 Backend · 폰에서 localhost 직접 호출이 없고 same-origin dev proxy로 앱 API가 정상 동작.
미검증 · PC localhost 회귀 · 기존 localhost와 127.0.0.1 주소에서 Google/Apple 로그인 및 callback 흐름 유지.
미검증 · HTTPS 회귀 · 기존 HTTPS 앱에서 flag 없이 Google/Apple 로그인, 별도 origin callback 지원과 기존 callback/state 검증, 세션 갱신과 logout 유지.
미검증 · preflight 및 popup 순서 · 클릭에서 SDK 직접 호출 시 동기 preflight 후 popup 확보. 거절 시 popup/네트워크 없음. 앱 자체 popup을 유지하면 preflight를 먼저 실행.
브라우저 로그인 QA와 SDK 단위 검증을 구분한다. SDK 담당은 SHA-256 known-vector/native digest 대조, 안전난수 미지원 거절, private IPv4 경계, 동일 origin 및 callback override, PKCE S256 유지 결과를 제공한다. Auth 담당은 test exact callback 허용/미등록 거절, live/public HTTP 거절, Keycloak webOrigins 반영 결과를 제공한다. 문서 build 성공은 이 검증들을 대체하지 않는다.
실패 진단과 CS 전달
실패 시 SDK code, status, requestId가 있으면 기록하고, 소비 프로젝트명, 환경, SDK 버전, origin/callback, 브라우저, provider, 발생 단계, 재현 절차를 Platform CS에 전달한다. callback URL의 code/state query, verifier, token, provider credential 원문은 기록하지 않는다.
requestId가 없을 수 있다. SDK가 네트워크 호출 전 crypto/local preflight에서 거절했거나 popup이 차단된 경우 서버 요청 자체가 없을 수 있고, 브라우저 CORS preflight 실패로 본요청이 전송되지 않거나 응답을 읽지 못할 수도 있다. 이때는 requestId: 없음과 실패 단계, 시각, 민감값을 제거한 오류를 기록한다. 임의 ID를 만들거나 requestId가 없다는 이유만으로 서버 장애로 단정하지 않는다.
다음 코드는 발행된 0.1.10의 소스와 대조했다. 문자열 message 전체에 의존하지 않고 code로 분기한다.
SDK error code · 소비 앱 조치
INSECURE_HTTP_NOT_ALLOWED · HTTPS를 사용하거나 opt-in/test/실제 RFC1918 HTTP 조건을 확인한다. live 설정을 환경 문자열만 바꿔 재사용하지 않는다.
REDIRECT_URI_INVALID · 정확한 callback 전체 URL과 개발 HTTP의 origin·포트를 확인한다. userinfo/query/fragment/wildcard를 제거하고 Console에 등록된 URL과 맞춘다.
CRYPTO_UNAVAILABLE · 안전한 난수 기능과 native digest 지원을 확인한다. digest가 없으면 HTTPS 또는 승인된 fallback 조건을 충족한 환경을 사용한다. 난수 기능은 대체하지 않는다.
SIGN_IN_IN_PROGRESS · 기존 로그인이 완료되거나 취소될 때까지 중복 요청을 막는다. 반복 클릭마다 새 SDK 인스턴스를 만들어 우회하지 않는다.
POPUP_BLOCKED · 버튼 클릭에서 SDK 로그인 함수를 바로 호출하고 해당 사이트 popup 허용을 확인한다. SDK 호출 전에 비동기 작업을 기다리지 않는다.
POPUP_CLOSED · 사용자가 창을 닫은 상태를 성공 처리하지 않는다. 로딩을 종료하고 사용자 요청 시 다시 로그인한다.
SIGN_IN_CANCELLED · Provider callback의 access_denied로 사용자가 로그인을 취소했다. 로딩을 종료하고 사용자 요청 시 다시 로그인한다. 창 닫기와 구분한다.
SIGN_IN_FAILED · 그 밖의 provider 오류다. 성공으로 처리하지 않고 실패 단계를 기록한다. provider 오류 원문을 사용자 메시지나 로그에 그대로 노출하지 않는다.
SIGN_IN_TIMEOUT · 준비부터 세션 발급까지 제한 시간을 넘긴 상태다. 로딩을 종료하고 네트워크와 callback 흐름을 확인한 후 재시도한다.
SIGN_IN_TIMEOUT_INVALID · timeoutMs를 양수이며 2147483647 이하인 밀리초 값으로 수정한다.
SpectraAuthErrorDetails는 오류 생성자의 입력 타입이다. 소비 앱에서 읽는 위치는 error.status, error.requestId, error.retryAfterSeconds이며 error.details.status 또는 error.details.requestId가 아니다. 값이 없으면 undefined다.
import { isSpectraAuthError } from "@spectra-platform/auth-sdk";
try {
await auth.signInWithGoogle();
} catch (error) {
if (isSpectraAuthError(error)) {
console.warn({
code: error.code,
status: error.status,
requestId: error.requestId,
retryAfterSeconds: error.retryAfterSeconds,
});
}
}
이 예시는 오류 metadata만 읽는 방법이다. 실제 앱은 위 표의 조치와 로딩 종료를 연결하고, cause나 callback 전체 URL을 그대로 출력하지 않는다.
- Console 환경 불일치:
403 PUBLIC_CLIENT_MISMATCH이면 기존 client의 Auth 환경과 현재 Project/client 연결을 확인한다. Live에 같은 ID로 Test를 강제 등록하지 않는다. Live의 HTTP callback은 redirect 정책에서 먼저422로 거절될 수 있다. - OPTIONS 실패: 새 Auth 배포, 저장된 exact origin, Project/Auth Test 활성화와 App Client active 여부를 확인한다. Provider valid 상태나 SDK flag만으로 CORS가 허용되는 것은 아니다. 운영 배포 뒤에도 실패하면 시각·origin·안전한 metadata를 CS에 전달한다.
- provider redirect 오류: 기존 HTTPS broker callback과 provider 등록값 확인. LAN 앱 URL로 교체하지 않는다.
- callback 이후 세션 없음: callback route, opener, source/origin/state, exchange 응답 확인.
- 로그인 후 앱 API 실패: 폰 localhost URL과 dev proxy/Backend 상태 확인.
릴리즈와 운영 확인
항목 · 현재 상태 · 완료 증거
모바일 HTTP 개발 정책 · 사용자 승인 · 이 문서의 제한 및 공개 설정 계약
SDK public API 및 SHA-256 대체 구현 · 0.1.10 published / install-verified, 7a7e521 · development.allowInsecureHttp, 동기 preflightSignIn, popup 순서, 77 unit tests 및 mock browser smoke
Auth test callback 및 Keycloak/Console 반영 · 63cf46f 배포 완료, LAN Keycloak 왕복 미검증 · exact redirect 정책, adapter webOrigins 요청, 환경 변경 거절, callback 보존·저장·취소의 로컬 검증
SDK package publish · 0.1.10 발행 완료, 2026-09-05 11:14:21 UTC · registry latest, 새 캐시 설치, 공개 import·타입, 다운로드 tarball 일치 확인
Auth/Console 서버 배포 · 63cf46f 배포 완료, public-verified · Auth/Console deploy 성공, 양쪽 runtime revision 일치, Console HTTP200 및 새 UI asset 확인
JWT signer/verifier · public-verified · Inspect Auth API Smoke self_check_result=pass, current kid와 public key fingerprint, public JWKS200/current key 확인
Test callback 기반 동적 CORS · 기존 HTTPS 및 미등록 LAN 거절 public-verified, LAN 등록 후 검증 대기 · HTTPS 5 OPTIONS204·Google/Apple challenge201, 미등록 LAN 5 OPTIONS404·actual422/ACAO 없음, owner401. 삭제 후 actual 회수는 로컬 검증
실제 LAN callback 등록 · 공개 UI에 /auth/callback 1개 초안 추가, 저장 확인 대기·PUT0 · 기존 표시4개+내부1개 보존. 저장·active 확인 후 OPTIONS와 본요청 검증 필요. static 환경 변수 편집은 불필요
실제 iPhone 및 PC/HTTPS 회귀 · 미검증 · 위 QA 시나리오별 기록
소비 앱 adapter와 실행 설정 · 별도 적용 필요 · 실제 실행 SDK, HTTP 옵션 전달, LAN callback, Backend 연결과 타입 확인
Auth callback 정책의 선행 커밋은 76f370351aebb5f8b577d856a343e7c872ad3417 (Allow exact RFC1918 HTTP callbacks for test App Clients)이며, 동적 CORS와 최종 Console 변경은 63cf46f (Allow Auth SDK CORS from registered active Test callbacks)다. 새 환경 변수나 DB migration은 없다. App Client 환경은 변경할 수 없으며, Project의 current client와 실제 Test 활성화 조건이 맞아야 한다.
역할 20 최종 결과는 Auth source 501 passed/1 provider smoke skipped, Console 105 passed, contracts 60 passed, workspace lint/typecheck/build 및 image packaging 검사 통과다. PostgreSQL/HTTP 검증은 실제 Project inspection, callback 저장·삭제·disable, Project archive/current client 교체, Auth 환경 변경, DB 장애, static/loopback 보존과 실제 challenge service의 Test 201·client mismatch·Live HTTP 422를 포함한다. 검증 집계는 중복될 수 있으므로 합산하지 않는다. 확대 검증에서는 기존 architecture 검사 실패 1건이 남았으며 선행 76f3703에서도 같은 import 위반 3항목을 재현했다. 전체 검증을 모두 통과했다고 표기하지 않는다.
Owner session과 provider readiness는 fixture이며 Keycloak 반영은 adapter 요청 검증이다. Chrome Test/Live desktop/mobile viewport의 mock API 검증은 숨긴 내부 callback 보존, 추가·삭제 후 취소 PUT 0건, 저장 환경 고정과 reload 보존을 확인했다. 실제 iPhone Safari/Chrome, 실제 Keycloak worker/provider 왕복 또는 운영 JWT 검증을 뜻하지 않는다.
SDK 완료 커밋은 7a7e521aba34e08d7c23a4c46abe64246e7d0546 (fix(auth): support opt-in test LAN HTTP popup sign-in)이며 릴리즈 tag는 auth-sdk-v0.1.10이다. GitHub Packages 0.1.10은 2026-09-05 11:14:21 UTC에 수동 발행됐다. 이번 발행의 Actions run은 없다. 77 unit tests, typecheck/build, 실제 pack 및 독립 설치가 통과했고 registry에서 새 캐시로 설치한 public API·타입과 tarball 바이트 일치를 확인했다. 설치 tarball 기반 native subtle이 없는 LAN HTTP Chrome desktop/Pixel 7 에뮬레이션에서 Google/Apple 모의 로그인 4회, opt-in/live 제한, popup activation과 취소 처리가 통과했다. 기존 HTTPS 별도 origin callback과 native localhost 회귀는 unit 검증으로 확인했다. 모의 browser smoke와 실제 iPhone/provider E2E를 구분한다.
역할 70의 2026-09-05 모두캠프 읽기 전용 확인에서는 실행 중인 local SDK dist가 0.1.10 발행 build와 같았지만, 앱 adapter의 HTTP 옵션 전달과 LAN callback 선택은 아직 적용되지 않았다. 앱 dependency/lock/installed package는 0.1.6이었다. 이는 확인 당시 소비 앱 상태이며 이 문서 작업은 앱을 변경하지 않았다. 서버 배포와 별도로 소비 프로젝트 담당이 adapter·환경·설치 버전을 정렬해야 한다.
역할 80이 Auth 배포,
JWT 검사의 성공을 확인했다.
Auth와 Console runtime revision은 63cf46fd46d0b3b411e706a40c17c0d96e973a35와 일치한다.
JWT self-check와 public JWKS의 current key가 확인됐으며 기존 static origin 목록과 SMTP 설정은 유지됐다.
기존 등록 HTTPS에서 Google/Apple challenge201은 challenge 생성 성공이며 실제 provider 로그인 완료가 아니다.
실제 Keycloak LAN authorize/callback/exchange, Google/Apple 완료·취소와 iPhone 검증은 남아 있다.
이 문서와 Auth JS SDK, Agent Auth spec는 위 SDK/Auth 소스와 확인된 운영 결과에 정렬했다. 다음 단계는 주 담당의 LAN callback 저장, 역할80의 등록 후 CORS 검증, 소비 앱 담당의 설정 정렬과 실제 모바일/PC/HTTPS QA다. 공개 문서는 역할80이 최종 커밋의 정적 산출물을 Caddy에 동기화한다. 운영 적용과 consumer 검증은 각각 별도 증거로 기록한다. 소비 앱 수정은 이 문서 작업에서 수행하지 않는다.