Welcome to the third chapter of the Login project tutorial!
In the previous chapter, React-based Terminal UI, we built the visual interface. We created a nice dialog box that accepts credentials and tells us if the login was successful.
But getting a "Success" message is just the beginning. Now we face the "Hotel Room" Problem.
Imagine a hotel room. When Guest A checks out and Guest B checks in, the hotel staff doesn't just hand Guest B the key. They have to:
If the hotel skipped step 1, Guest B would find someone else's trash. If they skipped step 3, Guest B couldn't order food.
The Solution: In our CLI, when a user logs in, we must perform a choreographed routine of Side Effects. We need to wipe stale data (caches, old tokens) and fetch new data (permissions, settings) before the user starts typing commands.
We manage this process using a "Cleanup & Setup" pattern inside the callback function we discovered in the last chapter.
onDone)The React UI is just for display. When it finishes, it calls a function saying, "I'm done, here is the result." This is where our cleanup logic lives.
Before we trust the new user, we must ensure no data from the previous user remains. This includes cached profiles, cost tracking, and "trusted device" tokens.
Once the room is clean, we start fetching data. Because network requests take time, we trigger them immediately so they are ready by the time the user types their next command.
We are looking at the login.tsx file again. Specifically, we are looking inside the call function, at the logic inside onDone.
First, we need to tell the CLI context that the key has changed. This is like re-keying the door lock.
// Inside the call() function in login.tsx
return <Login onDone={async (success) => {
// 1. Swap the API Key in memory
context.onChangeAPIKey();
// 2. Clear old text formatting that might be signed by the old key
context.setMessages(stripSignatureBlocks);
// ... continued below
}} />;
onChangeAPIKey: Updates the internal state to use the new credentials.stripSignatureBlocks: Removes sensitive text blocks (like "Thinking..." indicators) that were verified by the old key.
If the login was successful (if (success)), we immediately wipe old data.
if (success) {
// Reset session cost tracking ($0.00 for the new session)
resetCostState();
// Clear the user profile cache (Name, Email, Avatar)
resetUserCache();
// Remove the old "Trusted Device" token so we don't accidentally send it
clearTrustedDeviceToken();
}
resetUserCache: Deletes the local JSON file containing the previous user's info.clearTrustedDeviceToken: This is crucial. If we send a token belonging to "User A" while trying to authenticate "User B", the server will reject us.Now that the environment is clean, we start fresh processes.
// Inside the if (success) block...
// Fetch new settings from the server (e.g. "Is this user allowed to use Beta features?")
void refreshRemoteManagedSettings();
// Check usage limits (e.g. "Does this user have credits left?")
void refreshPolicyLimits();
// Register this computer as a "Trusted Device" for this new account
void enrollTrustedDevice();
void keyword: Notice we put void before these functions? We trigger these background tasks without awaiting them. We want the CLI to feel snappy, so we let these run in the background while the UI closes.What actually happens during this choreography? Let's look at the sequence of events.
One of the most complex parts of this cleanup is the Trusted Device logic.
clearTrustedDeviceToken() deletes the file on the disk.enrollTrustedDevice() asks the server: "Hello, I am a new session for the Work Account. Please trust this computer."
If we didn't clear the token first, the enroll request might attach the old Personal token to the header. The server would see a mismatch ("Why is Personal Token asking about Work Account?") and reject the request.
We also need to update our feature flags (like A/B tests or permission toggles).
// Refresh feature flags (GrowthBook) to get updated permissions
refreshGrowthBookAfterAuthChange();
This ensures that if the new user has access to a feature like "Claude 3.5 Sonnet", the CLI knows about it immediately.
In this chapter, we learned about Session Initialization & Cleanup.
We discovered that logging in is more than just checking a password. It requires a strict protocol:
This ensures that every time you switch accounts, you get a fresh, secure, and bug-free experience.
Now that our session is clean and initialized, how does the rest of the application know that the login status changed? We need to broadcast this update to the rest of the app.
Next Chapter: Context-Driven State Updates
Generated by Code IQ