fix(auth): don't panic on /token/test with API token

The JWT skipper bypassed validation entirely for /token/test when the
bearer was an API token, leaving "user" unset in the context. CheckToken
then type-asserted it to *jwt.Token and panicked.

Validate the API token in the skipper but skip the route permission
check (since /token/test is not exposed in the API token route registry,
no token can hold explicit permission for it). Drop the now-redundant
JWT assertion in CheckToken — auth has already passed by the time the
handler runs.
This commit is contained in:
kolaente 2026-05-01 11:13:12 +02:00
parent 01fff665c6
commit 3d75ca049b
No known key found for this signature in database
2 changed files with 6 additions and 16 deletions

View File

@ -19,20 +19,13 @@ package v1
import (
"net/http"
"code.vikunja.io/api/pkg/log"
"code.vikunja.io/api/pkg/models"
"github.com/golang-jwt/jwt/v5"
"github.com/labstack/echo/v5"
)
// CheckToken checks prints a message if the token is valid or not. Currently only used for testing purposes.
// CheckToken returns 418 if the bearer token is valid. Used for testing.
func CheckToken(c *echo.Context) error {
user := c.Get("user").(*jwt.Token)
log.Debugf("token valid: %t", user.Valid)
return c.JSON(418, models.Message{Message: "🍵"})
return c.JSON(http.StatusTeapot, models.Message{Message: "🍵"})
}
// TestToken returns a simple test message. Used for testing purposes.

View File

@ -46,11 +46,8 @@ func SetupTokenMiddleware() echo.MiddlewareFunc {
for _, s := range authHeader {
if strings.HasPrefix(s, "Bearer "+models.APITokenPrefix) {
if c.Request().URL.Path == "/api/v1/token/test" {
return true
}
err := checkAPITokenAndPutItInContext(s, c)
skipRouteCheck := c.Request().URL.Path == "/api/v1/token/test"
err := checkAPITokenAndPutItInContext(s, c, skipRouteCheck)
return err == nil
}
}
@ -71,14 +68,14 @@ func SetupTokenMiddleware() echo.MiddlewareFunc {
})
}
func checkAPITokenAndPutItInContext(tokenHeaderValue string, c *echo.Context) error {
func checkAPITokenAndPutItInContext(tokenHeaderValue string, c *echo.Context, skipRouteCheck bool) error {
token, u, err := auth.ValidateAPITokenString(strings.TrimPrefix(tokenHeaderValue, "Bearer "))
if err != nil {
log.Debugf("[auth] API token validation failed: %v", err)
return echo.NewHTTPError(http.StatusUnauthorized, "Unauthorized")
}
if !models.CanDoAPIRoute(c, token) {
if !skipRouteCheck && !models.CanDoAPIRoute(c, token) {
log.Debugf("[auth] Tried authenticating with token %d but it does not have permission to do this route", token.ID)
return echo.NewHTTPError(http.StatusUnauthorized, "Unauthorized")
}