mirror of
https://github.com/knadh/listmonk.git
synced 2025-11-07 08:16:09 +08:00
Add API token authentication.
This commit is contained in:
parent
10f1c38996
commit
bf0b500bb0
3 changed files with 37 additions and 3 deletions
12
cmd/users.go
12
cmd/users.go
|
|
@ -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})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue