From a13390aad886e6647713365dd34c20e0fb72cb48 Mon Sep 17 00:00:00 2001
From: ale <ale@incal.net>
Date: Fri, 6 Dec 2024 20:57:12 +0000
Subject: [PATCH] Support bearer tokens for authentication

---
 auth.go | 43 ++++++++++++++++++++++++++++++++++---------
 1 file changed, 34 insertions(+), 9 deletions(-)

diff --git a/auth.go b/auth.go
index 96921b3..e83223b 100644
--- a/auth.go
+++ b/auth.go
@@ -1,21 +1,26 @@
 package main
 
 import (
+	"encoding/base64"
 	"encoding/json"
 	"errors"
 	"flag"
 	"log"
 	"net/http"
 	"os"
+	"strings"
 
 	"git.autistici.org/ai3/go-common/pwhash"
 	"github.com/coreos/go-systemd/v22/daemon"
 )
 
 var (
-	passwordFile = flag.String("passwords", "/etc/nginx-authenticator/users.json", "JSON file with usernames/passwords")
+	passwordFile     = flag.String("passwords", "/etc/nginx-authenticator/users.json", "JSON file with usernames/passwords")
+	enableBasicAuth  = flag.Bool("enable-basic-auth", true, "enable Basic authentication")
+	enableBearerAuth = flag.Bool("enable-bearer-token-auth", true, "enable bearer token authentication")
 
 	passwords = make(map[string]string)
+	tokens    = make(map[string]string)
 )
 
 func authenticate(username, password string) bool {
@@ -29,22 +34,39 @@ func authenticate(username, password string) bool {
 func handleAuthenticate(w http.ResponseWriter, req *http.Request) {
 	status := http.StatusForbidden
 
-	if username, password, ok := req.BasicAuth(); ok {
-		if authenticate(username, password) {
-			status = http.StatusOK
-			log.Printf("auth(%s) -> ok", username)
-		} else {
-			log.Printf("auth(%s) -> ERROR", username)
+	if *enableBasicAuth {
+		if username, password, ok := req.BasicAuth(); ok {
+			if authenticate(username, password) {
+				status = http.StatusOK
+				log.Printf("auth(%s) -> ok", username)
+			} else {
+				log.Printf("auth(%s) -> ERROR", username)
+			}
+			goto done
 		}
 	}
 
+	if *enableBearerAuth {
+		if auth := req.Header.Get("Authorization"); strings.HasPrefix(auth, "Bearer ") {
+			key, _ := base64.StdEncoding.DecodeString(auth)
+			if username, ok := tokens[string(key)]; ok {
+				status = http.StatusOK
+				log.Printf("auth(%s) -> ok", username)
+			} else {
+				log.Printf("auth() -> ERROR")
+			}
+		}
+	}
+
+done:
 	w.WriteHeader(status)
 }
 
 func loadUsers(path string) error {
 	var users []struct {
-		Name     string `json:"name"`
-		Password string `json:"password"`
+		Name     string   `json:"name"`
+		Password string   `json:"password"`
+		Tokens   []string `json:"api_tokens"`
 	}
 	data, err := os.ReadFile(path)
 	if err != nil {
@@ -55,6 +77,9 @@ func loadUsers(path string) error {
 	}
 	for _, u := range users {
 		passwords[u.Name] = u.Password
+		for _, t := range u.Tokens {
+			tokens[t] = u.Name
+		}
 	}
 	return nil
 }
-- 
GitLab