Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 6 additions & 16 deletions app/artifact-cas/internal/server/grpc.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ import (
"github.com/go-kratos/kratos/v2/errors"
jwtMiddleware "github.com/go-kratos/kratos/v2/middleware/auth/jwt"
"github.com/go-kratos/kratos/v2/middleware/selector"
jwt "github.com/golang-jwt/jwt/v4"
jwt "github.com/golang-jwt/jwt/v5"
"github.com/prometheus/client_golang/prometheus"
"google.golang.org/genproto/googleapis/bytestream"

Expand Down Expand Up @@ -194,24 +194,14 @@ func verifyAndMarshalJWT(token string, keyFunc jwt.Keyfunc, signingMethod jwt.Si

tokenInfo, err := jwt.ParseWithClaims(token, claims, keyFunc)
if err != nil {
var ve *jwt.ValidationError
if !errors.As(err, &ve) {
return nil, errors.Unauthorized("UNAUTHORIZED", err.Error())
}

if ve.Errors&jwt.ValidationErrorMalformed != 0 {
switch {
case errors.Is(err, jwt.ErrTokenMalformed):
return nil, jwtMiddleware.ErrTokenInvalid
}

if ve.Errors&(jwt.ValidationErrorExpired) != 0 {
return nil, jwtMiddleware.ErrTokenExpired
}

if ve.Errors&(jwt.ValidationErrorNotValidYet) != 0 {
case errors.Is(err, jwt.ErrTokenExpired), errors.Is(err, jwt.ErrTokenNotValidYet):
return nil, jwtMiddleware.ErrTokenExpired
default:
return nil, errors.Unauthorized("UNAUTHORIZED", err.Error())
}

return nil, err
}

if !tokenInfo.Valid {
Expand Down
6 changes: 3 additions & 3 deletions app/artifact-cas/internal/server/grpc_test.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
//
// Copyright 2023 The Chainloop Authors.
// Copyright 2023-2026 The Chainloop Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
Expand All @@ -24,7 +24,7 @@ import (

robotaccount "github.com/chainloop-dev/chainloop/internal/robotaccount/cas"
jwtMiddleware "github.com/go-kratos/kratos/v2/middleware/auth/jwt"
jwt "github.com/golang-jwt/jwt/v4"
jwt "github.com/golang-jwt/jwt/v5"
"github.com/grpc-ecosystem/go-grpc-middleware/util/metautils"
"github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors"
"github.com/stretchr/testify/assert"
Expand Down Expand Up @@ -126,7 +126,7 @@ func TestJWTAuthFunc(t *testing.T) {
assert.NoError(t, err)
// Validate and extract the claims
claims := infoFromAuth(ctx, t)
assert.NoError(t, claims.Valid())
assert.NoError(t, claims.Validate())
assert.Equal(t, "secret-id", claims.StoredSecretID)
assert.Equal(t, "backend-type", claims.BackendType)
assert.Equal(t, robotaccount.Downloader, claims.Role)
Expand Down
2 changes: 1 addition & 1 deletion app/artifact-cas/internal/server/http.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ import (
"github.com/go-kratos/kratos/v2/middleware/logging"
"github.com/go-kratos/kratos/v2/middleware/recovery"
"github.com/go-kratos/kratos/v2/transport/http"
"github.com/golang-jwt/jwt/v4"
"github.com/golang-jwt/jwt/v5"
)

// NewHTTPServer new a HTTP server.
Expand Down
2 changes: 1 addition & 1 deletion app/cli/cmd/auth_login.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ import (

pb "github.com/chainloop-dev/chainloop/app/controlplane/api/controlplane/v1"
"github.com/chainloop-dev/chainloop/internal/oauth"
jwt "github.com/golang-jwt/jwt/v4"
jwt "github.com/golang-jwt/jwt/v5"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)
Expand Down
2 changes: 1 addition & 1 deletion app/cli/internal/token/token.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ package token

import (
v1 "github.com/chainloop-dev/chainloop/pkg/attestation/crafter/api/attestation/v1"
"github.com/golang-jwt/jwt/v4"
"github.com/golang-jwt/jwt/v5"
)

const (
Expand Down
4 changes: 2 additions & 2 deletions app/cli/internal/token/token_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,15 +55,15 @@ func TestParse(t *testing.T) {
},
{
name: "federated github token",
token: "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJodHRwczovL3Rva2VuLmFjdGlvbnMuZ2l0aHVidXNlcmNvbnRlbnQuY29tIiwiYXVkIjoiY2hhaW5sb29wIiwicmVwb3NpdG9yeSI6Im1hdGlhc2luc2F1cnJhbGRlL3Byb2plY3QiLCJzdWIiOiJyZXBvOm1hdGlhc2luc2F1cnJhbGRlL3Byb2plY3Q6cmVmOnJlZnMvaGVhZHMvbWFpbiJ9.signature",
token: "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJodHRwczovL3Rva2VuLmFjdGlvbnMuZ2l0aHVidXNlcmNvbnRlbnQuY29tIiwiYXVkIjoiY2hhaW5sb29wIiwicmVwb3NpdG9yeSI6Im1hdGlhc2luc2F1cnJhbGRlL3Byb2plY3QiLCJzdWIiOiJyZXBvOm1hdGlhc2luc2F1cnJhbGRlL3Byb2plY3Q6cmVmOnJlZnMvaGVhZHMvbWFpbiJ9.c2lnbmF0dXJl",
want: &ParsedToken{
ID: "https://token.actions.githubusercontent.com",
TokenType: v1.Attestation_Auth_AUTH_TYPE_FEDERATED,
},
},
{
name: "federated token without issuer",
token: "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJjaGFpbmxvb3AifQ.signature",
token: "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJjaGFpbmxvb3AifQ.c2lnbmF0dXJl",
},
{
name: "old api token (without orgID)",
Expand Down
2 changes: 1 addition & 1 deletion app/controlplane/cmd/wire.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ import (
"github.com/chainloop-dev/chainloop/pkg/credentials"
"github.com/chainloop-dev/chainloop/pkg/natsconn"
"github.com/go-kratos/kratos/v2/log"
"github.com/golang-jwt/jwt/v4"
"github.com/golang-jwt/jwt/v5"
"github.com/google/wire"
)

Expand Down
2 changes: 1 addition & 1 deletion app/controlplane/cmd/wire_gen.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion app/controlplane/internal/server/grpc.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ import (
"github.com/chainloop-dev/chainloop/app/controlplane/internal/usercontext"
"github.com/chainloop-dev/chainloop/pkg/credentials"
"github.com/getsentry/sentry-go"
"github.com/golang-jwt/jwt/v4"
"github.com/golang-jwt/jwt/v5"

"github.com/go-kratos/kratos/v2/errors"
"github.com/go-kratos/kratos/v2/log"
Expand Down
2 changes: 1 addition & 1 deletion app/controlplane/internal/server/http.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import (

"github.com/chainloop-dev/chainloop/app/controlplane/pkg/jwt/apitoken"
middlewares_http "github.com/chainloop-dev/chainloop/pkg/middlewares/http"
"github.com/golang-jwt/jwt/v4"
"github.com/golang-jwt/jwt/v5"

"buf.build/go/protovalidate"
v1 "github.com/chainloop-dev/chainloop/app/controlplane/api/controlplane/v1"
Expand Down
6 changes: 3 additions & 3 deletions app/controlplane/internal/usercontext/apitoken_middleware.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import (
"fmt"

"github.com/go-kratos/kratos/v2/log"
"github.com/golang-jwt/jwt/v4"
"github.com/golang-jwt/jwt/v5"

"github.com/chainloop-dev/chainloop/app/controlplane/internal/usercontext/attjwtmiddleware"
"github.com/chainloop-dev/chainloop/app/controlplane/internal/usercontext/entities"
Expand Down Expand Up @@ -70,7 +70,7 @@ func WithCurrentAPITokenAndOrgMiddleware(apiTokenUC *biz.APITokenUseCase, orgUC
}

// We've received an API-token
if genericClaims.VerifyAudience(apitoken.Audience, true) {
if claimsHaveAudience(genericClaims, apitoken.Audience) {
var err error
tokenID, ok := genericClaims["jti"].(string)
if !ok || tokenID == "" {
Expand Down Expand Up @@ -120,7 +120,7 @@ func WithAttestationContextFromAPIToken(apiTokenUC *biz.APITokenUseCase, orgUC *
}

// We've received an API-token, double check its audience
if !claims.VerifyAudience(apitoken.Audience, true) {
if !claimsHaveAudience(claims, apitoken.Audience) {
return nil, errors.New("unexpected token, invalid audience")
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ import (
"github.com/chainloop-dev/chainloop/app/controlplane/pkg/jwt/apitoken"
"github.com/go-kratos/kratos/v2/log"
jwtmiddleware "github.com/go-kratos/kratos/v2/middleware/auth/jwt"
"github.com/golang-jwt/jwt/v4"
"github.com/golang-jwt/jwt/v5"
"github.com/google/uuid"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ import (
"github.com/go-kratos/kratos/v2/middleware"
"github.com/go-kratos/kratos/v2/transport"

"github.com/golang-jwt/jwt/v4"
"github.com/golang-jwt/jwt/v5"
)

const (
Expand Down Expand Up @@ -98,7 +98,7 @@ func NewRobotAccountProvider(signingSecret string) JWTOption {
return false
}
for _, aud := range []string{robotaccount.Audience, robotaccount.DeprecatedAudience} {
if claims.VerifyAudience(aud, true) {
if claimsHaveAudience(claims, aud) {
return true
}
}
Expand All @@ -123,7 +123,7 @@ func NewAPITokenProvider(signingSecret string) JWTOption {
return false
}

return claims.VerifyAudience(apitoken.Audience, true)
return claimsHaveAudience(claims, apitoken.Audience)
}),
WithSigningMethod(user.SigningMethod),
WithKeyFunc(func(_ *jwt.Token) (interface{}, error) {
Expand All @@ -141,7 +141,7 @@ func NewUserTokenProvider(signingSecret string) JWTOption {
return false
}

return genericClaims.VerifyAudience(user.Audience, true)
return claimsHaveAudience(genericClaims, user.Audience)
}),
WithSigningMethod(user.SigningMethod),
WithKeyFunc(func(_ *jwt.Token) (interface{}, error) {
Expand Down Expand Up @@ -427,21 +427,14 @@ func runProviderValidator(provider providerOption, jwtToken string) (*jwt.Token,
tokenInfo, err = jwt.ParseWithClaims(jwtToken, claimsFunc(), provider.keyFunc)

if err != nil {
var ve *jwt.ValidationError
ok := errors.As(err, &ve)
if !ok {
return nil, errorsAPI.Unauthorized(reason, err.Error())
}
if ve.Errors&jwt.ValidationErrorMalformed != 0 {
switch {
case errors.Is(err, jwt.ErrTokenMalformed):
return nil, ErrTokenInvalid
}
if ve.Errors&(jwt.ValidationErrorExpired|jwt.ValidationErrorNotValidYet) != 0 {
case errors.Is(err, jwt.ErrTokenExpired), errors.Is(err, jwt.ErrTokenNotValidYet):
return nil, ErrTokenExpired
default:
return nil, errorsAPI.Unauthorized(reason, err.Error())
}
if ve.Inner != nil {
return nil, ve.Inner
}
return nil, ErrTokenParseFail
}
if !tokenInfo.Valid {
return nil, ErrTokenInvalid
Expand Down Expand Up @@ -470,3 +463,21 @@ func FromJWTAuthContext(ctx context.Context) (authContext JWTAuthContext, ok boo
authContext, ok = ctx.Value(authzContextKey{}).(JWTAuthContext)
return
}

// claimsHaveAudience reports whether the JWT claims include the expected
// audience. It replaces the Claims.VerifyAudience helper that golang-jwt
// removed in v5.
func claimsHaveAudience(claims jwt.Claims, expected string) bool {
audiences, err := claims.GetAudience()
if err != nil {
return false
}

for _, aud := range audiences {
if aud == expected {
return true
}
}

return false
}
22 changes: 20 additions & 2 deletions app/controlplane/internal/usercontext/currentuser_middleware.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import (
"fmt"

"github.com/go-kratos/kratos/v2/log"
"github.com/golang-jwt/jwt/v4"
"github.com/golang-jwt/jwt/v5"

"github.com/chainloop-dev/chainloop/app/controlplane/internal/usercontext/attjwtmiddleware"
"github.com/chainloop-dev/chainloop/app/controlplane/internal/usercontext/entities"
Expand Down Expand Up @@ -56,7 +56,7 @@ func WithCurrentUserMiddleware(userUseCase biz.UserOrgFinder, logger *log.Helper

// Check wether the token is for a user or an API-token and handle accordingly
// We've received a token for a user
if genericClaims.VerifyAudience(user.Audience, true) {
if claimsHaveAudience(genericClaims, user.Audience) {
userID, ok := genericClaims["user_id"].(string)
if !ok || userID == "" {
return nil, errors.New("error mapping the user claims")
Expand Down Expand Up @@ -148,3 +148,21 @@ func WithAttestationContextFromUser(userUC *biz.UserUseCase, orgUC *biz.Organiza
}
}
}

// claimsHaveAudience reports whether the JWT claims include the expected
// audience. It replaces the Claims.VerifyAudience helper that golang-jwt
// removed in v5.
func claimsHaveAudience(claims jwt.Claims, expected string) bool {
audiences, err := claims.GetAudience()
if err != nil {
return false
}

for _, aud := range audiences {
if aud == expected {
return true
}
}

return false
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import (
userjwtbuilder "github.com/chainloop-dev/chainloop/app/controlplane/pkg/jwt/user"
"github.com/go-kratos/kratos/v2/log"
jwtmiddleware "github.com/go-kratos/kratos/v2/middleware/auth/jwt"
"github.com/golang-jwt/jwt/v4"
"github.com/golang-jwt/jwt/v5"
"github.com/google/uuid"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
Expand Down
4 changes: 2 additions & 2 deletions app/controlplane/internal/usercontext/federated_middleware.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
//
// Copyright 2025 The Chainloop Authors.
// Copyright 2025-2026 The Chainloop Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
Expand All @@ -21,7 +21,7 @@ import (
"fmt"

"github.com/go-kratos/kratos/v2/log"
"github.com/golang-jwt/jwt/v4"
"github.com/golang-jwt/jwt/v5"

"github.com/chainloop-dev/chainloop/app/controlplane/internal/usercontext/attjwtmiddleware"
"github.com/chainloop-dev/chainloop/app/controlplane/internal/usercontext/entities"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
//
// Copyright 2023 The Chainloop Authors.
// Copyright 2023-2026 The Chainloop Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -70,7 +70,7 @@ func WithAttestationContextFromRobotAccount(robotAccountUseCase *biz.RobotAccoun

// Do not accept tokens that are crafted for a different audience in this system
// NOTE: we allow deprecated audience to not to break compatibility with previously issued robot-accounts
if !claims.VerifyAudience(robotaccount.Audience, true) && !claims.VerifyAudience(robotaccount.DeprecatedAudience, true) {
if !claimsHaveAudience(claims, robotaccount.Audience) && !claimsHaveAudience(claims, robotaccount.DeprecatedAudience) {
return nil, errors.New("unexpected token, invalid audience")
}

Expand Down
2 changes: 1 addition & 1 deletion app/controlplane/pkg/biz/apitoken_integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import (
"github.com/chainloop-dev/chainloop/app/controlplane/pkg/authz"
"github.com/chainloop-dev/chainloop/app/controlplane/pkg/biz"
"github.com/chainloop-dev/chainloop/app/controlplane/pkg/biz/testhelpers"
"github.com/golang-jwt/jwt/v4"
"github.com/golang-jwt/jwt/v5"
"github.com/google/uuid"

"github.com/stretchr/testify/assert"
Expand Down
2 changes: 1 addition & 1 deletion app/controlplane/pkg/jwt/apitoken/apitoken.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import (
"errors"
"time"

"github.com/golang-jwt/jwt/v4"
"github.com/golang-jwt/jwt/v5"
"github.com/google/uuid"
)

Expand Down
2 changes: 1 addition & 1 deletion app/controlplane/pkg/jwt/apitoken/apitoken_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import (
"testing"
"time"

"github.com/golang-jwt/jwt/v4"
"github.com/golang-jwt/jwt/v5"
"github.com/google/uuid"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
Expand Down
4 changes: 2 additions & 2 deletions app/controlplane/pkg/jwt/robotaccount/robotaccount.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
//
// Copyright 2023 The Chainloop Authors.
// Copyright 2023-2026 The Chainloop Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
Expand All @@ -18,7 +18,7 @@ package robotaccount
import (
"errors"

"github.com/golang-jwt/jwt/v4"
"github.com/golang-jwt/jwt/v5"
)

var SigningMethod = jwt.SigningMethodHS256
Expand Down
Loading
Loading