Review
Migrate session tokens off AsyncStorage
CourtReserve still kept refresh tokens in plain AsyncStorage after the security review. Routes session IO through SecureStore, and on first read after upgrade performs a one-shot lift: copy any legacy AsyncStorage value into SecureStore, delete the old key, then never touch AsyncStorage for that token again.
ExpoTier 3exposecure-storeauthmigration
Click a line to flag it, pick one or more labels, then submit. If the change looks correct, approve it.
src/auth/sessionStore.ts+22-11
| 1 | 1 | import AsyncStorage from "@react-native-async-storage/async-storage"; | |
| 2 | + | import * as SecureStore from "expo-secure-store"; | |
| 2 | 3 | ||
| 3 | 4 | const REFRESH_KEY = "courtreserve.refresh"; | |
| 4 | 5 | ||
| 5 | - | export async function getRefreshToken(): Promise<string | null> { | |
| 6 | - | return AsyncStorage.getItem(REFRESH_KEY); | |
| 7 | - | } | |
| 8 | - | ||
| 9 | - | export async function setRefreshToken(token: string): Promise<void> { | |
| 10 | - | await AsyncStorage.setItem(REFRESH_KEY, token); | |
| 11 | - | } | |
| 12 | - | ||
| 13 | - | export async function clearRefreshToken(): Promise<void> { | |
| 14 | - | await AsyncStorage.removeItem(REFRESH_KEY); | |
| 15 | - | } | |
| 6 | + | export async function getRefreshToken(): Promise<string | null> { | |
| 7 | + | const secure = await SecureStore.getItemAsync(REFRESH_KEY); | |
| 8 | + | if (secure != null) return secure; | |
| 9 | + | ||
| 10 | + | const legacy = await AsyncStorage.getItem(REFRESH_KEY); | |
| 11 | + | if (legacy == null) return null; | |
| 12 | + | ||
| 13 | + | await SecureStore.setItemAsync(REFRESH_KEY, legacy); | |
| 14 | + | await AsyncStorage.removeItem(REFRESH_KEY); | |
| 15 | + | return legacy; | |
| 16 | + | } | |
| 17 | + | ||
| 18 | + | export async function setRefreshToken(token: string): Promise<void> { | |
| 19 | + | await SecureStore.setItemAsync(REFRESH_KEY, token); | |
| 20 | + | await AsyncStorage.removeItem(REFRESH_KEY); | |
| 21 | + | } | |
| 22 | + | ||
| 23 | + | export async function clearRefreshToken(): Promise<void> { | |
| 24 | + | await SecureStore.deleteItemAsync(REFRESH_KEY); | |
| 25 | + | await AsyncStorage.removeItem(REFRESH_KEY); | |
| 26 | + | } |