From e152b8dcda52f3ab10354bc66baace78cd325050 Mon Sep 17 00:00:00 2001 From: Joachim Bauch Date: Tue, 5 Nov 2024 09:59:14 +0100 Subject: [PATCH] Migrate to github.com/golang-jwt/jwt/v5 --- api_proxy.go | 2 +- api_signaling.go | 20 ++++--------- go.mod | 3 +- go.sum | 2 ++ hub.go | 53 ++++++++++++++++++++++++---------- hub_test.go | 11 +++---- mcu_proxy.go | 2 +- proxy/proxy_remote.go | 2 +- proxy/proxy_server.go | 27 +++++++++++------ proxy/proxy_server_test.go | 2 +- proxy/proxy_testclient_test.go | 2 +- proxy/proxy_tokens_etcd.go | 2 +- proxy/proxy_tokens_static.go | 3 +- testclient_test.go | 2 +- 14 files changed, 80 insertions(+), 53 deletions(-) diff --git a/api_proxy.go b/api_proxy.go index 52802be..23acf35 100644 --- a/api_proxy.go +++ b/api_proxy.go @@ -26,7 +26,7 @@ import ( "fmt" "net/url" - "github.com/golang-jwt/jwt/v4" + "github.com/golang-jwt/jwt/v5" ) type ProxyClientMessage struct { diff --git a/api_signaling.go b/api_signaling.go index f68445e..d6729f7 100644 --- a/api_signaling.go +++ b/api_signaling.go @@ -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 } diff --git a/go.mod b/go.mod index b8b8b8d..302829c 100644 --- a/go.mod +++ b/go.mod @@ -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 diff --git a/go.sum b/go.sum index cf53ef1..726ee53 100644 --- a/go.sum +++ b/go.sum @@ -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= diff --git a/hub.go b/hub.go index 1720fe6..7c3e78a 100644 --- a/hub.go +++ b/hub.go @@ -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 diff --git a/hub_test.go b/hub_test.go index 6f835cf..f608149 100644 --- a/hub_test.go +++ b/hub_test.go @@ -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)) diff --git a/mcu_proxy.go b/mcu_proxy.go index 1cb0c7b..0abbaa0 100644 --- a/mcu_proxy.go +++ b/mcu_proxy.go @@ -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" ) diff --git a/proxy/proxy_remote.go b/proxy/proxy_remote.go index 838cecc..81a7bbf 100644 --- a/proxy/proxy_remote.go +++ b/proxy/proxy_remote.go @@ -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" diff --git a/proxy/proxy_server.go b/proxy/proxy_server.go index 4bddd63..d3655e8 100644 --- a/proxy/proxy_server.go +++ b/proxy/proxy_server.go @@ -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 } diff --git a/proxy/proxy_server_test.go b/proxy/proxy_server_test.go index 7dbe033..395a1d7 100644 --- a/proxy/proxy_server_test.go +++ b/proxy/proxy_server_test.go @@ -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" diff --git a/proxy/proxy_testclient_test.go b/proxy/proxy_testclient_test.go index 3264157..c08b5f3 100644 --- a/proxy/proxy_testclient_test.go +++ b/proxy/proxy_testclient_test.go @@ -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" diff --git a/proxy/proxy_tokens_etcd.go b/proxy/proxy_tokens_etcd.go index cbee803..09dfc51 100644 --- a/proxy/proxy_tokens_etcd.go +++ b/proxy/proxy_tokens_etcd.go @@ -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" ) diff --git a/proxy/proxy_tokens_static.go b/proxy/proxy_tokens_static.go index ac22c2b..8de255a 100644 --- a/proxy/proxy_tokens_static.go +++ b/proxy/proxy_tokens_static.go @@ -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" ) diff --git a/testclient_test.go b/testclient_test.go index 2abd8f4..cea8771 100644 --- a/testclient_test.go +++ b/testclient_test.go @@ -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"