// SPDX-License-Identifier: GPL-2.0-or-later // Copyright (C) 2026 Bryan Joshua Pedini package main import ( "context" "errors" "log/slog" "net/http" "strings" ) // Authentication result classes; these fixed strings are the only authentication detail ever logged const ( authMissingHeader = "missing_header" authEmptyHeader = "empty_header" authMultipleHeaders = "multiple_headers" authBadScheme = "bad_scheme" authEmptyToken = "empty_token" authUnknownToken = "unknown_token" authInactiveToken = "inactive_token" authLookupError = "lookup_error" authOK = "ok" ) // TokenLookup is the token-store dependency of the HTTP layer type TokenLookup interface { GetToken(ctx context.Context, value string) (Token, error) } // parseAuthorization extracts the bearer token from the request headers // It returns the token and an empty class on success, or an empty token and the failure class // Accepted form: exactly one Authorization header, scheme "Bearer" (ASCII case-insensitive), one space, token // The token is taken byte-exact: no trimming or normalization is applied func parseAuthorization(h http.Header) (string, string) { values := h.Values("Authorization") switch { case len(values) == 0: return "", authMissingHeader case len(values) > 1: return "", authMultipleHeaders case values[0] == "": return "", authEmptyHeader } // Split " " at the first space scheme, token, found := strings.Cut(values[0], " ") if !asciiEqualFold(scheme, "Bearer") { return "", authBadScheme } if !found || token == "" { return "", authEmptyToken } return token, "" } // authenticate validates the request credentials against the token store // It returns the authenticated token, the result class, and a non-nil error only when the lookup itself failed func authenticate(ctx context.Context, log *slog.Logger, tokens TokenLookup, h http.Header) (Token, string, error) { defer traceFunc(ctx, log, "authenticate")() // Parse the header value, class := parseAuthorization(h) if class != "" { return Token{}, class, nil } // Look the token up tok, err := tokens.GetToken(ctx, value) if errors.Is(err, ErrTokenNotFound) { return Token{}, authUnknownToken, nil } if err != nil { return Token{}, authLookupError, err } if !tok.Active { return Token{}, authInactiveToken, nil } return tok, authOK, nil }