---
name: http_session_cookie_middleware
kind: function
lang: go
domain: infra
version: "1.0.0"
purity: impure
signature: "func HTTPSessionCookieMiddleware(cfg SessionCookieConfig) Middleware"
description: "Middleware HTTP que valida sesiones via cookie o header Authorization: Bearer. Inyecta el userID en el contexto si la sesion es valida. Delega sin validar los paths en SkipPaths."
params:
  - name: cfg
    desc: "Configuracion: DB con tabla sessions, nombre de cookie, prefijos a saltarse y clave tipada para el contexto."
output: "Middleware (func(http.Handler) http.Handler) que protege los endpoints no listados en SkipPaths."
tags: [http, auth, session, cookie, middleware, bearer]
uses_functions:
  - session_validate_go_infra
  - http_error_response_go_infra
uses_types:
  - Session_go_infra
returns: []
returns_optional: false
error_type: "error_go_core"
imports:
  - context
  - database/sql
  - net/http
  - strings
tested: true
tests:
  - "sesion valida via cookie deja pasar y expone userID en contexto"
  - "sin cookie ni header devuelve 401"
  - "skip path bypassa sin validar token"
test_file_path: "functions/infra/http_session_cookie_middleware_test.go"
file_path: "functions/infra/http_session_cookie_middleware.go"
---

## Ejemplo

```go
type ctxKey string
const userKey ctxKey = "user_id"

mw := infra.HTTPSessionCookieMiddleware(infra.SessionCookieConfig{
    DB:         db,
    CookieName: "kanban_session",
    SkipPaths:  []string{"/api/auth/", "/health"},
    UserCtxKey: userKey,
})

mux := http.NewServeMux()
mux.Handle("/api/", mw(apiRouter))

// En un handler:
userID, ok := infra.UserIDFromContext(r.Context(), userKey)
```

## Notas

`SessionCookieConfig.UserCtxKey` debe ser una clave tipada propia del caller (no `string`) para evitar colisiones en el contexto. Patron canonico: `type ctxKey string; const userKey ctxKey = "user_id"`.

El helper `UserIDFromContext(ctx, key)` esta en el mismo paquete y hace el type-assert de forma segura retornando `("", false)` si no hay valor o el tipo no coincide.

El orden de extraccion del token es: cookie → `Authorization: Bearer`. Si ninguno esta presente responde 401 con `{"code":"unauthorized","message":"session required"}`.
