Skip to content

Commit 58991fa

Browse files
committed
Add token-based validation for authentication upload
Implemented token input for `/v1/auth/upload` API with global and instance-specific validation. Added token support to `auth_upload.html` form, refactored browser cookie handling, and improved instance page reload logic.
1 parent 4169649 commit 58991fa

File tree

4 files changed

+96
-14
lines changed

4 files changed

+96
-14
lines changed

internal/api/handlers.go

Lines changed: 46 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,34 @@ func (h *APIHandlers) validateAPIToken(c *gin.Context, instanceName string) bool
8484
return false
8585
}
8686

87+
// validateToken validates a token for the given instance
88+
func (h *APIHandlers) validateToken(token string, instanceName string) bool {
89+
if token == "" {
90+
return false
91+
}
92+
93+
// Check global tokens first
94+
for _, globalToken := range h.appConfig.Tokens {
95+
if globalToken == token {
96+
return true
97+
}
98+
}
99+
100+
// Check instance-specific tokens
101+
for _, instance := range h.appConfig.Instance {
102+
if instance.Name == instanceName {
103+
for _, instanceToken := range instance.Tokens {
104+
if instanceToken == token {
105+
return true
106+
}
107+
}
108+
break
109+
}
110+
}
111+
112+
return false
113+
}
114+
87115
func (h *APIHandlers) TakeScreenshot(c *gin.Context) {
88116
defer ScreenshotMutex.Unlock()
89117
ScreenshotMutex.Lock()
@@ -403,8 +431,9 @@ func (h *APIHandlers) BrowserReload(c *gin.Context) {
403431
// AuthUpload handles the /v1/auth/upload endpoint
404432
func (h *APIHandlers) AuthUpload(c *gin.Context) {
405433
var requestData struct {
406-
Name string `json:"name" binding:"required"`
407-
Auth string `json:"auth" binding:"required"`
434+
Name string `json:"name" binding:"required"`
435+
Token string `json:"token" binding:"required"`
436+
Auth string `json:"auth" binding:"required"`
408437
}
409438

410439
if err := c.ShouldBindJSON(&requestData); err != nil {
@@ -426,6 +455,12 @@ func (h *APIHandlers) AuthUpload(c *gin.Context) {
426455
return
427456
}
428457

458+
// Validate the token
459+
if !h.validateToken(requestData.Token, requestData.Name) {
460+
c.JSON(http.StatusUnauthorized, gin.H{"error": "Invalid verification token", "code": 401})
461+
return
462+
}
463+
429464
// Validate the auth JSON format
430465
var authInfo chrome.AuthInfo
431466
if err := json.Unmarshal([]byte(requestData.Auth), &authInfo); err != nil {
@@ -463,12 +498,20 @@ func (h *APIHandlers) AuthUpload(c *gin.Context) {
463498

464499
// Navigate to the instance URL
465500
pageCtx := page.GetContext()
501+
err = chrome.ClearCookies(pageCtx)
502+
if err != nil {
503+
c.JSON(http.StatusInternalServerError, gin.H{"error": fmt.Sprintf("Failed to load auth info: %v", err), "code": 500})
504+
return
505+
}
506+
page.Close()
466507

467-
err = chrome.LoadAuthInfo(pageCtx, instanceConfig.URL, instanceConfig.Auth.File)
508+
page, err = h.pages[instanceConfig.Name].GetBrowserManager().NewPage(instanceConfig.URL, instanceConfig.Adapter, instanceConfig.Auth.File)
468509
if err != nil {
469510
c.JSON(http.StatusInternalServerError, gin.H{"error": fmt.Sprintf("Failed to load auth info: %v", err), "code": 500})
470511
return
471512
}
513+
h.pages[instanceConfig.Name] = page
514+
pageCtx = page.GetContext()
472515

473516
if err = chromedp.Run(pageCtx, chromedp.Navigate(instanceConfig.URL)); err != nil {
474517
c.JSON(http.StatusInternalServerError, gin.H{"error": fmt.Sprintf("Failed to reload page: %v", err), "code": 500})

internal/browser/chrome/manager.go

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@ func (m *Manager) NewPage(url, adapterName, authFile string) (*Page, error) {
136136
sniffURL = append(sniffURL, m.appConfig.Instance[i].SniffURL...)
137137
}
138138

139-
return NewPage(m.browserCtx, adapterName, url, authFile, sniffURL)
139+
return NewPage(m, adapterName, url, authFile, sniffURL)
140140
}
141141

142142
func (m *Manager) Close() error {
@@ -207,6 +207,22 @@ func GetCookies(pageCtx context.Context) ([]*network.Cookie, error) {
207207
return cookies, nil
208208
}
209209

210+
func ClearCookies(pageCtx context.Context) error {
211+
var err error
212+
err = chromedp.Run(pageCtx, chromedp.ActionFunc(func(ctx context.Context) error {
213+
err = network.ClearBrowserCookies().Do(ctx)
214+
if err != nil {
215+
return err
216+
}
217+
return nil
218+
}))
219+
220+
if err != nil {
221+
return fmt.Errorf("failed to clear cookies: %w", err)
222+
}
223+
return nil
224+
}
225+
210226
// ClearBrowserCache clears the browser cache.
211227
func (m *Manager) ClearBrowserCache() error {
212228
if m.browserCtx == nil {

internal/browser/chrome/page.go

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import (
2222
)
2323

2424
type Page struct {
25+
manager *Manager
2526
ctx context.Context
2627
cancel context.CancelFunc
2728
queue *utils.Queue[*AIResponse]
@@ -30,14 +31,16 @@ type Page struct {
3031
URL string
3132
}
3233

33-
func NewPage(browserCtx context.Context, adapterName string, url string, authFilePath string, sniffURLs ...[]string) (*Page, error) {
34+
func NewPage(manager *Manager, adapterName string, url string, authFilePath string, sniffURLs ...[]string) (*Page, error) {
3435
var sniffURL []string
3536
if len(sniffURLs) > 0 {
3637
sniffURL = sniffURLs[0]
3738
} else {
3839
sniffURL = make([]string, 0)
3940
}
4041

42+
browserCtx := manager.browserCtx
43+
4144
if browserCtx == nil {
4245
return nil, fmt.Errorf("browser context not initialized. Call LaunchBrowserAndContext first")
4346
}
@@ -162,6 +165,7 @@ func NewPage(browserCtx context.Context, adapterName string, url string, authFil
162165
log.Debugf("New Chromedp page (targetID: %s) created.", newTargetID)
163166

164167
return &Page{
168+
manager: manager,
165169
ctx: newPageCtx,
166170
cancel: newPageCancel,
167171
queue: queue,
@@ -182,6 +186,10 @@ func (p *Page) ResponseData() (*adapter.AdapterResponse, error) {
182186
return nil, fmt.Errorf("adapter %s not found", p.adapterName)
183187
}
184188

189+
func (p *Page) GetBrowserManager() *Manager {
190+
return p.manager
191+
}
192+
185193
func (p *Page) GetContext() context.Context {
186194
return p.ctx
187195
}

internal/html/auth_upload.html

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,14 @@ <h1>🔐 Auth Upload</h1>
214214
</select>
215215
</div>
216216

217+
<div class="form-group">
218+
<label for="tokenInput">Verification Token:</label>
219+
<input type="password" id="tokenInput" class="form-control" placeholder="Enter your verification token" required>
220+
<small style="color: #666; font-size: 0.9rem; margin-top: 5px; display: block;">
221+
Enter a global token or instance-specific token for authentication
222+
</small>
223+
</div>
224+
217225
<div class="form-group">
218226
<label for="authFile">Authentication JSON File:</label>
219227
<div class="file-upload-wrapper">
@@ -318,9 +326,11 @@ <h1>🔐 Auth Upload</h1>
318326
function setupForm() {
319327
const form = document.getElementById('uploadForm');
320328
const instanceSelect = document.getElementById('instanceSelect');
321-
329+
const tokenInput = document.getElementById('tokenInput');
330+
322331
instanceSelect.addEventListener('change', updateSubmitButton);
323-
332+
tokenInput.addEventListener('input', updateSubmitButton);
333+
324334
form.addEventListener('submit', async function(e) {
325335
e.preventDefault();
326336
await uploadAuth();
@@ -329,23 +339,26 @@ <h1>🔐 Auth Upload</h1>
329339

330340
function updateSubmitButton() {
331341
const instanceSelect = document.getElementById('instanceSelect');
342+
const tokenInput = document.getElementById('tokenInput');
332343
const fileInput = document.getElementById('authFile');
333344
const submitButton = document.getElementById('submitButton');
334-
345+
335346
const hasInstance = instanceSelect.value !== '';
347+
const hasToken = tokenInput.value.trim() !== '';
336348
const hasFile = fileInput.files.length > 0;
337-
338-
submitButton.disabled = !(hasInstance && hasFile);
349+
350+
submitButton.disabled = !(hasInstance && hasToken && hasFile);
339351
}
340352

341353
async function uploadAuth() {
342354
const instanceSelect = document.getElementById('instanceSelect');
355+
const tokenInput = document.getElementById('tokenInput');
343356
const fileInput = document.getElementById('authFile');
344357
const loading = document.getElementById('loading');
345358
const submitButton = document.getElementById('submitButton');
346-
347-
if (!instanceSelect.value || !fileInput.files[0]) {
348-
showMessage('Please select an instance and choose a file.', 'error');
359+
360+
if (!instanceSelect.value || !tokenInput.value.trim() || !fileInput.files[0]) {
361+
showMessage('Please select an instance, enter a token, and choose a file.', 'error');
349362
return;
350363
}
351364

@@ -358,7 +371,7 @@ <h1>🔐 Auth Upload</h1>
358371
// Read file content
359372
const file = fileInput.files[0];
360373
const fileContent = await readFileAsText(file);
361-
374+
362375
// Validate JSON
363376
let authData;
364377
try {
@@ -375,6 +388,7 @@ <h1>🔐 Auth Upload</h1>
375388
},
376389
body: JSON.stringify({
377390
name: instanceSelect.value,
391+
token: tokenInput.value.trim(),
378392
auth: fileContent
379393
})
380394
});
@@ -385,6 +399,7 @@ <h1>🔐 Auth Upload</h1>
385399
showMessage('Authentication uploaded successfully!', 'success');
386400
// Reset form
387401
instanceSelect.value = '';
402+
tokenInput.value = '';
388403
fileInput.value = '';
389404
document.querySelector('.upload-text').textContent = 'Choose JSON file or drag & drop here';
390405
document.querySelector('.file-upload-button').classList.remove('has-file');

0 commit comments

Comments
 (0)