You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用Auth0与GoFiber时获取解码JWT令牌的异常问题

问题:GoFiber中无法从上下文获取Auth0验证后的JWT Claims

问题背景

  • 前端基于Next.js + Auth0搭建,已完成配置,可正常向后端发送accessToken
  • 后端采用Golang + GoFiber,参照Auth0教程实现JWT验证中间件,令牌验证通过,但在接口中无法获取解析后的Claims
  • 使用adaptor.HTTPMiddleware将net/http风格的Auth0中间件转换为Fiber中间件
  • 在接口处理函数getAll中尝试获取*validator.ValidatedClaims时触发panic:panic: interface conversion: interface {} is nil, not *validator.ValidatedClaims
  • 排查发现Auth0中间件已将验证后的令牌存入net/http的Request上下文,但Fiber上下文无法直接读取该值

Auth0 JWT验证中间件代码

func EnsureValidToken(next http.Handler) http.Handler {
    // EnsureValidToken is a middleware that will check the validity of our JWT.
    err := godotenv.Load()
    if err != nil {
        log.Fatal("Error loading .env file")
    }
    issuerURL, err := url.Parse("https://" + os.Getenv("AUTH0_DOMAIN") + "/")
    if err != nil {
        log.Fatalf("Failed to parse the issuer url: %v", err)
    }

    provider := jwks.NewCachingProvider(issuerURL, 5*time.Minute)

    jwtValidator, err := validator.New(
        provider.KeyFunc,
        validator.RS256,
        issuerURL.String(),
        []string{os.Getenv("AUTH0_AUDIENCE")},
        validator.WithCustomClaims(
            func() validator.CustomClaims {
                return &CustomClaims{}
            },
        ),
        validator.WithAllowedClockSkew(time.Minute),
    )
    if err != nil {
        log.Fatalf("Failed to set up the jwt validator")
    }

    errorHandler := func(w http.ResponseWriter, r *http.Request, err error) {
        log.Printf("Encountered error while validating JWT: %v", err)

        w.Header().Set("Content-Type", "application/json")
        w.WriteHeader(http.StatusUnauthorized)
        w.Write([]byte(`{"message":"Failed to validate JWT."}`))
    }

    middleware := jwtmiddleware.New(
        jwtValidator.ValidateToken,
        jwtmiddleware.WithErrorHandler(errorHandler),
    )

    return middleware.CheckJWT(next)
}

GoFiber主程序及接口处理代码

func main() {
    // 转换net/http中间件为Fiber中间件
    var ensureValidToken = adaptor.HTTPMiddleware(EnsureValidToken)

    app := fiber.New()
    app.Use(cors.New())
    app.Use(logger.New())
    // 全局应用Auth0验证中间件
    app.Use(ensureValidToken)
    app.Get("/api/books", getAll)

    app.Listen(":8080")
}

func getAll(c *fiber.Ctx) error {
       token := c.Context().Value(jwtmiddleware.ContextKey{}).(*validator.ValidatedClaims)

        // 此处触发panic,验证通过但上下文无法获取到Claims
}

Auth0中间件存入上下文的核心代码

r = r.Clone(context.WithValue(r.Context(), ContextKey{}, validToken))

解决方案

问题根源在于Fiber上下文与net/http的Request上下文并非直接互通:adaptor.HTTPMiddleware转换中间件时会创建临时net/http Request对象,Auth0将Claims存入该临时对象的上下文,但Fiber的c.Context()是独立的标准库context.Context,无法读取临时Request上下文的值。

方式1:自定义Fiber中间件(推荐)

放弃net/http适配器,直接基于Fiber中间件模式实现Auth0验证逻辑,验证通过后将Claims存入Fiber的c.Locals():

func EnsureValidTokenFiber(c *fiber.Ctx) error {
    err := godotenv.Load()
    if err != nil {
        log.Fatal("Error loading .env file")
    }
    issuerURL, err := url.Parse("https://" + os.Getenv("AUTH0_DOMAIN") + "/")
    if err != nil {
        log.Fatalf("Failed to parse the issuer url: %v", err)
    }

    provider := jwks.NewCachingProvider(issuerURL, 5*time.Minute)

    jwtValidator, err := validator.New(
        provider.KeyFunc,
        validator.RS256,
        issuerURL.String(),
        []string{os.Getenv("AUTH0_AUDIENCE")},
        validator.WithCustomClaims(
            func() validator.CustomClaims {
                return &CustomClaims{}
            },
        ),
        validator.WithAllowedClockSkew(time.Minute),
    )
    if err != nil {
        log.Fatalf("Failed to set up the jwt validator")
    }

    // 从Fiber请求头提取Bearer Token
    authHeader := c.Get("Authorization")
    if authHeader == "" {
        return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{"message": "Missing Authorization header"})
    }
    tokenStr := strings.TrimPrefix(authHeader, "Bearer ")
    if tokenStr == authHeader {
        return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{"message": "Invalid Authorization header format"})
    }

    // 验证Token
    validatedClaims, err := jwtValidator.ValidateToken(c.Context(), tokenStr)
    if err != nil {
        log.Printf("Encountered error while validating JWT: %v", err)
        return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{"message": "Failed to validate JWT."})
    }

    // 将验证后的Claims存入Fiber本地存储
    c.Locals("validatedClaims", validatedClaims)

    return c.Next()
}

在主程序中使用该自定义中间件:

func main() {
    app := fiber.New()
    app.Use(cors.New())
    app.Use(logger.New())
    // 使用自定义Fiber Auth0中间件
    app.Use(EnsureValidTokenFiber)
    app.Get("/api/books", getAll)

    app.Listen(":8080")
}

func getAll(c *fiber.Ctx) error {
    // 从Fiber本地存储获取Claims
    token := c.Locals("validatedClaims").(*validator.ValidatedClaims)
    // 后续业务逻辑
    return c.JSON(fiber.Map{"claims": token})
}

方式2:通过适配器传递上下文(不推荐)

若坚持使用net/http适配器,可在Auth0中间件中通过Fiber内部关联逻辑传递Claims,但依赖Fiber版本实现,稳定性较差:

func EnsureValidToken(next http.Handler) http.Handler {
    // ... 原Auth0中间件代码 ...

    // 包装Handler,将Claims存入Fiber上下文
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        middleware.CheckJWT(http.HandlerFunc(func(w2 http.ResponseWriter, r2 *http.Request) {
            // 从临时Request上下文获取Claims
            if claims := r2.Context().Value(jwtmiddleware.ContextKey{}); claims != nil {
                // 通过Fiber内部key获取Fiber上下文并存储Claims
                if fiberCtx, ok := r2.Context().Value(fiber.ContextKey).(*fiber.Ctx); ok {
                    fiberCtx.Locals("validatedClaims", claims)
                }
            }
            next.ServeHTTP(w2, r2)
        })).ServeHTTP(w, r)
    })
}

内容的提问来源于stack exchange,提问作者airsoftFreak

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.21 09:15:37