refactor(api): switch to header-based auth for GET requests
- Replace body parsing with header validation for GET endpoints - Update router to properly handle GET vs POST requests - Add new ValidateRequestWithHeader function - Update README to document header requirements
This commit is contained in:
@@ -68,6 +68,12 @@ All API responses are returned in JSON format:
|
||||
}
|
||||
```
|
||||
|
||||
**Important Notes:**
|
||||
- For **POST** requests: All data is sent in the request body as JSON
|
||||
- For **GET** requests: All data is sent in HTTP headers (prefixed with `X-`)
|
||||
- All requests require authentication (except `/register` and `/login`)
|
||||
- Timestamps are in milliseconds (Unix timestamp)
|
||||
|
||||
---
|
||||
|
||||
### 1. Register User
|
||||
@@ -269,13 +275,16 @@ You can modify multiple fields at once:
|
||||
**Method:** GET
|
||||
**Auth Required:** Yes (token)
|
||||
|
||||
**Request:**
|
||||
```json
|
||||
{
|
||||
"token": "your_token",
|
||||
"timeStamp": 1704067200000
|
||||
}
|
||||
**Request Headers:**
|
||||
```
|
||||
X-Token: your_token
|
||||
X-Timestamp: 1704067200000
|
||||
```
|
||||
|
||||
| Header | Type | Required | Description |
|
||||
|--------|------|----------|-------------|
|
||||
| X-Token | string | Yes | Authentication token |
|
||||
| X-Timestamp | int64 | Yes | Client timestamp in milliseconds |
|
||||
|
||||
**Response (admin/superuser):**
|
||||
```json
|
||||
|
||||
@@ -433,14 +433,7 @@ func ListInstancesHandler(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
body, err := io.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
SendErrorResponse(w, http.StatusBadRequest, "failed to read request body")
|
||||
return
|
||||
}
|
||||
defer r.Body.Close()
|
||||
|
||||
userID, _, err := ValidateRequestWithBody(w, r, body)
|
||||
userID, _, err := ValidateRequestWithHeader(w, r)
|
||||
if err != nil {
|
||||
SendErrorResponse(w, http.StatusUnauthorized, err.Error())
|
||||
return
|
||||
|
||||
+41
@@ -249,6 +249,47 @@ func ValidateRequestWithBody(w http.ResponseWriter, r *http.Request, body []byte
|
||||
return userID, token, nil
|
||||
}
|
||||
|
||||
func ValidateRequestWithHeader(w http.ResponseWriter, r *http.Request, requiredFields ...string) (int, string, error) {
|
||||
token := r.Header.Get("X-Token")
|
||||
if token == "" {
|
||||
return 0, "", errors.New("token is required in header")
|
||||
}
|
||||
|
||||
timeStampStr := r.Header.Get("X-Timestamp")
|
||||
timeStamp := int64(0)
|
||||
if timeStampStr != "" {
|
||||
var err error
|
||||
timeStamp, err = strconv.ParseInt(timeStampStr, 10, 64)
|
||||
if err != nil {
|
||||
return 0, "", errors.New("invalid timestamp format in header")
|
||||
}
|
||||
} else if !globalConfig.Debug {
|
||||
return 0, "", errors.New("timestamp is required in header")
|
||||
}
|
||||
|
||||
if err := ValidateTimeStamp(timeStamp); err != nil {
|
||||
return 0, "", err
|
||||
}
|
||||
|
||||
userID, err := extractUserIDFromToken(token)
|
||||
if err != nil {
|
||||
return 0, "", err
|
||||
}
|
||||
|
||||
if err := ValidateToken(userID, token); err != nil {
|
||||
return 0, "", err
|
||||
}
|
||||
|
||||
for _, field := range requiredFields {
|
||||
headerValue := r.Header.Get(fmt.Sprintf("X-%s", field))
|
||||
if headerValue == "" {
|
||||
return 0, "", fmt.Errorf("required field %s is missing in header", field)
|
||||
}
|
||||
}
|
||||
|
||||
return userID, token, nil
|
||||
}
|
||||
|
||||
func GetUserType(userID int) (string, error) {
|
||||
user, err := GetUserByID(userID)
|
||||
if err != nil {
|
||||
|
||||
@@ -21,16 +21,24 @@ func setupRoutes() {
|
||||
|
||||
remainingPath := path[len("/frpcAct/instanceMgr/"):]
|
||||
|
||||
if r.Method == http.MethodPost {
|
||||
if remainingPath == "create" {
|
||||
CreateInstanceHandler(w, r)
|
||||
if r.Method == http.MethodGet {
|
||||
if remainingPath == "list" {
|
||||
ListInstancesHandler(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
if remainingPath == "list" {
|
||||
instanceName := strings.Trim(remainingPath, "/")
|
||||
if instanceName != "" {
|
||||
ListInstancesHandler(w, r)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if r.Method == http.MethodPost {
|
||||
if remainingPath == "create" {
|
||||
CreateInstanceHandler(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
if strings.HasSuffix(remainingPath, "/delete") {
|
||||
instanceName := strings.TrimSuffix(remainingPath, "/delete")
|
||||
@@ -47,12 +55,6 @@ func setupRoutes() {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
instanceName := strings.Trim(remainingPath, "/")
|
||||
if instanceName != "" {
|
||||
ListInstancesHandler(w, r)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
SendErrorResponse(w, http.StatusNotFound, "endpoint not found")
|
||||
|
||||
Reference in New Issue
Block a user