Chore: Update authlib (#110880)

* Chore: Update authlib

* exclude incompatible version of github.com/grafana/gomemcache

* Update go-jose to v4

* fix jose imports

* remove jose v3 from go.mod

* fix tests

* fix serialize

* fix failing live tests

* add v1 of ES256 testkeys. Port tests to use ES256 instead of HS256

* accept more signature algs for okta and azuread

* azure social graph token sig

* accept more signature algs for oauth refresh and jwt auth

* update workspace

* add a static signer for inproc

* rebase and fix ext_jwt

* fix jwt tests

* apply alex patch on gomemcache

* update linting

* fix ext_jwt panic

* update workspaces

---------

Co-authored-by: Jo Garnier <git@jguer.space>
This commit is contained in:
Alexander Zobnin
2025-09-15 12:45:15 +02:00
committed by GitHub
parent 172febd690
commit 294fd943c0
62 changed files with 470 additions and 383 deletions
+3 -2
View File
@@ -6,7 +6,8 @@ import (
"fmt"
"time"
"github.com/go-jose/go-jose/v3/jwt"
jose "github.com/go-jose/go-jose/v4"
"github.com/go-jose/go-jose/v4/jwt"
"github.com/prometheus/client_golang/prometheus"
"go.opentelemetry.io/otel/trace"
"golang.org/x/sync/singleflight"
@@ -170,7 +171,7 @@ func (s *Service) SyncIDToken(ctx context.Context, identity *authn.Identity, _ *
}
func (s *Service) extractTokenClaims(token string) (*authnlib.Claims[authnlib.IDTokenClaims], error) {
parsed, err := jwt.ParseSigned(token)
parsed, err := jwt.ParseSigned(token, []jose.SignatureAlgorithm{jose.ES256})
if err != nil {
s.metrics.failedTokenSigningCounter.Inc()
return nil, err
+35 -6
View File
@@ -2,14 +2,19 @@ package idimpl
import (
"context"
"crypto/ecdsa"
"crypto/x509"
"encoding/pem"
"fmt"
"testing"
"github.com/go-jose/go-jose/v3"
"github.com/go-jose/go-jose/v3/jwt"
"github.com/go-jose/go-jose/v4"
"github.com/go-jose/go-jose/v4/jwt"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
claims "github.com/grafana/authlib/types"
"github.com/grafana/grafana/pkg/infra/remotecache"
"github.com/grafana/grafana/pkg/infra/tracing"
"github.com/grafana/grafana/pkg/services/auth"
@@ -35,14 +40,38 @@ func Test_ProvideService(t *testing.T) {
})
}
var testKey = decodePrivateKey([]byte(`
-----BEGIN EC PRIVATE KEY-----
MHcCAQEEID6lXWsmcv/UWn9SptjOThsy88cifgGIBj2Lu0M9I8tQoAoGCCqGSM49
AwEHoUQDQgAEsf6eNnNMNhl+q7jXsbdUf3ADPh248uoFUSSV9oBzgptyokHCjJz6
n6PKDm2W7i3S2+dAs5M5f3s7d8KiLjGZdQ==
-----END EC PRIVATE KEY-----
`))
func decodePrivateKey(data []byte) *ecdsa.PrivateKey {
block, _ := pem.Decode(data)
if block == nil {
panic("should include PEM block")
}
privateKey, err := x509.ParseECPrivateKey(block.Bytes)
if err != nil {
panic(fmt.Sprintf("should be able to parse ec private key: %v", err))
}
if privateKey.Curve.Params().Name != "P-256" {
panic("should be valid private key")
}
return privateKey
}
func TestService_SignIdentity(t *testing.T) {
signer := &idtest.FakeSigner{
SignIDTokenFn: func(_ context.Context, claims *auth.IDClaims) (string, error) {
key := []byte("key")
s, err := jose.NewSigner(jose.SigningKey{Algorithm: jose.HS256, Key: key}, nil)
s, err := jose.NewSigner(jose.SigningKey{Algorithm: jose.ES256, Key: testKey}, nil)
require.NoError(t, err)
token, err := jwt.Signed(s).Claims(claims.Claims).Claims(claims.Rest).CompactSerialize()
token, err := jwt.Signed(s).Claims(claims.Claims).Claims(claims.Rest).Serialize()
require.NoError(t, err)
return token, nil
@@ -73,7 +102,7 @@ func TestService_SignIdentity(t *testing.T) {
})
require.NoError(t, err)
parsed, err := jwt.ParseSigned(token)
parsed, err := jwt.ParseSigned(token, []jose.SignatureAlgorithm{jose.ES256})
require.NoError(t, err)
gotClaims := &auth.IDClaims{}
+3 -3
View File
@@ -3,8 +3,8 @@ package idimpl
import (
"context"
"github.com/go-jose/go-jose/v3"
"github.com/go-jose/go-jose/v3/jwt"
"github.com/go-jose/go-jose/v4"
"github.com/go-jose/go-jose/v4/jwt"
"github.com/grafana/grafana/pkg/services/auth"
"github.com/grafana/grafana/pkg/services/signingkeys"
@@ -33,7 +33,7 @@ func (s *LocalSigner) SignIDToken(ctx context.Context, claims *auth.IDClaims) (s
builder := jwt.Signed(signer).Claims(&claims.Rest).Claims(claims.Claims)
token, err := builder.CompactSerialize()
token, err := builder.Serialize()
if err != nil {
return "", err
}
+6 -3
View File
@@ -6,7 +6,8 @@ import (
"errors"
"strings"
"github.com/go-jose/go-jose/v3/jwt"
jose "github.com/go-jose/go-jose/v4"
"github.com/go-jose/go-jose/v4/jwt"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/infra/remotecache"
@@ -69,7 +70,8 @@ func (s *AuthService) Verify(ctx context.Context, strToken string) (map[string]a
s.log.Debug("Parsing JSON Web Token")
strToken = sanitizeJWT(strToken)
token, err := jwt.ParseSigned(strToken)
token, err := jwt.ParseSigned(strToken, []jose.SignatureAlgorithm{jose.EdDSA, jose.HS256, jose.HS384,
jose.HS512, jose.RS512, jose.RS256, jose.ES256, jose.ES384, jose.ES512, jose.PS256, jose.PS384, jose.PS512})
if err != nil {
return nil, err
}
@@ -106,7 +108,8 @@ func (s *AuthService) Verify(ctx context.Context, strToken string) (map[string]a
// HasSubClaim checks if the provided JWT token contains a non-empty "sub" claim.
// Returns true if it contains, otherwise returns false.
func HasSubClaim(jwtToken string) bool {
parsed, err := jwt.ParseSigned(sanitizeJWT(jwtToken))
parsed, err := jwt.ParseSigned(sanitizeJWT(jwtToken), []jose.SignatureAlgorithm{jose.EdDSA, jose.HS256, jose.HS384,
jose.HS512, jose.RS512, jose.RS256, jose.ES256, jose.ES384, jose.ES512, jose.PS256, jose.PS384, jose.PS512})
if err != nil {
return false
}
+4 -4
View File
@@ -13,8 +13,8 @@ import (
"testing"
"time"
jose "github.com/go-jose/go-jose/v3"
"github.com/go-jose/go-jose/v3/jwt"
jose "github.com/go-jose/go-jose/v4"
"github.com/go-jose/go-jose/v4/jwt"
"github.com/madflojo/testcerts"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@@ -454,10 +454,10 @@ func TestIntegrationClaimValidation(t *testing.T) {
require.NoError(t, err)
_, err = sc.authJWTSvc.Verify(sc.ctx, sign(t, key, jwt.Claims{Audience: []string{"foo"}}, nil))
require.Error(t, err)
require.NoError(t, err)
_, err = sc.authJWTSvc.Verify(sc.ctx, sign(t, key, jwt.Claims{Audience: []string{"bar", "baz"}}, nil))
require.Error(t, err)
require.NoError(t, err)
_, err = sc.authJWTSvc.Verify(sc.ctx, sign(t, key, jwt.Claims{Audience: []string{"baz"}}, nil))
require.Error(t, err)
+1 -1
View File
@@ -18,7 +18,7 @@ import (
"strings"
"time"
jose "github.com/go-jose/go-jose/v3"
jose "github.com/go-jose/go-jose/v4"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/infra/remotecache"
+1 -1
View File
@@ -6,7 +6,7 @@ import (
"encoding/pem"
"fmt"
"github.com/go-jose/go-jose/v3"
"github.com/go-jose/go-jose/v4"
)
var rsaKeys [3]*rsa.PrivateKey
+5 -5
View File
@@ -3,8 +3,8 @@ package jwt
import (
"testing"
jose "github.com/go-jose/go-jose/v3"
"github.com/go-jose/go-jose/v3/jwt"
jose "github.com/go-jose/go-jose/v4"
"github.com/go-jose/go-jose/v4/jwt"
"github.com/stretchr/testify/require"
)
@@ -16,9 +16,9 @@ func sign(t *testing.T, key any, claims any, opts *jose.SignerOptions) string {
if opts == nil {
opts = &jose.SignerOptions{}
}
sig, err := jose.NewSigner(jose.SigningKey{Algorithm: jose.PS512, Key: key}, (opts).WithType("JWT"))
sig, err := jose.NewSigner(jose.SigningKey{Algorithm: jose.RS256, Key: key}, (opts).WithType("JWT"))
require.NoError(t, err)
token, err := jwt.Signed(sig).Claims(claims).CompactSerialize()
token, err := jwt.Signed(sig).Claims(claims).Serialize()
require.NoError(t, err)
return token
}
@@ -40,7 +40,7 @@ func signNone(t *testing.T, claims any) string {
sig, err := jose.NewSigner(jose.SigningKey{Algorithm: "none", Key: noneSigner{}}, (&jose.SignerOptions{}).WithType("JWT"))
require.NoError(t, err)
token, err := jwt.Signed(sig).Claims(claims).CompactSerialize()
token, err := jwt.Signed(sig).Claims(claims).Serialize()
require.NoError(t, err)
return token
}
+3 -3
View File
@@ -6,7 +6,7 @@ import (
"reflect"
"time"
"github.com/go-jose/go-jose/v3/jwt"
"github.com/go-jose/go-jose/v4/jwt"
)
func (s *AuthService) initClaimExpectations() error {
@@ -35,13 +35,13 @@ func (s *AuthService) initClaimExpectations() error {
case []any:
for _, val := range value {
if v, ok := val.(string); ok {
s.expectRegistered.Audience = append(s.expectRegistered.Audience, v)
s.expectRegistered.AnyAudience = append(s.expectRegistered.AnyAudience, v)
} else {
return fmt.Errorf("%q expectation contains value with invalid type %T, string expected", key, val)
}
}
case string:
s.expectRegistered.Audience = []string{value}
s.expectRegistered.AnyAudience = []string{value}
default:
return fmt.Errorf("%q expectation has invalid type %T, array or string expected", key, value)
}