oauth2-proxy/oauth2-proxy · error

Forbidden

Error message

Forbidden

What it means

When a request carries a JWT in the Authorization header but the session cannot be retrieved from it (invalid, expired, or malformed token), and denyInvalidJWTs is enabled, the middleware responds with HTTP 403 Forbidden and stops the request chain. Without the deny flag the request would proceed unauthenticated instead.

Source

Thrown at pkg/middleware/jwt_session.go:56

// will be loaded and the request will be passed to the next handler.
// Or if the JWT is invalid and denyInvalidJWTs, return 403 now.
// If a session was loaded by a previous handler, it will not be replaced.
func (j *jwtSessionLoader) loadSession(next http.Handler) http.Handler {
	return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
		scope := middlewareapi.GetRequestScope(req)
		// If scope is nil, this will panic.
		// A scope should always be injected before this handler is called.
		if scope.Session != nil {
			// The session was already loaded, pass to the next handler
			next.ServeHTTP(rw, req)
			return
		}

		session, err := j.getJwtSession(req)
		if err != nil {
			logger.Errorf("Error retrieving session from token in Authorization header: %v", err)
			if j.denyInvalidJWTs {
				http.Error(rw, http.StatusText(http.StatusForbidden), http.StatusForbidden)
				return
			}
		}

		// Add the session to the scope if it was found
		scope.Session = session
		next.ServeHTTP(rw, req)
	})
}

// getJwtSession loads a session based on a JWT token in the authorization header.
// (see the config options skip-jwt-bearer-tokens, extra-jwt-issuers, and bearer-token-login-fallback)
func (j *jwtSessionLoader) getJwtSession(req *http.Request) (*sessionsapi.SessionState, error) {
	auth := req.Header.Get("Authorization")
	if auth == "" {
		// No auth header provided, so don't attempt to load a session
		return nil, nil
	}

View on GitHub (pinned to 33c2eb92de)

Solutions

  1. Obtain a fresh, valid JWT from the configured issuer and resend with 'Authorization: Bearer <token>'
  2. Verify the JWT's signature key, issuer, and audience match the middleware configuration
  3. Check server logs for 'Error retrieving session from token in Authorization header' to see the underlying cause
  4. If tokens are only sometimes invalid and should not be hard-denied, disable the deny-invalid-JWTs option

Example fix

// client side
// before
Authorization: eyJhbGciOi...
// after
Authorization: Bearer eyJhbGciOi...  // with a valid, unexpired token
Defensive patterns

Strategy: try-catch

Validate before calling

parts := strings.SplitN(authz, " ", 2)
valid := len(parts) == 2 && parts[0] == "Bearer" && len(parts[1]) > 0
if !valid { /* refresh or obtain a token before the request */ }

Type guard

func hasBearerToken(h http.Header) (string, bool) {
    const p = "Bearer "
    v := h.Get("Authorization")
    return strings.TrimPrefix(v, p), strings.HasPrefix(v, p)
}

Try / catch

// parse and validate the JWT client-side before sending:
tok, err := jwt.Parse(token, keyfunc)
if err != nil || !tok.Valid {
    token = refreshToken()
}

Prevention

When it happens

Trigger: Sending a request whose Authorization header JWT fails validation (bad signature, expired, wrong issuer/audience, malformed) while the JWT session middleware is configured with deny invalid JWTs enabled.

Common situations: Expired or rotated signing keys after a key change; clock skew between client and server; token from a different issuer than configured; copy-pasted tokens with extra whitespace or 'Bearer' casing issues.

Understand the failure class

Related errors


AI-assisted analysis of oauth2-proxy/oauth2-proxy@33c2eb92de (2026-09-06). Data as JSON: /api/errors/2e170871312ed94d. Report an issue: GitHub.