@startanaicompany/cli 1.4.8 → 1.4.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,166 @@
1
+ # OAuth CLI Fix - Complete
2
+
3
+ ## Status: ✅ Ready for Testing
4
+
5
+ All OAuth authentication issues have been fixed. The CLI version 1.4.9 is ready to be published to npm.
6
+
7
+ ## What Was Fixed
8
+
9
+ ### Backend Fixes (Deployed ✅)
10
+ 1. **Schema Mismatch** - Fixed oauth.js to use correct table (`session_tokens`) and columns (`token_hash`)
11
+ 2. **Email Retrieval** - Added JOIN with users table to get email field
12
+ 3. **Deployed** - Backend changes deployed to production at commit a43192f
13
+
14
+ ### CLI Fixes (Ready for Publish 📦)
15
+ 1. **Wait Time** - Increased from 5s to 60s to give users time to authorize
16
+ 2. **Error Handling** - Continue polling on 401/404 errors instead of failing immediately
17
+ 3. **HTTP Headers** - Use correct header based on token type:
18
+ - Session tokens (`st_*`) → `X-Session-Token` header
19
+ - API keys (`cw_*`) → `X-API-Key` header
20
+ 4. **Version** - Bumped to 1.4.9
21
+
22
+ ## Publishing the CLI
23
+
24
+ **The CLI cannot auto-publish due to npm 2FA requirement.**
25
+
26
+ To publish manually:
27
+
28
+ ```bash
29
+ cd /home/milko/projects/saac-cli
30
+
31
+ # Get 2FA code from authenticator app
32
+ npm publish --access public --otp=<YOUR_CODE>
33
+ ```
34
+
35
+ ## Testing the OAuth Flow
36
+
37
+ Once published, test with:
38
+
39
+ ```bash
40
+ # Install latest version
41
+ npm install -g @startanaicompany/cli
42
+
43
+ # Test OAuth connection
44
+ saac git connect git.startanaicompany.com
45
+ ```
46
+
47
+ **Expected Behavior:**
48
+ 1. ✅ CLI opens browser to OAuth page
49
+ 2. ✅ User has 60 seconds to authorize
50
+ 3. ✅ CLI polls using `X-Session-Token` header
51
+ 4. ✅ Browser shows: "✅ Authorization Successful"
52
+ 5. ✅ CLI detects completion: "Connected to git.startanaicompany.com as <username>"
53
+
54
+ ## What Changed in the Code
55
+
56
+ ### Backend: `/home/milko/projects/coolifywrapper/src/routes/oauth.js`
57
+
58
+ **Before (Broken):**
59
+ ```javascript
60
+ const sessionResult = await db.query(
61
+ 'SELECT user_id, email FROM sessions WHERE session_token = $1',
62
+ [token]
63
+ );
64
+ ```
65
+
66
+ **After (Fixed):**
67
+ ```javascript
68
+ const sessionResult = await db.query(
69
+ `SELECT st.user_id, u.email
70
+ FROM session_tokens st
71
+ JOIN users u ON st.user_id = u.id
72
+ WHERE st.token_hash = $1 AND st.expires_at > NOW() AND st.revoked_at IS NULL`,
73
+ [crypto.createHash('sha256').update(token).digest('hex')]
74
+ );
75
+ ```
76
+
77
+ ### CLI: `/home/milko/projects/saac-cli/src/lib/oauth.js`
78
+
79
+ **Before (Broken):**
80
+ ```javascript
81
+ // Always used X-API-Key header
82
+ const response = await axios.get(
83
+ `${baseUrl}/oauth/poll/${sessionId}`,
84
+ {
85
+ headers: {
86
+ 'X-API-Key': apiKey,
87
+ },
88
+ }
89
+ );
90
+ ```
91
+
92
+ **After (Fixed):**
93
+ ```javascript
94
+ // Use correct header based on token type
95
+ const headers = apiKey.startsWith('st_')
96
+ ? { 'X-Session-Token': apiKey }
97
+ : { 'X-API-Key': apiKey };
98
+
99
+ const response = await axios.get(
100
+ `${baseUrl}/oauth/poll/${sessionId}`,
101
+ { headers }
102
+ );
103
+ ```
104
+
105
+ ## Timeline of Fixes
106
+
107
+ 1. **Backend schema fix** - Commit a43192f (deployed)
108
+ 2. **CLI wait time** - Changed to 60s (version 1.4.8)
109
+ 3. **CLI header fix** - Commit 493a512 (version 1.4.9, ready to publish)
110
+
111
+ ## Architecture Notes
112
+
113
+ ### Why Two Token Types?
114
+
115
+ - **Session Tokens (`st_*`)** - Short-lived (1 day), browser-based, for CLI login
116
+ - **API Keys (`cw_*`)** - Long-lived, for programmatic access
117
+
118
+ ### Why Different Headers?
119
+
120
+ The backend authentication middleware checks headers in this priority:
121
+
122
+ 1. `X-Session-Token` - For session tokens
123
+ 2. `X-API-Key` - For API keys
124
+
125
+ If you send `st_*` tokens via `X-API-Key` header, the backend validates them as API keys and rejects them (API keys must start with `cw_`).
126
+
127
+ ### How OAuth Flow Works
128
+
129
+ ```
130
+ 1. CLI calls: saac git connect git.startanaicompany.com
131
+ 2. CLI generates session_id (random hex)
132
+ 3. CLI opens browser: /oauth/authorize?git_host=...&session_id=...&token=st_...
133
+ 4. User clicks "Authorize" in browser
134
+ 5. Browser redirects to Gitea OAuth
135
+ 6. User approves in Gitea
136
+ 7. Gitea redirects to: /oauth/callback?code=...&state=...
137
+ 8. Backend exchanges code for access token
138
+ 9. Backend stores connection in database
139
+ 10. Backend updates oauth_cli_sessions to 'completed'
140
+ 11. CLI polls /oauth/poll/:session_id (with X-Session-Token header)
141
+ 12. CLI receives status='completed' and displays success
142
+ ```
143
+
144
+ ## Troubleshooting
145
+
146
+ ### If "Request failed with status code 401"
147
+ - Check that CLI version is 1.4.9 or higher
148
+ - Verify backend is deployed (commit a43192f or later)
149
+
150
+ ### If "Authorization timed out"
151
+ - User has 5 minutes total (60s initial wait + 150 polls × 2s)
152
+ - Check that user completed OAuth in browser
153
+
154
+ ### If browser shows success but CLI keeps polling
155
+ - This was the header mismatch bug - fixed in 1.4.9
156
+ - Ensure using latest CLI version
157
+
158
+ ## Next Steps
159
+
160
+ 1. **Publish CLI** - Run `npm publish --access public --otp=<code>`
161
+ 2. **Test OAuth** - Run `saac git connect git.startanaicompany.com`
162
+ 3. **Verify** - Browser should show success, CLI should detect it
163
+
164
+ ---
165
+
166
+ **All issues resolved. Ready for production testing.**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@startanaicompany/cli",
3
- "version": "1.4.8",
3
+ "version": "1.4.10",
4
4
  "description": "Official CLI for StartAnAiCompany.com - Deploy AI recruitment sites with ease",
5
5
  "main": "src/index.js",
6
6
  "bin": {
package/src/lib/oauth.js CHANGED
@@ -97,13 +97,14 @@ async function pollForCompletion(sessionId, apiKey) {
97
97
  await sleep(pollInterval);
98
98
 
99
99
  try {
100
+ // Use correct header based on token type
101
+ const headers = apiKey.startsWith('st_')
102
+ ? { 'X-Session-Token': apiKey }
103
+ : { 'X-API-Key': apiKey };
104
+
100
105
  const response = await axios.get(
101
106
  `${baseUrl}/oauth/poll/${sessionId}`,
102
- {
103
- headers: {
104
- 'X-API-Key': apiKey,
105
- },
106
- }
107
+ { headers }
107
108
  );
108
109
 
109
110
  const { status, gitUsername, gitHost } = response.data;
@@ -144,13 +145,14 @@ async function pollForCompletion(sessionId, apiKey) {
144
145
  */
145
146
  async function getConnection(gitHost, apiKey) {
146
147
  try {
148
+ // Use correct header based on token type
149
+ const headers = apiKey.startsWith('st_')
150
+ ? { 'X-Session-Token': apiKey }
151
+ : { 'X-API-Key': apiKey };
152
+
147
153
  const response = await axios.get(
148
154
  `${getApiUrl()}/users/me/oauth`,
149
- {
150
- headers: {
151
- 'X-API-Key': apiKey,
152
- },
153
- }
155
+ { headers }
154
156
  );
155
157
 
156
158
  const connection = response.data.connections.find(
@@ -169,13 +171,14 @@ async function getConnection(gitHost, apiKey) {
169
171
  * @returns {Promise<array>} - Array of connection objects
170
172
  */
171
173
  async function listConnections(apiKey) {
174
+ // Use correct header based on token type
175
+ const headers = apiKey.startsWith('st_')
176
+ ? { 'X-Session-Token': apiKey }
177
+ : { 'X-API-Key': apiKey };
178
+
172
179
  const response = await axios.get(
173
180
  `${getApiUrl()}/users/me/oauth`,
174
- {
175
- headers: {
176
- 'X-API-Key': apiKey,
177
- },
178
- }
181
+ { headers }
179
182
  );
180
183
 
181
184
  return response.data.connections || [];
@@ -187,13 +190,14 @@ async function listConnections(apiKey) {
187
190
  * @param {string} apiKey - User's API key
188
191
  */
189
192
  async function revokeConnection(gitHost, apiKey) {
193
+ // Use correct header based on token type
194
+ const headers = apiKey.startsWith('st_')
195
+ ? { 'X-Session-Token': apiKey }
196
+ : { 'X-API-Key': apiKey };
197
+
190
198
  await axios.delete(
191
199
  `${getApiUrl()}/users/me/oauth/${encodeURIComponent(gitHost)}`,
192
- {
193
- headers: {
194
- 'X-API-Key': apiKey,
195
- },
196
- }
200
+ { headers }
197
201
  );
198
202
  }
199
203