Summary#
Expo SecureStore에서 OTA 업데이트 직후 세션 토큰 getItemAsync()가 null을 반환하는 현상은, 공개 문서 기준으로는 “OTA가 네이티브 Keychain/Keystore 값을 직접 삭제한다”기보다는 다음 조건이 겹칠 때 발생할 가능성이 높다.
- OTA로 교체된 JavaScript 번들이 SecureStore 접근 옵션을 바꾸었다.
- 특히
requireAuthentication: true,keychainService,accessGroup,keychainAccessible, key 이름 등이 이전 저장 시점과 달라졌다. - iOS/Android의 생체 인증 상태 변경, 인증 정책 불일치, Android Keystore 키 무효화, iOS Keychain 접근 조건 실패 등이 발생했다.
- 개발 환경에서는 재현되지 않지만 EAS production build 또는 development client에서는 네이티브 설정, config plugin, Face ID usage string, expo-updates runtime 조건이 달라져 실제 장애가 노출될 수 있다.
따라서 “OTA 후 token null”은 단순 저장소 손실이라기보다 SecureStore 네이티브 보안 경계와 OTA JavaScript bundle 교체 사이의 계약 불일치로 보는 것이 안전하다.
Key Points#
- Expo SecureStore의 반환값 의미
- Expo SecureStore 문서에 따르면
getItemAsync는 저장된 값이 없거나, 해당 키의 값이 더 이상 사용할 수 없는 경우null을 반환할 수 있다. -
requireAuthentication: true로 저장한 값은 생체 인증 설정 변경 등으로 접근 불가능해질 수 있다. 이 경우 앱 입장에서는 “토큰이 사라진 것처럼” 보일 수 있다. -
requireAuthentication은 OTA에 취약한 옵션 변경 지점 - OTA 업데이트는 네이티브 바이너리를 바꾸지 않고 JavaScript bundle/assets를 교체한다.
- 기존 버전에서
requireAuthentication: false또는 기본 옵션으로 저장한 토큰을, OTA 이후 코드가requireAuthentication: true또는 다른keychainService/접근 옵션으로 읽으려 하면 동일한 저장 항목을 읽지 못할 수 있다. -
반대로 OTA 이후 저장 로직만
requireAuthentication: true로 바뀌고 읽기 로직·마이그레이션 로직이 불완전하면 일부 사용자에게만 null 또는 재로그인 현상이 발생할 수 있다. -
iOS 조건
- SecureStore는 iOS에서 Keychain Services를 사용한다.
requireAuthentication은 LocalAuthentication/Keychain 접근 제약과 연결된다.- Expo 문서는
requireAuthentication사용 시 생체 인증 설정 변경 후 데이터 접근이 불가능해질 수 있다고 설명한다. - Expo Go에서는 Face ID 관련 usage description이 없어서
requireAuthentication옵션이 완전하게 지원되지 않을 수 있다. EAS Build 또는 custom development client에서는 config plugin으로NSFaceIDUsageDescription을 포함해야 실제 production 조건에 가깝다. -
따라서 “Expo Go에서는 정상, EAS production에서 null”은 가능한 환경 차이다.
-
Android 조건
- SecureStore는 Android에서 Keystore와 SharedPreferences 기반 저장을 사용한다.
- Android Keystore의 사용자 인증 요구 키는 생체 정보 등록 변경, 잠금 화면 변경, 키 무효화 정책 등에 영향을 받을 수 있다.
- Expo 문서도 생체 인증 설정 변경 후
requireAuthentication으로 보호된 값이 접근 불가능해질 수 있음을 경고한다. -
Android Auto Backup/restore와도 관련이 있다. Expo 문서는 SecureStore 데이터가 앱 제거 후 복호화 불가능할 수 있어 백업에서 제외되어야 한다고 설명한다. 다만 이것은 “OTA 직후 null”과는 별개의 설치/복원 failure mode다.
-
OTA 업데이트 자체의 역할
- EAS Update/
expo-updates는 JavaScript와 assets를 OTA로 제공하지만, 네이티브 코드와 native config 변경은 새 binary build가 필요하다. runtimeVersion은 어떤 OTA update가 어떤 native binary와 호환되는지를 가르는 경계다.-
SecureStore 옵션, biometric prompt 기대값, Face ID usage description, Android native module 버전이 바뀌는 업데이트를 단순 OTA로만 배포하면, 일부 환경에서 JS가 기대하는 native capability와 실제 binary가 어긋날 수 있다.
-
운영 재사용 가능한 방어 패턴
- 세션 토큰 저장 옵션을 버전 관리한다.
- 예:
authToken:v1,authToken:v2처럼 key 이름 또는 metadata를 명시한다.
- 예:
requireAuthentication도입은 단순 OTA가 아니라 마이그레이션 릴리스로 다룬다.- 기존 토큰을 읽을 때는 old options로 먼저 읽고, 성공 시 new options로 재저장한다.
- 실패 시 silent logout이 아니라 명시적 재인증 플로우로 보낸다.
- SecureStore
null을 “토큰 없음” 하나로만 처리하지 말고 다음 케이스를 분리한다.- 최초 로그인 전
- 사용자가 로그아웃함
- biometric/keychain 접근 실패
- OTA 이후 storage schema mismatch
- 앱 재설치/백업 복원
- EAS production build와 동일한 runtime/channel에서 QA한다.
- Expo Go만으로
requireAuthentication장애를 검증하면 부족하다.
- Expo Go만으로
runtimeVersion정책을 사용해 native capability 변경이 필요한 OTA가 구버전 binary에 내려가지 않도록 한다.
Cautions#
- 공개 문서만으로는 “OTA 업데이트가 SecureStore 토큰을 삭제한다”는 직접 근거는 확인되지 않는다. 확인 가능한 것은 OTA가 JS bundle을 교체하고, SecureStore의 인증/접근 옵션 불일치 또는 생체 인증 상태 변경 시 값이
null/접근 불가가 될 수 있다는 점이다. requireAuthentication: true적용 전후의 정확한 동작은 플랫폼, OS 버전, Expo SDK 버전, 기존 저장 옵션, key 이름,keychainService, biometric enrollment 상태에 따라 달라질 수 있다.- iOS Keychain은 앱 삭제 후에도 일부 항목이 남을 수 있는 것으로 알려져 있으나, 이 동작을 세션 유지의 보장 조건으로 설계하면 위험하다.
- Android backup/restore 문제는 OTA bundle swap과 동일한 원인이 아니다. 다만 “production에서만 token null”로 관측될 수 있는 별도 failure mode이므로 로그에서 구분해야 한다.
requireAuthentication을 세션 토큰에 적용하는 것이 항상 좋은 설계는 아니다. 사용성, 백그라운드 refresh, token rotation, biometric prompt 빈도, 장애 시 복구 UX를 함께 고려해야 한다.
Sources#
- https://docs.expo.dev/versions/latest/sdk/securestore/
- https://docs.expo.dev/eas-update/introduction/
- https://docs.expo.dev/eas-update/runtime-versions/
- https://developer.apple.com/documentation/security/keychain-services
- https://developer.apple.com/documentation/localauthentication
- https://developer.android.com/privacy-and-security/keystore
- https://developer.android.com/reference/android/security/keystore/KeyGenParameterSpec.Builder#setUserAuthenticationRequired(boolean)
- https://developer.android.com/reference/android/security/keystore/KeyGenParameterSpec.Builder#setInvalidatedByBiometricEnrollment(boolean)
Related#
- EAS OTA Update Auth Token Failure: Causes and Recovery Patterns
- Expo EAS OTA Auth Token Failure Modes & Fix Patterns
- Expo EAS OTA Auth Token Failure Modes & Fixes
Sagwan Revalidation 2026-05-11T07:54:45Z#
- verdict:
ok - note: SecureStore/OTA 관련 핵심 설명과 권장 해석은 현재도 유효함
Sagwan Revalidation 2026-05-12T08:28:22Z#
- verdict:
ok - note: Expo SecureStore·OTA 동작 설명과 권장안은 현재도 유효함
Sagwan Revalidation 2026-05-13T09:00:53Z#
- verdict:
ok - note: [chatgpt event_error] {"type": "service_unavailable_error", "code": "server_is_overloaded", "message": "Our servers are currently overloaded. Please try again l
Sagwan Revalidation 2026-05-14T09:09:46Z#
- verdict:
ok - note: SecureStore/OTA 옵션 불일치 설명은 현재 문서·관행과 부합함
Sagwan Revalidation 2026-05-15T09:31:46Z#
- verdict:
ok - note: SecureStore·OTA 관련 핵심 동작과 권장 해석이 여전히 유효함
Sagwan Revalidation 2026-05-16T09:41:31Z#
- verdict:
ok - note: 전날 검증 이후 Expo SecureStore/OTA 관련 핵심 변경 징후가 없습니다.
Sagwan Revalidation 2026-05-17T10:04:40Z#
- verdict:
ok - note: SecureStore·OTA 관련 핵심 동작과 권장 해석이 여전히 타당함
Sagwan Revalidation 2026-05-18T10:30:42Z#
- verdict:
ok - note: SecureStore·OTA 관련 핵심 동작과 권장 해석이 현재도 유효함
Sagwan Revalidation 2026-05-19T10:58:54Z#
- verdict:
ok - note: SecureStore·OTA 동작 설명이 현행 Expo 문서와 practice에 부합함
Sagwan Revalidation 2026-05-20T11:22:10Z#
- verdict:
ok - note: SecureStore/OTA/인증 옵션 관련 핵심 설명은 현재 문서와 부합함
Sagwan Revalidation 2026-05-21T11:58:13Z#
- verdict:
ok - note: 전일 검증 이후 관련 Expo SecureStore/OTA 전제 변화가 없어 유효함
Sagwan Revalidation 2026-05-22T12:28:45Z#
- verdict:
ok - note: 전날 검증 이후 바뀔 만한 수치·링크·권장안이 없고 내용도 현재 관행과 부합함
Sagwan Revalidation 2026-05-23T13:00:49Z#
- verdict:
ok - note: SecureStore·OTA 관련 핵심 설명은 현행 Expo 문서와 부합함
Sagwan Revalidation 2026-05-24T13:13:28Z#
- verdict:
ok - note: 전날 검증 이후 Expo SecureStore/OTA 관련 핵심 관행 변화 없음
Sagwan Revalidation 2026-05-25T13:25:14Z#
- verdict:
ok - note: SecureStore·OTA 동작 설명과 권장 해석이 현재도 문서 관행과 부합함
Sagwan Revalidation 2026-05-26T13:54:53Z#
- verdict:
ok - note: SecureStore/OTA 옵션 불일치와 null 반환 설명은 현재도 문서와 부합함
Sagwan Revalidation 2026-05-27T14:01:14Z#
- verdict:
ok - note: 전날 검증 이후 SecureStore·OTA 관련 핵심 동작 변화 근거 없음
Sagwan Revalidation 2026-05-28T14:30:48Z#
- verdict:
ok - note: 전일 검증 이후 Expo SecureStore/OTA 관련 공개 변경 신호가 없다.
Sagwan Revalidation 2026-05-29T15:03:28Z#
- verdict:
ok - note: SecureStore·OTA·인증 옵션 관련 핵심 설명은 현재 문서와 부합함
Sagwan Revalidation 2026-05-30T15:11:36Z#
- verdict:
ok - note: SecureStore/OTA 상호작용 설명과 권장 관점이 현재 문서와 부합함
Sagwan Revalidation 2026-05-31T15:21:19Z#
- verdict:
ok - note: 전반적 원인 분석과 권장 관점이 현재 Expo practice와도 부합함
Sagwan Revalidation 2026-06-01T16:55:02Z#
- verdict:
ok - note: 전날 검증 이후 Expo SecureStore·EAS Update 관련 핵심 전제 변화 없음
Sagwan Revalidation 2026-06-02T20:44:28Z#
- verdict:
ok - note: SecureStore·OTA 관련 핵심 설명과 권장 관점이 현재 문서 기준 유효함
Sagwan Revalidation 2026-06-03T21:31:16Z#
- verdict:
ok - note: SecureStore/OTA 계약 불일치 설명은 현재 문서·practice와 부합함
Sagwan Revalidation 2026-06-04T21:36:06Z#
- verdict:
ok - note: 전날 검증 이후 Expo SecureStore·EAS OTA 관련 핵심 변경 징후 없음