Migrate to github.com/golang-jwt/jwt/v5

This commit is contained in:
Joachim Bauch 2024-11-05 09:59:14 +01:00
commit e152b8dcda
No known key found for this signature in database
GPG key ID: 77C1D22D53E15F02
14 changed files with 78 additions and 51 deletions

View file

@ -26,7 +26,7 @@ import (
"fmt"
"net/url"
"github.com/golang-jwt/jwt/v4"
"github.com/golang-jwt/jwt/v5"
)
type ProxyClientMessage struct {

View file

@ -31,7 +31,7 @@ import (
"strings"
"time"
"github.com/golang-jwt/jwt/v4"
"github.com/golang-jwt/jwt/v5"
"github.com/pion/sdp/v3"
)
@ -394,11 +394,9 @@ func (p *HelloV2AuthParams) CheckValid() error {
}
type AuthTokenClaims interface {
TokenSubject() string
TokenUserData() json.RawMessage
jwt.Claims
VerifyIssuedAt(cmp time.Time, req bool) bool
VerifyExpiresAt(cmp time.Time, req bool) bool
GetUserData() json.RawMessage
}
type HelloV2TokenClaims struct {
@ -407,11 +405,7 @@ type HelloV2TokenClaims struct {
UserData json.RawMessage `json:"userdata,omitempty"`
}
func (c *HelloV2TokenClaims) TokenSubject() string {
return c.Subject
}
func (c *HelloV2TokenClaims) TokenUserData() json.RawMessage {
func (c *HelloV2TokenClaims) GetUserData() json.RawMessage {
return c.UserData
}
@ -432,11 +426,7 @@ type FederationTokenClaims struct {
UserData json.RawMessage `json:"userdata,omitempty"`
}
func (c *FederationTokenClaims) TokenSubject() string {
return c.Subject
}
func (c *FederationTokenClaims) TokenUserData() json.RawMessage {
func (c *FederationTokenClaims) GetUserData() json.RawMessage {
return c.UserData
}

3
go.mod
View file

@ -5,7 +5,7 @@ go 1.21.0
require (
github.com/dlintw/goconf v0.0.0-20120228082610-dcc070983490
github.com/fsnotify/fsnotify v1.8.0
github.com/golang-jwt/jwt/v4 v4.5.1
github.com/golang-jwt/jwt/v5 v5.2.1
github.com/golang/protobuf v1.5.4
github.com/google/uuid v1.6.0
github.com/gorilla/mux v1.8.1
@ -41,6 +41,7 @@ require (
github.com/go-logr/logr v1.3.0 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang-jwt/jwt/v4 v4.5.1 // indirect
github.com/google/btree v1.0.1 // indirect
github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 // indirect
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 // indirect

2
go.sum
View file

@ -53,6 +53,8 @@ github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/golang-jwt/jwt/v4 v4.5.1 h1:JdqV9zKUdtaa9gdPlywC3aeoEsR681PlKC+4F5gQgeo=
github.com/golang-jwt/jwt/v4 v4.5.1/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk=
github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/glog v1.2.2 h1:1+mZ9upx1Dh6FmUTFR1naJ77miKiXgALjWOZ3NVFPmY=
github.com/golang/glog v1.2.2/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w=

53
hub.go
View file

@ -47,7 +47,7 @@ import (
"time"
"github.com/dlintw/goconf"
"github.com/golang-jwt/jwt/v4"
"github.com/golang-jwt/jwt/v5"
"github.com/gorilla/mux"
"github.com/gorilla/websocket"
"google.golang.org/protobuf/types/known/timestamppb"
@ -111,6 +111,9 @@ var (
// Delay after which a "cleared" / "rejected" dialout status should be removed.
removeCallStatusTTL = 5 * time.Second
// Allow time differences of up to one minute between server and proxy.
tokenLeeway = time.Minute
DefaultTrustedProxies = DefaultPrivateIps()
)
@ -1338,15 +1341,20 @@ func (h *Hub) processHelloV2(ctx context.Context, client HandlerClient, message
}
return key, nil
})
}, jwt.WithValidMethods([]string{
jwt.SigningMethodRS256.Alg(),
jwt.SigningMethodRS384.Alg(),
jwt.SigningMethodRS512.Alg(),
jwt.SigningMethodES256.Alg(),
jwt.SigningMethodES384.Alg(),
jwt.SigningMethodES512.Alg(),
jwt.SigningMethodEdDSA.Alg(),
}), jwt.WithIssuedAt(), jwt.WithLeeway(tokenLeeway))
if err != nil {
if err, ok := err.(*jwt.ValidationError); ok {
if err.Errors&jwt.ValidationErrorIssuedAt == jwt.ValidationErrorIssuedAt {
return nil, nil, TokenNotValidYet
}
if err.Errors&jwt.ValidationErrorExpired == jwt.ValidationErrorExpired {
return nil, nil, TokenExpired
}
if errors.Is(err, jwt.ErrTokenNotValidYet) || errors.Is(err, jwt.ErrTokenUsedBeforeIssued) {
return nil, nil, TokenNotValidYet
} else if errors.Is(err, jwt.ErrTokenExpired) {
return nil, nil, TokenExpired
}
return nil, nil, InvalidToken
@ -1367,20 +1375,35 @@ func (h *Hub) processHelloV2(ctx context.Context, client HandlerClient, message
}
authTokenClaims = claims
}
now := time.Now()
if !authTokenClaims.VerifyIssuedAt(now, true) {
return nil, nil, TokenNotValidYet
issuedAt, err := authTokenClaims.GetIssuedAt()
if err != nil {
return nil, nil, InvalidToken
}
if !authTokenClaims.VerifyExpiresAt(now, true) {
expiresAt, err := authTokenClaims.GetExpirationTime()
if err != nil {
return nil, nil, InvalidToken
}
now := time.Now()
if issuedAt != nil && expiresAt != nil && expiresAt.Before(issuedAt.Time) {
return nil, nil, TokenExpired
} else if issuedAt == nil {
return nil, nil, TokenNotValidYet
} else if minExpiresAt := now.Add(-tokenLeeway); expiresAt == nil || expiresAt.Before(minExpiresAt) {
return nil, nil, TokenExpired
}
subject, err := authTokenClaims.GetSubject()
if err != nil {
return nil, nil, InvalidToken
}
auth := &BackendClientResponse{
Type: "auth",
Auth: &BackendClientAuthResponse{
Version: message.Hello.Version,
UserId: authTokenClaims.TokenSubject(),
User: authTokenClaims.TokenUserData(),
UserId: subject,
User: authTokenClaims.GetUserData(),
},
}
return backend, auth, nil

View file

@ -44,7 +44,7 @@ import (
"time"
"github.com/dlintw/goconf"
"github.com/golang-jwt/jwt/v4"
"github.com/golang-jwt/jwt/v5"
"github.com/gorilla/mux"
"github.com/gorilla/websocket"
"github.com/stretchr/testify/assert"
@ -934,7 +934,7 @@ func TestClientHelloV2_IssuedInFuture(t *testing.T) {
client := NewTestClient(t, server, hub)
defer client.CloseWithBye()
issuedAt := time.Now().Add(time.Minute)
issuedAt := time.Now().Add(tokenLeeway * 2)
expiresAt := issuedAt.Add(time.Second)
require.NoError(client.SendHelloV2WithTimes(testDefaultUserId, issuedAt, expiresAt))
@ -962,8 +962,9 @@ func TestClientHelloV2_Expired(t *testing.T) {
client := NewTestClient(t, server, hub)
defer client.CloseWithBye()
issuedAt := time.Now().Add(-time.Minute)
require.NoError(client.SendHelloV2WithTimes(testDefaultUserId, issuedAt, issuedAt.Add(time.Second)))
issuedAt := time.Now().Add(-tokenLeeway * 3)
expiresAt := time.Now().Add(-tokenLeeway * 2)
require.NoError(client.SendHelloV2WithTimes(testDefaultUserId, issuedAt, expiresAt))
ctx, cancel := context.WithTimeout(context.Background(), testTimeout)
defer cancel()
@ -1017,7 +1018,7 @@ func TestClientHelloV2_ExpiresAtMissing(t *testing.T) {
client := NewTestClient(t, server, hub)
defer client.CloseWithBye()
issuedAt := time.Now().Add(-time.Minute)
issuedAt := time.Now()
var expiresAt time.Time
require.NoError(client.SendHelloV2WithTimes(testDefaultUserId, issuedAt, expiresAt))

View file

@ -42,7 +42,7 @@ import (
"time"
"github.com/dlintw/goconf"
"github.com/golang-jwt/jwt/v4"
"github.com/golang-jwt/jwt/v5"
"github.com/gorilla/websocket"
)

View file

@ -35,7 +35,7 @@ import (
"sync/atomic"
"time"
"github.com/golang-jwt/jwt/v4"
"github.com/golang-jwt/jwt/v5"
"github.com/gorilla/websocket"
signaling "github.com/strukturag/nextcloud-spreed-signaling"

View file

@ -43,7 +43,7 @@ import (
"time"
"github.com/dlintw/goconf"
"github.com/golang-jwt/jwt/v4"
"github.com/golang-jwt/jwt/v5"
"github.com/google/uuid"
"github.com/gorilla/mux"
"github.com/gorilla/websocket"
@ -71,6 +71,9 @@ const (
// Maximum age a token may have to prevent reuse of old tokens.
maxTokenAge = 5 * time.Minute
// Allow time differences of up to one minute between server and proxy.
tokenLeeway = time.Minute
remotePublisherTimeout = 5 * time.Second
ProxyFeatureRemoteStreams = "remote-streams"
@ -1296,13 +1299,18 @@ func (s *ProxyServer) parseToken(tokenValue string) (*signaling.TokenClaims, str
}
return tokenKey.key, nil
})
if err, ok := err.(*jwt.ValidationError); ok {
if err.Errors&jwt.ValidationErrorIssuedAt == jwt.ValidationErrorIssuedAt {
return nil, "not-valid-yet", TokenNotValidYet
}
}
}, jwt.WithValidMethods([]string{
jwt.SigningMethodRS256.Alg(),
jwt.SigningMethodRS384.Alg(),
jwt.SigningMethodRS512.Alg(),
}), jwt.WithIssuedAt(), jwt.WithLeeway(tokenLeeway))
if err != nil {
if errors.Is(err, jwt.ErrTokenNotValidYet) || errors.Is(err, jwt.ErrTokenUsedBeforeIssued) {
return nil, "not-valid-yet", TokenNotValidYet
} else if errors.Is(err, jwt.ErrTokenExpired) {
return nil, "expired", TokenExpired
}
return nil, reason, TokenAuthFailed
}
@ -1311,8 +1319,9 @@ func (s *ProxyServer) parseToken(tokenValue string) (*signaling.TokenClaims, str
return nil, "auth-failed", TokenAuthFailed
}
minIssuedAt := time.Now().Add(-maxTokenAge)
if issuedAt := claims.IssuedAt; issuedAt != nil && issuedAt.Before(minIssuedAt) {
now := time.Now()
minIssuedAt := now.Add(-(maxTokenAge + tokenLeeway))
if issuedAt := claims.IssuedAt; issuedAt == nil || issuedAt.Before(minIssuedAt) {
return nil, "expired", TokenExpired
}

View file

@ -38,7 +38,7 @@ import (
"time"
"github.com/dlintw/goconf"
"github.com/golang-jwt/jwt/v4"
"github.com/golang-jwt/jwt/v5"
"github.com/gorilla/mux"
"github.com/gorilla/websocket"
"github.com/stretchr/testify/assert"

View file

@ -30,7 +30,7 @@ import (
"testing"
"time"
"github.com/golang-jwt/jwt/v4"
"github.com/golang-jwt/jwt/v5"
"github.com/gorilla/websocket"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

View file

@ -31,7 +31,7 @@ import (
"time"
"github.com/dlintw/goconf"
"github.com/golang-jwt/jwt/v4"
"github.com/golang-jwt/jwt/v5"
signaling "github.com/strukturag/nextcloud-spreed-signaling"
)

View file

@ -29,7 +29,8 @@ import (
"sync/atomic"
"github.com/dlintw/goconf"
"github.com/golang-jwt/jwt/v4"
"github.com/golang-jwt/jwt/v5"
signaling "github.com/strukturag/nextcloud-spreed-signaling"
)

View file

@ -38,7 +38,7 @@ import (
"testing"
"time"
"github.com/golang-jwt/jwt/v4"
"github.com/golang-jwt/jwt/v5"
"github.com/gorilla/websocket"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"