|
| 1 | +# CLAUDE-KNOWLEDGE.md |
| 2 | + |
| 3 | +This file documents key learnings from implementing wildcard domain support in Stack Auth, organized in Q&A format. |
| 4 | + |
| 5 | +## OAuth Flow and Validation |
| 6 | + |
| 7 | +### Q: Where does OAuth redirect URL validation happen in the flow? |
| 8 | +A: The validation happens in the callback endpoint (`/api/v1/auth/oauth/callback/[provider_id]/route.tsx`), not in the authorize endpoint. The authorize endpoint just stores the redirect URL and redirects to the OAuth provider. The actual validation occurs when the OAuth provider calls back, and the oauth2-server library validates the redirect URL. |
| 9 | + |
| 10 | +### Q: How do you test OAuth flows that should fail? |
| 11 | +A: Use `Auth.OAuth.getMaybeFailingAuthorizationCode()` instead of `Auth.OAuth.getAuthorizationCode()`. The latter expects success (status 303), while the former allows you to test failure cases. The failure happens at the callback stage with a 400 status and specific error message. |
| 12 | + |
| 13 | +### Q: What error is thrown for invalid redirect URLs in OAuth? |
| 14 | +A: The callback endpoint returns a 400 status with the message: "Invalid redirect URI. The URL you are trying to redirect to is not trusted. If it should be, add it to the list of trusted domains in the Stack Auth dashboard." |
| 15 | + |
| 16 | +## Wildcard Pattern Implementation |
| 17 | + |
| 18 | +### Q: How do you handle ** vs * precedence in regex patterns? |
| 19 | +A: Use a placeholder approach to prevent ** from being corrupted when replacing *: |
| 20 | +```typescript |
| 21 | +const doubleWildcardPlaceholder = '\x00DOUBLE_WILDCARD\x00'; |
| 22 | +regexPattern = regexPattern.replace(/\*\*/g, doubleWildcardPlaceholder); |
| 23 | +regexPattern = regexPattern.replace(/\*/g, '[^.]*'); |
| 24 | +regexPattern = regexPattern.replace(new RegExp(doubleWildcardPlaceholder, 'g'), '.*'); |
| 25 | +``` |
| 26 | + |
| 27 | +### Q: Why can't you use `new URL()` with wildcard domains? |
| 28 | +A: Wildcard characters (* and **) are not valid in URLs and will cause parsing errors. For wildcard domains, you need to manually parse the URL components instead of using the URL constructor. |
| 29 | + |
| 30 | +### Q: How do you validate URLs with wildcards? |
| 31 | +A: Extract the hostname pattern manually and use `matchHostnamePattern()`: |
| 32 | +```typescript |
| 33 | +const protocolEnd = domain.baseUrl.indexOf('://'); |
| 34 | +const protocol = domain.baseUrl.substring(0, protocolEnd + 3); |
| 35 | +const afterProtocol = domain.baseUrl.substring(protocolEnd + 3); |
| 36 | +const pathStart = afterProtocol.indexOf('/'); |
| 37 | +const hostnamePattern = pathStart === -1 ? afterProtocol : afterProtocol.substring(0, pathStart); |
| 38 | +``` |
| 39 | + |
| 40 | +## Testing Best Practices |
| 41 | + |
| 42 | +### Q: How should you run multiple independent test commands? |
| 43 | +A: Use parallel execution by batching tool calls together: |
| 44 | +```typescript |
| 45 | +// Good - runs in parallel |
| 46 | +const [result1, result2] = await Promise.all([ |
| 47 | + niceBackendFetch("/endpoint1"), |
| 48 | + niceBackendFetch("/endpoint2") |
| 49 | +]); |
| 50 | + |
| 51 | +// In E2E tests, the framework handles this automatically when you |
| 52 | +// batch multiple tool calls in a single response |
| 53 | +``` |
| 54 | + |
| 55 | +### Q: What's the correct way to update project configuration in E2E tests? |
| 56 | +A: Use the `/api/v1/internal/config/override` endpoint with PATCH method and admin access token: |
| 57 | +```typescript |
| 58 | +await niceBackendFetch("/api/v1/internal/config/override", { |
| 59 | + method: "PATCH", |
| 60 | + accessType: "admin", |
| 61 | + headers: { |
| 62 | + 'x-stack-admin-access-token': adminAccessToken, |
| 63 | + }, |
| 64 | + body: { |
| 65 | + config_override_string: JSON.stringify({ |
| 66 | + 'domains.trustedDomains.name': { baseUrl: '...', handlerPath: '...' } |
| 67 | + }), |
| 68 | + }, |
| 69 | +}); |
| 70 | +``` |
| 71 | + |
| 72 | +## Code Organization |
| 73 | + |
| 74 | +### Q: Where does domain validation logic belong? |
| 75 | +A: Core validation functions (`isValidHostnameWithWildcards`, `matchHostnamePattern`) belong in the shared utils package (`packages/stack-shared/src/utils/urls.tsx`) so they can be used by both frontend and backend. |
| 76 | + |
| 77 | +### Q: How do you simplify validation logic with wildcards? |
| 78 | +A: Replace wildcards with valid placeholders before validation: |
| 79 | +```typescript |
| 80 | +const normalizedDomain = domain.replace(/\*+/g, 'wildcard-placeholder'); |
| 81 | +url = new URL(normalizedDomain); // Now this won't throw |
| 82 | +``` |
| 83 | + |
| 84 | +## Debugging E2E Tests |
| 85 | + |
| 86 | +### Q: What does "ECONNREFUSED" mean in E2E tests? |
| 87 | +A: The backend server isn't running. Make sure to start the backend with `pnpm dev` before running E2E tests. |
| 88 | + |
| 89 | +### Q: How do you debug which stage of OAuth flow is failing? |
| 90 | +A: Check the error location: |
| 91 | +- Authorize endpoint (307 redirect) - Initial request succeeded |
| 92 | +- Callback endpoint (400 error) - Validation failed during callback |
| 93 | +- Token endpoint (400 error) - Validation failed during token exchange |
| 94 | + |
| 95 | +## Git and Development Workflow |
| 96 | + |
| 97 | +### Q: How should you format git commit messages in this project? |
| 98 | +A: Use a HEREDOC to ensure proper formatting: |
| 99 | +```bash |
| 100 | +git commit -m "$(cat <<'EOF' |
| 101 | +Commit message here. |
| 102 | +
|
| 103 | +🤖 Generated with [Claude Code](https://claude.ai/code) |
| 104 | +
|
| 105 | +Co-Authored-By: Claude <noreply@anthropic.com> |
| 106 | +EOF |
| 107 | +)" |
| 108 | +``` |
| 109 | + |
| 110 | +### Q: What commands should you run before considering a task complete? |
| 111 | +A: Always run: |
| 112 | +1. `pnpm test run <relevant-test-files>` - Run tests |
| 113 | +2. `pnpm lint` - Check for linting errors |
| 114 | +3. `pnpm typecheck` - Check for TypeScript errors |
| 115 | + |
| 116 | +## Common Pitfalls |
| 117 | + |
| 118 | +### Q: Why might imports get removed after running lint --fix? |
| 119 | +A: ESLint may remove "unused" imports. Always verify your changes after auto-fixing, especially if you're using imports in a way ESLint doesn't recognize (like in test expectations). |
| 120 | + |
| 121 | +### Q: What's a common linting error in test files? |
| 122 | +A: Missing newline at end of file. ESLint requires files to end with a newline character. |
| 123 | + |
| 124 | +### Q: How do you handle TypeScript errors about missing exports? |
| 125 | +A: Double-check that you're only importing what's actually exported from a module. The error "Module declares 'X' locally, but it is not exported" means you're trying to import something that isn't exported. |
0 commit comments