When a user presses back after completing login or onboarding and returns to the splash screen, they reach a pre-auth state while already authenticated — a CWE-862 (Missing Authorization Check) scenario where the auth gate is bypassed by navigating backwards through the stack. Beyond security, splash-screen return is a confusing dead end: users see a loading screen with no active content, often conclude the app has crashed, and close it. At minimum, you lose engagement; in apps with payment or subscription flows, back-navigation to the splash can expose a path to bypass gating.
Medium because post-auth back-navigation to splash screens bypasses auth gating (CWE-862) and presents a UX dead-end that causes user abandonment, though exploitation requires physical device access.
Use conditional navigator groups keyed on auth state rather than manual navigation.reset() calls — this is more reliable and eliminates the entire class of problem. In src/navigation/RootNavigator.tsx:
function RootNavigator() {
const { user } = useAuth() // your auth context
return (
<Stack.Navigator screenOptions={{ headerShown: false }}>
{user ? (
// Authenticated group — back button never reaches unauthenticated screens
<Stack.Group>
<Stack.Screen name="Home" component={HomeScreen} />
<Stack.Screen name="Settings" component={SettingsScreen} />
</Stack.Group>
) : (
// Unauthenticated group — rendered with no animation to avoid flash
<Stack.Group screenOptions={{ animationEnabled: false }}>
<Stack.Screen name="Splash" component={SplashScreen} />
<Stack.Screen name="Login" component={LoginScreen} />
</Stack.Group>
)}
</Stack.Navigator>
)
}
Swapping groups on auth state change resets the stack automatically — no CommonActions.reset() boilerplate needed, and back navigation is structurally impossible between groups.
ID: mobile-navigation-linking.navigation-ux.prevent-initial-back
Severity: medium
What to look for: Check whether your app has a splash or onboarding screen that should only be shown once. Look for navigation state reset or stack clearing after the user completes onboarding. Verify that users can't use back button to return to splash after completing it.
Pass criteria: Count all navigation reset or stack-clearing operations post-onboarding. Once a user leaves the splash/onboarding screen, back button navigates between app screens but never returns to splash. At least 1 navigation reset (CommonActions.reset or equivalent) must be called after onboarding completion. No more than 0 paths should allow back-navigation to splash.
Fail criteria: Users can press back to return to the splash screen after completing onboarding. Navigation state is not reset post-onboarding.
Skip (N/A) when: The app has no splash or onboarding screen (0 splash-related screen definitions found).
Detail on fail: "Users can press back after logging in to return to the splash screen" or "Navigation stack is not reset after onboarding completion"
Remediation: After onboarding or auth completion, reset the navigation stack to prevent back navigation to the initial screen:
function AuthContext() {
const [user, setUser] = useState(null)
const onLogIn = async (email, password) => {
// ... authenticate ...
setUser(authenticatedUser)
// This will trigger navigation to the app screens
}
return { user, onLogIn }
}
function RootNavigator() {
const { user } = useContext(AuthContext)
return (
<Stack.Navigator screenOptions={{ headerShown: false }}>
{user ? (
<Stack.Group>
<Stack.Screen name="Home" component={HomeScreen} />
{/* other app screens */}
</Stack.Group>
) : (
<Stack.Group screenOptions={{ animationEnabled: false }}>
<Stack.Screen name="Splash" component={SplashScreen} />
<Stack.Screen name="Login" component={LoginScreen} />
</Stack.Group>
)}
</Stack.Navigator>
)
}
By swapping navigator groups based on auth state, you ensure the navigation stack is reset and users can't back navigate to previous auth states.