Welcome to Authentication Strategies!
In the previous chapter, GitHub Infrastructure Logic, we built the heavy machinery capable of creating branches, files, and secrets. However, there is one major catch: GitHub won't let us touch anything yet.
Just like a construction crew can't enter a building site without a security badge, our tool can't modify a repository without a valid Access Token.
How do we get this security badge (the API Key)?
.env file or knows how to generate one manually. They just want to paste it in.If we force the beginner to manually generate keys, they will quit. If we force the power user to go through a browser login, they will get annoyed.
We solve this by implementing an Authentication Strategy pattern in our UI. We present the user with a choice, and the application adapts its behavior based on that choice.
We handle this primarily in two components:
ApiKeyStep.tsx: The menu where the user chooses their method.OAuthFlowStep.tsx: A specialized component that handles the complex browser-based login.ApiKeyStep)
The ApiKeyStep is more than just a text box; it is a switchboard. It looks at what is available and offers the best path.
This component accepts props to know if an "Existing Key" was found on the user's computer or if "OAuth" (browser login) is available.
// ApiKeyStep.tsx logic (simplified)
const selectedOption =
props.existingApiKey ? 'existing' : // Default to existing if found
props.onCreateOAuthToken ? 'oauth' : // Default to OAuth if available
'new'; // Fallback to manual entry
Explanation: We automatically pick the easiest option. If we found a key, select it. If not, suggest OAuth. If neither, ask for manual input.
We use a simple visual trick to show the user which path is active using the color() helper.
// ApiKeyStep.tsx rendering
<Text>
{selectedOption === 'oauth' ? color('success', theme)('> ') : ' '}
Create a long-lived token with your Claude subscription
</Text>
<Text>
{selectedOption === 'new' ? color('success', theme)('> ') : ' '}
Enter a new API key
</Text>
Explanation: We render a list. The active option gets a green arrow (>). The user uses Up/Down arrow keys to change the selectedOption state.
When the user presses Enter, we check which path they chose.
// Inside handleConfirm function
if (selectedOption === 'oauth' && onCreateOAuthToken) {
// Path A: Trigger the browser flow
onCreateOAuthToken();
} else {
// Path B: Submit the text key
onSubmit();
}
Explanation: If they picked OAuth, we signal the Wizard Orchestrator to switch scenes to the OAuth flow. If they picked manual, we submit the key they typed.
OAuthFlowStep)
If the user chooses the browser login, we enter the most complex part of our tool. The OAuthFlowStep.tsx component is actually a mini-application with its own internal state machine.
OAuth is like giving a valet key to your car. You (the User) tell the parking garage (GitHub/Claude) that this specific valet (Our App) is allowed to drive your car (Modify Code), but only for a limited time.
Let's visualize the "dance" required to get the key without the user copying and pasting it manually.
Let's look at how OAuthFlowStep.tsx manages this complexity.
Unlike simple steps, this step needs to track the progress of the network request.
type OAuthStatus =
| { state: 'starting' }
| { state: 'waiting_for_login'; url: string }
| { state: 'processing' }
| { state: 'success'; token: string }
| { state: 'error'; message: string };
Explanation: We define every possible situation the user can be in. This prevents bugs like showing a "Success" message while we are still loading.
When the component mounts, we immediately trigger the service.
// OAuthFlowStep.tsx
useEffect(() => {
if (oauthStatus.state === 'starting') {
// Start the process
startOAuth();
}
}, [oauthStatus.state]);
Explanation: As soon as this screen appears, we kick off the logic. The user doesn't need to press anything else.
The startOAuth function does the heavy lifting. It waits for the browser interaction to finish.
// Inside startOAuth
const result = await oauthService.startOAuthFlow(async (url) => {
// When we get the URL, update UI to tell user to look at browser
setOAuthStatus({ state: 'waiting_for_login', url });
});
// Once the 'await' finishes, we have the token!
setOAuthStatus({ state: 'success', token: result.accessToken });
Explanation: The oauthService (a helper class) pauses execution here until the user finishes logging in on the web. Once they do, the promise resolves, and we get the accessToken.
Sometimes, CLI tools can't open the browser automatically (e.g., inside a remote server). We handle this gracefully.
// Inside renderStatusMessage()
{showPastePrompt && (
<Box>
<Text>Paste code here if prompted ></Text>
<TextInput onChange={handleSubmitCode} ... />
</Box>
)}
Explanation: If the process takes too long, we assume the browser might not have opened. We show the URL on screen and provide a text box as a fallback plan.
In this chapter, we learned how to implement Authentication Strategies.
We didn't force a single method on the user. Instead:
ApiKeyStep) to let the user choose between Manual Entry or OAuth.OAuthFlowStep) to handle the complex asynchronous nature of browser-based logins.Now we have the infrastructure ready, and we have the keys to access it. But what happens if the keys are wrong? Or if the internet goes down?
Next Chapter: Error & Warning Management
Generated by Code IQ