multica-ai/multica · error

missing authorization

Error message

missing authorization

What it means

HTTP 401 from the auth middleware when extractToken finds no bearer token and no auth cookie on the request. This is the first gate of the authentication chain: every subsequent branch (mat_ task tokens, mcn_ cloud PATs, mul_ PATs, JWTs) requires a non-empty token string, so an absent token fails here with 'missing authorization'. The path is logged at Debug level.

Source

Thrown at server/internal/middleware/auth.go:52

// local DB. When nil (Fleet URL unset) mcn_ tokens are rejected at the
// prefix branch — we don't fall through to the mul_ / JWT paths, since
// an mcn_ string is by construction not a valid mul_ PAT or JWT.
func Auth(queries *db.Queries, patCache *auth.PATCache, cloudPAT *auth.CloudPATVerifier) func(http.Handler) http.Handler {
	return func(next http.Handler) http.Handler {
		return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			// X-Actor-Source is server-set only — any value supplied by
			// the client is untrusted and discarded before the auth
			// branches run. Only the mat_ branch below re-sets it. This
			// is what prevents a client from sending a normal mul_ PAT
			// plus a forged `X-Actor-Source: member` (or anything else)
			// to convince a downstream handler that its request came
			// from a non-task-token path.
			r.Header.Del("X-Actor-Source")

			tokenString, fromCookie := extractToken(r)
			if tokenString == "" {
				slog.Debug("auth: no token found", "path", r.URL.Path)
				http.Error(w, `{"error":"missing authorization"}`, http.StatusUnauthorized)
				return
			}

			// Cookie-based auth requires CSRF validation for state-changing methods.
			if fromCookie && !auth.ValidateCSRF(r) {
				slog.Debug("auth: CSRF validation failed", "path", r.URL.Path)
				http.Error(w, `{"error":"CSRF validation failed"}`, http.StatusForbidden)
				return
			}

			// Agent task token: "mat_" prefix. Minted by the server at
			// task-claim time and injected by the daemon into the agent
			// process. Authoritative for actor identity — the bound
			// (user_id, agent_id, task_id, workspace_id) triple is
			// written into request headers here, OVERRIDING whatever the
			// client sent, so a downstream actor-resolver cannot be
			// tricked by a client that strips or forges X-Agent-ID /
			// X-Task-ID. Human-only endpoints (e.g. agent env

View on GitHub (pinned to 2c0912b6ec)

Solutions

  1. Attach a valid token: Authorization: Bearer <PAT or mat_ task token> or the auth cookie.
  2. If using cookie auth in a browser, log in again to obtain a fresh session cookie.
  3. Verify no intermediate proxy or load balancer strips the Authorization header.
  4. Check the client's token-loading code actually reads the env/config where the token is stored.

Example fix

# before
curl :8080/api/issues
# → 401 {"error":"missing authorization"}

# after
curl :8080/api/issues -H "Authorization: Bearer $PAT"
Defensive patterns

Strategy: validation

Validate before calling

if strings.TrimSpace(token) == "" {
    return errors.New("no token configured; set PAT env var")
}
req.Header.Set("Authorization", "Bearer "+token)

Type guard

func hasToken(tok string) bool { return strings.TrimSpace(tok) != "" }

Try / catch

resp, err := client.Do(req)
if err == nil && resp.StatusCode == 401 {
    // 401 missing authorization: load/refresh credentials, retry once; else prompt login
}

Prevention

When it happens

Trigger: Calling any API endpoint behind the auth middleware without an Authorization: Bearer <token> header and without the auth cookie; sending 'Bearer' with an empty value; a client that stores the token in a differently-named header.

Common situations: Forgot to attach the PAT in a curl/script; browser session cookie expired and was cleared; proxy strips the Authorization header; frontend not yet wired to send credentials.

Related errors


AI-assisted analysis of multica-ai/multica@2c0912b6ec (2026-08-15). Data as JSON: /api/errors/6620cda7b81d8393. Report an issue: GitHub.