In the previous chapter, XML Messaging Protocol, we taught the AI how to speak clearly using "Radio Codes" (XML tags). The AI can now say "I want to run a command," and the system understands perfectly.
But before the AI can do anything useful on your behalfβlike checking your billing, managing your account, or syncing your historyβit needs to know who you are.
This chapter introduces the Keycard System. Just as you need a specific badge to enter your office building, the application needs specific keys (URLs, Client IDs, and Scopes) to connect to the right servers.
Imagine you are a developer working on this CLI tool. You exist in three parallel universes:
claude.ai and api.anthropic.com. Real money is spent here.api-staging.anthropic.com.localhost:8000.If you accidentally used the Production Keycard while trying to enter the Workbench, the system would reject you. Even worse, if you used Workbench code to talk to Production, you might crash the real server.
OAuth and Environment Configuration is the system that automatically checks which universe you are in and hands you the correct map and keycard.
This is a dictionary of addresses. It tells the app where the "Front Door" (Login Page) and the "Bank" (API Endpoint) are located for the current environment.
A keycard doesn't open every door. Scopes define exactly what the app is allowed to do.
This is the logic that looks at your environment variables (e.g., process.env.USER_TYPE) and decides which configuration object to return.
In oauth.ts, we define the addresses for our different universes. We use const objects to ensure these addresses never change while the app is running.
This is the default. If you download the tool from the internet, this is what you use.
// From oauth.ts
const PROD_OAUTH_CONFIG = {
// Where we go to verify who you are
CONSOLE_AUTHORIZE_URL: 'https://platform.claude.com/oauth/authorize',
// Where we send API requests
BASE_API_URL: 'https://api.anthropic.com',
// The ID badge number for this specific app
CLIENT_ID: '9d1c250a-e61b-44d9-88ed-5944d1962f5e',
}
Explanation: These are hardcoded strings. CLIENT_ID is a public UUID that tells the server "This request is coming from the Claude CLI."
When developers are fixing bugs, they run servers on their own machines.
// From oauth.ts
function getLocalOauthConfig() {
return {
// Note: connecting to localhost instead of the internet!
BASE_API_URL: 'http://localhost:8000',
CONSOLE_AUTHORIZE_URL: 'http://localhost:3000/oauth/authorize',
// ... other local ports
}
}
Explanation: Notice how the URLs change. If the app tries to connect to https://api.anthropic.com while testing a local feature, the test will fail.
When you log in with Google or Facebook, a popup asks: "Allow this app to view your contacts?" That is a Scope.
Our CLI needs specific permissions to function. We group these in oauth.ts.
We separate permissions based on which system we are talking to.
// From oauth.ts
// Permission to see who you are
export const CLAUDE_AI_PROFILE_SCOPE = 'user:profile'
// Permission to create API keys (spending money)
const CONSOLE_SCOPE = 'org:create_api_key'
When the user logs in, we ask for everything at once so they only have to click "Accept" one time.
// From oauth.ts
export const ALL_OAUTH_SCOPES = Array.from(
new Set([
CONSOLE_SCOPE, // For the Console (Billing)
CLAUDE_AI_PROFILE_SCOPE, // For the Chat Interface
'user:file_upload' // To upload files
])
)
Explanation: We combine the lists into a Set (to remove duplicates) and then turn it back into an array. This is the list sent to the OAuth server.
How does the app know which map to use? It checks the "Weather" (Environment Variables).
This logic lives in the getOauthConfig function.
// From oauth.ts
export function getOauthConfig() {
const type = getOauthConfigType() // 'prod', 'staging', or 'local'
switch (type) {
case 'local':
return getLocalOauthConfig()
case 'staging':
return STAGING_OAUTH_CONFIG
case 'prod':
return PROD_OAUTH_CONFIG
}
}
The helper function getOauthConfigType checks specific flags.
// From oauth.ts
function getOauthConfigType() {
// Only internal employees ('ant') can use staging/local
if (process.env.USER_TYPE === 'ant') {
if (process.env.USE_LOCAL_OAUTH) return 'local'
if (process.env.USE_STAGING_OAUTH) return 'staging'
}
// Everyone else defaults to Production
return 'prod'
}
Explanation: This is a safety mechanism. Even if a public user tries to set USE_STAGING_OAUTH, the code ignores it unless they are also flagged as an internal user (ant). This prevents users from accidentally connecting to test servers.
Let's visualize what happens when a user types claude login.
Sometimes, we deploy this tool to highly secure environments (like Government Clouds) that are neither Production nor Local. We call these "Custom" environments.
The code supports an override variable: CLAUDE_CODE_CUSTOM_OAUTH_URL.
// From oauth.ts
// Safety Check: We only allow specific secure URLs
const ALLOWED_OAUTH_BASE_URLS = [
'https://claude.fedstart.com',
// ... others
]
// Inside getOauthConfig...
const customUrl = process.env.CLAUDE_CODE_CUSTOM_OAUTH_URL
if (customUrl) {
// 1. Verify safety
if (!ALLOWED_OAUTH_BASE_URLS.includes(customUrl)) {
throw new Error('Security Alert: Unapproved endpoint.')
}
// 2. Override the config
config = {
...config,
BASE_API_URL: customUrl,
TOKEN_URL: `${customUrl}/v1/oauth/token`
}
}
Explanation: This allows the tool to work in private clouds without changing the source code. However, the ALLOWED_OAUTH_BASE_URLS list ensures a hacker can't trick the tool into sending passwords to a fake server.
Aside from OAuth, the app also needs keys for feature flagging (which we will cover in the next chapter). These keys also change based on the environment.
We handle this in keys.ts.
// From keys.ts
export function getGrowthBookClientKey(): string {
// Internal developers get the 'Dev' key
if (process.env.USER_TYPE === 'ant') {
return 'sdk-yZQvlplybuXjYh6L'
}
// Public users get the 'Prod' key
return 'sdk-zAZezfDKGoZuXXKe'
}
Explanation: This ensures that when we test a new feature (like a "Dark Mode") internally, regular users don't see it until we are ready.
In this chapter, we learned:
oauth.ts to store all these addresses in one place, rather than scattering strings throughout the code.ALL_OAUTH_SCOPES) to ensure the app has the access it needsβand nothing more.Now the AI knows how to speak (Chapter 4) and where to connect (Chapter 5). But how do we roll out experimental features to just some users without breaking the app for everyone?
Next Chapter: Feature Flagging and Betas
Generated by Code IQ