Files
identity-manager/authenticate.go

124 lines
3.9 KiB
Go

package main
import (
"context"
"crypto/rsa"
"encoding/json"
"fmt"
"libshared"
"log"
"net/http"
"github.com/jackc/pgx/v5"
)
type AuthenticateRequest struct {
Accountid int64 `json:"accountid,string"`
Username string `json:"username"`
Password string `json:"password"`
}
type AuthenticateResponse struct {
Token string `json:"token"`
}
var privateKey *rsa.PrivateKey
func authenticateHandler(w http.ResponseWriter, r *http.Request) {
var authenticaterequest AuthenticateRequest
var err error
var checkExisting pgx.Row
var hashText string
var ok bool
var token string
//secret := []byte("super-secret-key")
w.Header().Set("Content-Type", "application/json")
// Only allow POST method
if r.Method != http.MethodPost {
//http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
w.WriteHeader(http.StatusMethodNotAllowed)
w.Header().Set("Content-Type", "application/json")
apiresponse := libshared.NewAPIResponse("fail", "POST method required", AuthenticateResponse{})
json.NewEncoder(w).Encode(apiresponse)
return
}
// Optional: enforce content type
if r.Header.Get("Content-Type") != "application/json" {
apiresponse := libshared.NewAPIResponse("fail", "Content-Type must be application/json", AuthenticateResponse{})
w.WriteHeader(http.StatusUnsupportedMediaType)
//response["status"] = "error"
//response["message"] = "Content-Type must be application/json"
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(apiresponse)
return
}
// Read body with size limit (protect against huge requests)
const maxBodyBytes = 1 << 20 // 1 MB
r.Body = http.MaxBytesReader(w, r.Body, maxBodyBytes)
// Decode JSON from request body directly into struct
log.Println(r.Body)
err = json.NewDecoder(r.Body).Decode(&authenticaterequest)
if err != nil {
apiresponse := libshared.NewAPIResponse("fail", "Invalid JSON: "+err.Error(), AuthenticateResponse{})
w.WriteHeader(http.StatusInternalServerError)
//response["status"] = "error"
//response["message"] = "Invalid JSON: " + err.Error()
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(apiresponse)
return
}
log.Println(authenticaterequest)
checkExisting = libshared.Pool.QueryRow(context.Background(),
"SELECT password_hash FROM identities WHERE accountid = $1 AND provider = $2 AND provider_user_id = $3",
authenticaterequest.Accountid, "local", authenticaterequest.Username)
log.Println("Received Authentication Request: AccountID:", authenticaterequest.Accountid, "Username:", authenticaterequest.Username, "Password:", authenticaterequest.Password)
err = checkExisting.Scan(&hashText)
if err != nil {
apiresponse := libshared.NewAPIResponse("fail", "User account does not exist", AuthenticateResponse{})
log.Println(err)
w.WriteHeader(http.StatusUnauthorized)
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(apiresponse)
return
}
log.Println(hashText)
ok = verifyPassword(authenticaterequest.Password, hashText)
if ok == false {
apiresponse := libshared.NewAPIResponse("fail", "Incorrect username or password", AuthenticateResponse{})
//response["message"] = "Bad password"
w.WriteHeader(http.StatusUnauthorized)
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(apiresponse)
return
}
token, err = libshared.CreateJWT(privateKey, fmt.Sprintf("%d", authenticaterequest.Accountid), authenticaterequest.Username, "user")
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
apiresponse := libshared.NewAPIResponse("fail", "Failed to create JWT", AuthenticateResponse{})
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(apiresponse)
return
}
apiresponse := libshared.NewAPIResponse("success", "Authentication successful", AuthenticateResponse{Token: token})
w.WriteHeader(http.StatusOK)
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(apiresponse)
}