Skip to content

Troubleshooting

OAuth 연동에서 가장 흔한 실패 패턴은 환경변수 누락입니다. 코드는 그대로지만 prod 빌드에 env가 안 주입돼서 IdP URL이 localhost로 떨어지는 케이스가 압도적으로 많습니다. 이 페이지는 그 1순위부터 점검합니다.

RP 측 필수 환경변수 체크리스트

OAuth 클라이언트(=RP, Relying Party) 앱이 logi에 붙으려면 다음 4개가 prod에 모두 주입돼야 합니다.

변수값 예시빠지면
LOGI_API_URLhttps://api.1pass.devlocalhost:3000으로 fallback → 연결 실패
LOGI_CLIENT_IDlogi_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxclient_id missing 400
LOGI_CLIENT_SECRETlogi_secret_…/oauth/token 단계 401
LOGI_SCOPES (선택)openid profile:basic email코드 기본값 사용 (보통 OK)

⚠️ 변수명은 프로젝트 컨벤션을 따르세요

이 문서는 LOGI_* 네이밍을 권장합니다. 다른 컨벤션(ONE_PASS_*, IDP_* 등)을 쓰는 코드베이스라면 4개 모두 그 prefix로 통일하세요. 하나라도 prefix가 다르면 코드가 ENV.fetch에서 못 찾고 default fallback 됩니다.

자가 점검: 1분 안에 원인 찾기

bash
# 1. RP에서 logi 서버 도달성 확인
curl -I "$LOGI_API_URL/.well-known/openid-configuration"
# → 200 OK 가 떠야 함. 안 뜨면 LOGI_API_URL 잘못됨.

# 2. client_id 가 logi에 등록돼 있는지 확인 (CLI)
logi apps verify $LOGI_CLIENT_ID --redirect-uri "$YOUR_CALLBACK_URL"

# 3. 직접 authorize URL 만들어서 브라우저로 열어보기
echo "$LOGI_API_URL/oauth/authorize?response_type=code&client_id=$LOGI_CLIENT_ID&redirect_uri=$YOUR_CALLBACK_URL&scope=openid&state=test&code_challenge=test&code_challenge_method=plain"
# → consent 화면이 뜨면 RP→IdP 경로 정상.
# → "Invalid redirect_uri" 가 뜨면 logi DB에 redirect_uri 미등록 또는 sandbox tier.

흔한 실패 시나리오

❌ 시나리오 1: 로컬은 되는데 prod에서 "리다이렉트가 안 됨" / 빈 화면

증상: 로그인 버튼 클릭 → 잠시 로딩 → ERR_CONNECTION_REFUSED 또는 빈 페이지.

원인: LOGI_API_URL 환경변수가 prod에 안 주입됨. 코드 fallback이 http://localhost:3000이라 브라우저가 사용자 PC의 3000번 포트로 요청.

확인:

bash
# Render 예시
curl -s "https://api.render.com/v1/services/$SERVICE_ID/env-vars" \
  -H "Authorization: Bearer $RENDER_API_KEY" | jq '.[] | select(.envVar.key | startswith("LOGI"))'

수정: 배포 플랫폼(Vercel/Render/Fly/Railway/...)의 env 설정에 LOGI_API_URL=https://api.1pass.dev 추가 → redeploy.


❌ 시나리오 2: Invalid redirect_uri 에러

증상: logi authorize 페이지 진입은 되는데 error=invalid_redirect_uri 또는 빨간 에러 화면.

원인 A — 등록 안 됨: prod callback URL이 logi DB에 등록 안 됨. 원인 B — sandbox tier: 앱 tier가 sandboxlocalhost/*.staging.*/*.test.*/*.localhost 만 허용. onrender.com/vercel.app 같은 prod 도메인은 차단. 원인 C — 🌟 커스텀 도메인 누락 (가장 흔함): 배포 플랫폼 default URL (*.onrender.com / *.vercel.app / *.fly.dev) 만 등록하고 실제 prod 도메인 (www.example.com) 을 빠뜨린 경우. RP 코드는 자기 도메인으로 콜백을 만들어 보내는데, logi DB 에는 onrender URL 만 있어서 redirect_uri not registered 거부. redirect_uri 매칭은 scheme + host + path 가 정확히 일치 해야 함 — substring/wildcard 매칭 없음.

확인:

bash
logi apps show $CLIENT_ID
# tier: sandbox  ← 이거면 production 으로 승급 필요
# redirect_uris: [...]  ← prod URL 들어있는지 확인

수정:

bash
# redirect_uri 추가 (변종 도메인 모두 등록 권장)
logi apps add-redirect $CLIENT_ID "https://www.example.com/auth/callback"
logi apps add-redirect $CLIENT_ID "https://example.com/auth/callback"        # apex 도 등록
logi apps add-redirect $CLIENT_ID "https://yourapp.onrender.com/auth/callback"  # 백업 (debug 용)

# tier 승급 (개발자 포털 → Submit for review → 승인)
# 자세한 절차: https://docs.1pass.dev/guide/security#promoting-to-production

🌟 권장: 커스텀 도메인 + 플랫폼 URL 모두 등록

배포 플랫폼 default URL (*.onrender.com 등) 도 함께 등록해두면:

  • 커스텀 도메인 DNS/SSL 사고 시 backup 경로
  • staging/preview 배포 디버깅
  • 도메인 마이그레이션 시 cutover 윈도우 확보

redirect_uris 는 배열이라 여러 개 등록 가능합니다 (개수 제한은 plan 기준 — free 5개, pro 20개).

왜 sandbox tier 가 prod 도메인을 차단하나요?

개발 단계 앱이 prod 사용자 데이터에 접근하지 못하도록 막는 안전장치입니다 (Stripe pk_test_… 와 동일 사상). 실수로 test 키로 prod 트래픽을 받는 사고를 원천 차단합니다. 자세한 보안 모델: Security · Sandbox vs Production.


❌ 시나리오 3: Callback 까지 오는데 /oauth/token 에서 401

증상: code 까지 받았는데 token 교환에서 invalid_client 401.

원인 후보:

  1. LOGI_CLIENT_SECRET prod env 미주입 → fallback 또는 빈 값
  2. client_secret 회전됐는데 RP env 업데이트 누락
  3. redirect_uri mismatch — authorize 때와 token 때의 redirect_uri 가 정확히 일치해야 함 (스키마/포트/trailing slash 포함)

수정:

bash
# secret 회전 후 새 값 주입
logi apps rotate-secret $CLIENT_ID
# → 출력된 client_secret 을 RP env 에 즉시 복사

❌ 시나리오 4: Mac/iOS 데스크톱에서 SSO 버튼 클릭 시 native 앱 안 열림 (브라우저로만 열림)

증상: RP 로그인 페이지에서 "logi 로 로그인" 클릭 → Safari/Chrome에서 api.1pass.dev/session/new 가 그대로 보임. logi Mac 앱이 설치돼있는데도 안 열림.

원인: RP가 server-side 302 redirect로 OAuth flow를 시작하는 패턴. macOS Universal Link는 사용자가 직접 클릭한 링크의 도메인 만 AASA 매칭하고, redirect chain은 user-gesture를 끊어서 무시합니다. iOS 14+ 는 redirect 후에도 매칭하지만 macOS는 더 엄격.

해결: RP 로그인 페이지 렌더 시점에 server에서 api.1pass.dev/oauth/authorize?… URL을 미리 생성하여 <a href> 에 직접 박아둡니다. 클릭이 logi 도메인을 향해 가니까 macOS가 AASA 매칭 → 네이티브 앱 open.

ruby
# Rails 예시 — Web::SessionsController#new
def new
  state    = SecureRandom.urlsafe_base64(32)
  verifier, challenge = OnePassSsoService.generate_pkce
  session[:one_pass_state]         = state
  session[:one_pass_code_verifier] = verifier
  render inertia: "Auth/Login", props: {
    one_pass_authorize_url: OnePassSsoService.authorize_url(
      redirect_uri:   auth_1pass_callback_url,
      state:          state,
      code_challenge: challenge
    )
  }
end
svelte
<a href={one_pass_authorize_url} data-turbo="false" rel="external">logi 로 로그인</a>

PKCE verifier/state 는 server session에 그대로 보존 → 보안 영향 없음. callback flow 는 기존과 동일.

⚠️ 주소창에 URL을 paste해서 enter는 macOS Universal Link를 트리거하지 않습니다 (클릭 링크만 트리거). 검증할 때 반드시 실제 <a> 태그 클릭으로 테스트.


❌ 시나리오 5: native 앱은 열리는데 동의 화면에 "불러오기 실패"

증상: Universal Link로 logi 앱이 정상 open → 동의 sheet 표시 → "불러오기 실패" / 빈 화면 / 즉시 dismiss.

원인: Cold start race condition. iOS/Mac 앱이 Universal Link 받으면 OAuthConsentView가 즉시 /api/v1/oauth/authorize/preview 를 호출하는데, SessionStore.bootstrap()이 Keychain에서 PAK를 꺼내 APIClient 에 주입하기 전에 호출이 나가버려서 401 unauthenticated 반환 → 동의 화면이 "불러오기 실패" 상태로 멈춤.

해결: 동의 sheet 표시를 session.state == .signedIn 이후로 지연 (LogiApp.swift / LogiMacApp.swift):

swift
private var oauthSheetBinding: Binding<IdentifiableRequest?> {
  Binding(
    get: {
      guard case .signedIn = session.state else { return nil }   // ← race 차단
      return IncomingOAuthRouter.shared.pending.map(IdentifiableRequest.init)
    },
    set: { value in
      if value == nil { IncomingOAuthRouter.shared.clear() }
    }
  )
}

이렇게 하면 cold start 시: Universal Link 수신 → pending 보관 → bootstrap() 완료 → signedIn 전환 → 그 시점에 sheet 표시 (PAK 주입 끝난 상태).


❌ 시나리오 6: Localhost 에서도 안 됨

원인: logi 서버 미실행 또는 다른 포트.

확인:

bash
curl http://localhost:3000/up  # logi 서버 헬스체크
# OK → logi 동작 중
# Connection refused → logi 서버 안 돌아감

수정: logi 디렉토리에서 bin/rails server 또는 cd server && bin/dev.


증상: TestFlight 로 logi 앱 설치 + 한 번 실행 + Safari 에서 RP 사이트 → 1pass 버튼 탭 → logi 앱이 안 열리고 api.1pass.dev/session/new 가 그대로 보임. 시나리오 4 (server-side 302) 이슈는 이미 해결됐고 RP 가 <a href="api.1pass.dev/oauth/authorize?..."> 로 직접 박는데도 안 됨.

원인: entitlement 에 applinks:api.1pass.dev?mode=developer 가 들어간 채 Apple Distribution profile 로 빌드/제출됨. Apple 공식 문서 + WWDC19 session 717:

"Apps signed for distribution on the App Store or TestFlight or Mac apps that have been signed and notarized cannot be used with this alternate mode."

즉 Distribution-signed 빌드에서 ?mode=developer 토큰은 silently ignored. 그 결과:

  • AASA association 이 OS 측에 등록조차 안 됨
  • swcd 가 해당 도메인을 모르는 상태
  • Universal Link 매칭 실패 → 브라우저로 fallback

build 23 사고의 정확한 증상이 이것 (2026-04).

확인:

bash
# (macOS 디바이스에서) 앱이 설치된 상태로
sudo swcutil show | grep -A 8 "1pass.dev"
# 정상이면: Status: approved/approved
# 비정상이면: 도메인 자체가 안 보이거나 "denied" 상태

수정:

  1. ios/Sources/logi.entitlementsios/project.yml 의 entitlement 를 plain 으로:
    diff
    - applinks:api.1pass.dev?mode=developer
    + applinks:api.1pass.dev
  2. mac 도 동일하게 (mac/Sources/LogiMac.entitlements + mac/project.yml)
  3. 빌드번호 +1 (23 → 24+) 후 새 TestFlight 업로드:
    bash
    cd ios && make testflight
    cd mac && make testflight
  4. 새 빌드 설치 후 앱 한 번 실행 → Safari 에서 RP 사이트 → 1pass 버튼 탭 검증

?mode=developer 가 정말 필요한 시점은 로컬 Xcode debug 빌드뿐. make dev-mode-on / make dev-mode-off 토글로 관리하고 절대 commit 금지. 자세한 내용은 RELEASE_CHECKLIST.md 참고.

🚫 검증 시 주의: 주소창에 https://api.1pass.dev/oauth/authorize?... 직접 paste → 동일 도메인 출처라 Universal Link 트리거 자체가 무시됨 (Apple 의도 동작). 반드시 다른 도메인의 사이트에서 <a> 클릭으로 검증.

🚫 Chrome / Firefox / Brave / Safari 시크릿 모드는 Universal Link 트리거가 일관되지 않거나 아예 안 됨. Safari 정상 모드 에서만 검증.


배포 플랫폼별 env 설정 가이드

Render

bash
# 단일 변수 추가/수정 (collection PUT 은 절대 사용 금지 — 전체 교체됨)
curl -X PUT "https://api.render.com/v1/services/$SERVICE_ID/env-vars/LOGI_API_URL" \
  -H "Authorization: Bearer $RENDER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"value": "https://api.1pass.dev"}'

# 변경 후 redeploy
curl -X POST "https://api.render.com/v1/services/$SERVICE_ID/deploys" \
  -H "Authorization: Bearer $RENDER_API_KEY" \
  -d '{"clearCache": "do_not_clear"}'

Vercel

bash
vercel env add LOGI_API_URL production
# → 프롬프트에서 https://api.1pass.dev 입력
vercel --prod  # redeploy

Fly.io

bash
fly secrets set LOGI_API_URL=https://api.1pass.dev
# 자동 redeploy

디버그 모드

RP 코드에서 IdP 응답을 자세히 보고 싶을 때:

ruby
# Rails 예시
Rails.logger.info "[logi] authorize_url=#{url}"
Rails.logger.info "[logi] token_response=#{tokens.except('access_token', 'refresh_token').inspect}"

logi 서버 측 로그는 logi token inspect <access_token> 으로 디코드해서 확인 가능합니다.


그래도 안 되면

  • GitHub Issues — 재현 가능한 최소 예제 첨부
  • 로그에 client_id, redirect_uri, HTTP status code 만 포함하면 90%는 빠르게 진단됩니다 (access_token/secret 은 절대 첨부 금지).

MIT License · Identity가 제품의 신뢰를 만듭니다.