Add API token authentication.

This commit is contained in:
Kailash Nadh 2024-04-27 23:01:23 +05:30
parent 10f1c38996
commit bf0b500bb0
3 changed files with 37 additions and 3 deletions

View file

@ -4,6 +4,7 @@ import (
"net/http"
"strconv"
"strings"
"time"
"github.com/knadh/listmonk/models"
"github.com/labstack/echo/v4"
@ -176,10 +177,21 @@ func handleLoginUser(c echo.Context) error {
return echo.NewHTTPError(http.StatusBadRequest, app.i18n.Ts("globals.messages.invalidFields", "name", "password"))
}
start := time.Now()
_, err := app.core.LoginUser(u.Username, u.Password)
if err != nil {
return err
}
// While realistically the app will only have a tiny fraction of users and get operations
// on the user table will be instantatneous for IDs that exist or not, always respond after
// a minimum wait of 100ms (which is again, realistically, an order of magnitude or two more
// than what it wouldt take to complete the op) to simulate constant-time-comparison to address
// any possible timing attacks.
if ms := time.Now().Sub(start).Milliseconds(); ms < 100 {
time.Sleep(time.Duration(ms))
}
return c.JSON(http.StatusOK, okResp{true})
}

View file

@ -7,6 +7,7 @@ import (
"fmt"
"io"
"net/http"
"sync"
"github.com/coreos/go-oidc/v3/oidc"
"github.com/labstack/echo/v4"
@ -48,6 +49,9 @@ type Config struct {
}
type Auth struct {
tokens map[string]struct{}
mut sync.RWMutex
cfg oauth2.Config
verifier *oidc.IDTokenVerifier
skipper middleware.Skipper
@ -77,8 +81,26 @@ func New(cfg Config) *Auth {
}
}
// HandleCallback is the HTTP handler that handles the post-OIDC provider redirect callback.
func (o *Auth) HandleCallback(c echo.Context) error {
// SetTokens remembers a list of string API tokens that are used for authenticating
// API queries.
func (o *Auth) SetTokens(tokens []string) {
o.mut.Lock()
defer o.mut.Unlock()
o.tokens = make(map[string]struct{}, len(tokens))
for _, t := range tokens {
o.tokens[t] = struct{}{}
}
}
// CheckToken validates an API token.
func (o *Auth) CheckToken(token string) bool {
_, ok := o.tokens[token]
return ok
}
// HandleOIDCCallback is the HTTP handler that handles the post-OIDC provider redirect callback.
func (o *Auth) HandleOIDCCallback(c echo.Context) error {
tk, err := o.cfg.Exchange(c.Request().Context(), c.Request().URL.Query().Get("code"))
if err != nil {
return echo.NewHTTPError(http.StatusUnauthorized, fmt.Sprintf("error exchanging token: %v", err))

View file

@ -1051,6 +1051,6 @@ SELECT * FROM users WHERE $1=0 OR id=$1 ORDER BY created_at;
-- name: login-user
WITH u AS (
SELECT * FROM users WHERE username=$1 AND status = 'enabled' AND password_login = TRUE
SELECT * FROM users WHERE username=$1 AND status != 'disabled' AND password_login = TRUE
)
SELECT * FROM u WHERE CRYPT($2, password) = password;