Skip to content

Commit 601bd82

Browse files
eGamesZCode
andcommitted
refactor: rename parseAPIKeyBasicAuth and pin colon-in-password split
Rename parseAPIKeyBasicAuth to parseBasicAuthHeaderValue so the helper name matches the final X-Tinyauth-Authorization header, and add a test pinning that a password containing a colon keeps everything after the first colon, matching http.Request.BasicAuth(). Co-Authored-By: ZCode <noreply@z.ai>
1 parent 5b6a968 commit 601bd82

2 files changed

Lines changed: 23 additions & 3 deletions

File tree

‎internal/middleware/context_middleware.go‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ func (m *ContextMiddleware) Middleware() gin.HandlerFunc {
9696
}
9797

9898
if apiKeyHeaders := c.Request.Header["X-Tinyauth-Authorization"]; len(apiKeyHeaders) > 0 {
99-
username, password, ok := parseAPIKeyBasicAuth(apiKeyHeaders[0])
99+
username, password, ok := parseBasicAuthHeaderValue(apiKeyHeaders[0])
100100
if !ok {
101101
m.log.App.Debug().Msg("Invalid basic auth in X-Tinyauth-Authorization header")
102102
c.AbortWithStatus(http.StatusUnauthorized)
@@ -387,9 +387,9 @@ func (m *ContextMiddleware) tailscaleWhois(ip string) (*model.TailscaleContext,
387387
return &uctx, nil
388388
}
389389

390-
// parseAPIKeyBasicAuth parses an X-Tinyauth-Authorization value in the
390+
// parseBasicAuthHeaderValue parses an X-Tinyauth-Authorization value in the
391391
// form "Basic base64(username:password)".
392-
func parseAPIKeyBasicAuth(header string) (username string, password string, ok bool) {
392+
func parseBasicAuthHeaderValue(header string) (username string, password string, ok bool) {
393393
const prefix = "Basic "
394394

395395
if len(header) < len(prefix) || !strings.EqualFold(header[:len(prefix)], prefix) {

‎internal/middleware/context_middleware_test.go‎

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import (
1818
"github.com/tinyauthapp/tinyauth/internal/service"
1919
"github.com/tinyauthapp/tinyauth/internal/test"
2020
"github.com/tinyauthapp/tinyauth/internal/utils/logger"
21+
"golang.org/x/crypto/bcrypt"
2122
)
2223

2324
func TestContextMiddleware(t *testing.T) {
@@ -26,6 +27,13 @@ func TestContextMiddleware(t *testing.T) {
2627

2728
cfg, runtime := test.CreateTestConfigs(t)
2829

30+
colonPasswd, err := bcrypt.GenerateFromPassword([]byte("pa:ss"), bcrypt.DefaultCost)
31+
require.NoError(t, err)
32+
runtime.LocalUsers = append(runtime.LocalUsers, model.LocalUser{
33+
Username: "colonuser",
34+
Password: string(colonPasswd),
35+
})
36+
2937
basicAuthHeader := func(username, password string) string {
3038
return "Basic " + base64.StdEncoding.EncodeToString([]byte(username+":"+password))
3139
}
@@ -272,6 +280,18 @@ func TestContextMiddleware(t *testing.T) {
272280
assert.True(t, userCtx.Authenticated)
273281
},
274282
},
283+
{
284+
description: "Password containing a colon keeps everything after the first colon",
285+
run: func(t *testing.T, args runArgs) {
286+
req := httptest.NewRequest("GET", "/api/test", nil)
287+
req.Header.Set("X-Tinyauth-Authorization", basicAuthHeader("colonuser", "pa:ss"))
288+
userCtx, _ := args.do(req)
289+
290+
require.NotNil(t, userCtx)
291+
assert.Equal(t, "colonuser", userCtx.GetUsername())
292+
assert.True(t, userCtx.Authenticated)
293+
},
294+
},
275295
{
276296
description: "Malformed header is rejected without fallback to Authorization",
277297
run: func(t *testing.T, args runArgs) {

0 commit comments

Comments
 (0)