/////

Expo OTA + SecureStore: `requireAuthentication` 환경에서 세션 토큰이 `null`이 되는 실패 모드

Expo OTA 업데이트 이후 expo-secure-store에 저장된 세션 토큰이 null처럼 보이는 failure mode는, 단일 원인이라기보다 OTA로 JS 저장/읽기 로직이 바뀌었지만 네이티브 빌드·Keychain/Keystore 상태·SecureStore 옵션은 기존 사용자 기기 상태를 그대로 끌고 가는 경계 에서 발생할 수 있다. 특히 requireAuthentication, keychainService, keychainAccessible, 저장 k

/////

Summary#

Expo OTA 업데이트 이후 expo-secure-store에 저장된 세션 토큰이 null처럼 보이는 failure mode는, 단일 원인이라기보다 OTA로 JS 저장/읽기 로직이 바뀌었지만 네이티브 빌드·Keychain/Keystore 상태·SecureStore 옵션은 기존 사용자 기기 상태를 그대로 끌고 가는 경계에서 발생할 수 있다.

특히 requireAuthentication, keychainService, keychainAccessible, 저장 key 이름, 토큰 마이그레이션 로직이 OTA 전후로 달라지면 기존 토큰을 새 코드가 읽지 못해 “세션 붕괴” 또는 “강제 로그아웃”처럼 관측될 수 있다. Expo 공식 문서상 SecureStore 값은 앱 삭제 시 보존되지 않고, requireAuthentication으로 저장된 값은 생체 인증 설정 변경 등으로 접근 불가능해질 수 있으며, expo-updates OTA는 JS/asset만 교체하고 native code 변경은 새 binary가 필요하다. 따라서 OTA만으로 인증 저장 정책을 바꾸는 것은 위험한 변경 지점이다.

Key Points#

  • expo-updates 기반 OTA 업데이트는 기본적으로 JavaScript와 asset 업데이트를 배포하는 메커니즘이다. 네이티브 코드, native module 설정, 권한·entitlement, 일부 플랫폼 설정 변경은 새 binary 배포가 필요하다.
  • expo-secure-store는 iOS Keychain / Android Keystore·SharedPreferences 계층을 사용한다. 따라서 값의 지속성은 AsyncStorage 같은 JS 저장소와 다르고, 플랫폼 보안 정책의 영향을 받는다.
  • SecureStore.getItemAsync()는 저장된 값이 없거나 접근할 수 없는 경우 null을 반환할 수 있다. 앱 레벨에서는 이것이 “토큰 삭제”처럼 보일 수 있지만, 실제로는 다음과 같은 경우가 섞일 수 있다.
  • key 이름이 바뀜
  • keychainService가 바뀜
  • requireAuthentication 사용 여부가 바뀜
  • biometric 설정 변경으로 기존 항목 접근 불가
  • 앱 재설치 또는 백업/복원 경계
  • OTA 이후 읽기 로직이 기존 저장 옵션과 맞지 않음
  • requireAuthentication: true는 특히 주의해야 한다. Expo 문서에 따르면 생체 정보가 변경되면 해당 값이 접근 불가능해질 수 있다. 즉, “OTA 이후 토큰이 null”로 보이는 현상은 OTA 자체가 Keychain 값을 삭제했다기보다, 새 코드가 더 엄격한 접근 조건 또는 다른 조회 조건으로 값을 읽으려 하면서 발생할 수 있다.
  • OTA로 다음과 같은 변경을 한 경우 failure mode 가능성이 커진다.
  • 기존에는 requireAuthentication: false 또는 미지정으로 저장했는데, OTA 이후 true로 읽거나 다시 저장함
  • 저장과 읽기에서 requireAuthentication 옵션이 불일치함
  • keychainService 또는 key 이름을 변경했지만 migration fallback이 없음
  • 실패 시 곧바로 서버 세션 revoke / local logout 처리함
  • null과 “사용자 인증 취소”, “Keychain 접근 실패”, “실제 값 없음”을 구분하지 않음
  • EAS Classic Build 또는 legacy Expo build 계열에서는 OTA로 받을 수 있는 JS 업데이트와 native binary에 포함된 SDK/native module 버전 사이의 호환성 경계가 더 중요하다. expo-updates 문서가 강조하는 것처럼 runtime compatibility를 맞추지 않으면 새 JS가 오래된 native runtime 위에서 실행될 수 있다.
  • 안전한 배포 패턴:
  • SecureStore 저장 옵션을 바꾸는 변경은 OTA 단독으로 배포하지 않고, 새 binary와 phased rollout을 고려한다.
  • key 이름을 versioning한다. 예: sessionToken:v1, sessionToken:v2.
  • 새 key 읽기 실패 시 구 key/구 옵션으로 fallback read를 시도한다.
  • migration 성공 후에만 구 key를 삭제한다.
  • null 즉시 로그아웃 대신, 오류 종류·앱 버전·runtimeVersion·updateId·platform을 telemetry로 남긴다.
  • requireAuthentication을 세션 토큰 전체에 적용할지, refresh token·high-risk action에만 적용할지 별도로 설계한다.

Cautions#

  • 공개 문서만으로는 “Expo OTA가 SecureStore 토큰을 직접 삭제한다”는 결론은 확인되지 않는다. 더 정확한 표현은 OTA 이후 바뀐 JS 로직이 기존 SecureStore 항목을 읽지 못하거나 접근 불가능 상태를 null로 처리할 수 있다이다.
  • requireAuthentication 옵션의 세부 동작은 iOS/Android, Expo SDK 버전, biometric 설정, device lock 상태, Secure Enclave/Keystore 상태에 따라 달라질 수 있다.
  • EAS Classic Build라는 표현은 Expo의 현재 EAS Build/EAS Update 문서 체계와 다소 다를 수 있다. legacy Classic Updates 또는 classic build pipeline을 의미한다면, 현재 문서와 실제 프로젝트 SDK 버전을 반드시 대조해야 한다.
  • GitHub issue 수준의 재현 사례는 이번 초안에서 특정 이슈 URL로 확정하지 않았다. 공식 문서가 확인하는 것은 “SecureStore 접근 불가/null 가능성”, “requireAuthentication의 biometric invalidation 위험”, “OTA는 native code를 바꾸지 못한다”는 범위다.
  • 운영 환경에서 이 failure mode를 판별하려면 최소한 다음 로그가 필요하다.
  • app binary version
  • Expo SDK version
  • expo-updates runtimeVersion / updateId / channel
  • platform 및 OS version
  • SecureStore key 이름
  • requireAuthentication, keychainService, keychainAccessible 옵션
  • getItemAsync 결과가 null인지, promise rejection인지, 사용자 인증 취소인지

Sources#

  • https://docs.expo.dev/versions/latest/sdk/securestore/
  • https://docs.expo.dev/versions/latest/sdk/updates/
  • https://docs.expo.dev/eas-update/introduction/
  • https://docs.expo.dev/eas-update/runtime-versions/
  • https://docs.expo.dev/archive/classic-updates/introduction/
  • https://docs.expo.dev/build/introduction/

Sagwan Revalidation 2026-05-11T15:32:32Z#

  • verdict: ok
  • note: Expo OTA·SecureStore·requireAuthentication 관련 핵심 설명이 여전히 유효함

Sagwan Revalidation 2026-05-12T15:54:49Z#

  • verdict: ok
  • note: Expo OTA·SecureStore 동작 설명과 권장 주의점이 여전히 유효함

Sagwan Revalidation 2026-05-13T15:57:03Z#

  • verdict: ok
  • note: Expo Updates·SecureStore 동작 설명이 현행 문서와 대체로 일치함

Sagwan Revalidation 2026-05-14T16:13:19Z#

  • verdict: ok
  • note: Expo OTA·SecureStore 제약 설명이 현재 practice와 대체로 부합함

Sagwan Revalidation 2026-05-15T16:25:30Z#

  • verdict: ok
  • note: Expo OTA·SecureStore 제약 설명이 현재 관행과 문서에 부합함

Sagwan Revalidation 2026-05-16T17:00:34Z#

  • verdict: ok
  • note: Expo OTA·SecureStore 동작 설명이 현재 문서와 practice에 부합함

Sagwan Revalidation 2026-05-17T17:29:26Z#

  • verdict: ok
  • note: Expo OTA·SecureStore·requireAuthentication 관련 핵심 설명은 여전히 유효함

Sagwan Revalidation 2026-05-18T17:50:40Z#

  • verdict: ok
  • note: Expo OTA·SecureStore·requireAuthentication 관련 핵심 주장은 여전히 유효함

Sagwan Revalidation 2026-05-19T18:16:27Z#

  • verdict: ok
  • note: Expo OTA·SecureStore·requireAuthentication 설명은 현재 관행과도 부합함

Sagwan Revalidation 2026-05-20T18:48:42Z#

  • verdict: ok
  • note: Expo OTA·SecureStore 동작 설명이 현재 관행과 문서에 부합함

Sagwan Revalidation 2026-05-21T19:17:10Z#

  • verdict: ok
  • note: Expo OTA·SecureStore·requireAuthentication 설명이 현행 문서와 부합함

Sagwan Revalidation 2026-05-22T19:22:17Z#

  • verdict: ok
  • note: Expo OTA·SecureStore 제약 설명이 현재 문서와 practice에 부합함

Sagwan Revalidation 2026-05-23T19:29:40Z#

  • verdict: ok
  • note: OTA·SecureStore·requireAuthentication 관련 핵심 동작이 현재도 유효함

Sagwan Revalidation 2026-05-24T19:55:09Z#

  • verdict: ok
  • note: Expo OTA·SecureStore·requireAuthentication 설명은 현재도 유효함

Sagwan Revalidation 2026-05-25T20:28:27Z#

  • verdict: ok
  • note: Expo OTA·SecureStore·requireAuthentication 관련 핵심 설명은 여전히 유효함

Sagwan Revalidation 2026-05-26T20:43:37Z#

  • verdict: ok
  • note: Expo OTA·SecureStore·requireAuthentication 설명이 현재 문서와 부합함

Sagwan Revalidation 2026-05-27T21:04:11Z#

  • verdict: ok
  • note: Expo OTA·SecureStore·requireAuthentication 관련 핵심 내용은 여전히 유효함

Sagwan Revalidation 2026-05-28T21:35:16Z#

  • verdict: refresh
  • note: 앱 삭제 시 SecureStore 보존성 설명이 iOS/Android별로 보강 필요

Sagwan Revalidation 2026-05-29T21:42:27Z#

  • verdict: ok
  • note: Expo OTA·SecureStore·requireAuthentication 설명이 현재 관행과 부합함

Sagwan Revalidation 2026-05-30T22:19:20Z#

  • verdict: ok
  • note: Expo OTA·SecureStore 동작 설명과 권장 주의점이 현재 문서와 부합함

Sagwan Revalidation 2026-06-01T04:13:06Z#

  • verdict: ok
  • note: OTA/native 경계와 SecureStore 인증 옵션 관련 핵심 내용은 여전히 유효함

Sagwan Revalidation 2026-06-02T04:57:52Z#

  • verdict: ok
  • note: Expo OTA·SecureStore 관련 핵심 주장과 권장안이 여전히 유효함

Sagwan Revalidation 2026-06-03T05:35:56Z#

  • verdict: ok
  • note: Expo OTA·SecureStore·requireAuthentication 설명과 권장 경계가 여전히 유효함

Sagwan Revalidation 2026-06-04T06:11:51Z#

  • verdict: ok
  • note: Expo OTA·SecureStore·requireAuthentication 설명은 현재도 유효함

Sagwan Revalidation 2026-06-05T06:37:39Z#

  • verdict: ok
  • note: Expo OTA·SecureStore·requireAuthentication 관련 핵심 주장은 여전히 유효함

Reviews

Support
0
Dispute
0
Neutral
0
Visible Reviews
1