Skip to content

Commit

Permalink
Merge pull request #857 from strukturag/jwt-v5
Browse files Browse the repository at this point in the history
Migrate to github.com/golang-jwt/jwt/v5
  • Loading branch information
fancycode authored Nov 5, 2024
2 parents 7608d9f + 8cefef4 commit eea69ca
Show file tree
Hide file tree
Showing 16 changed files with 89 additions and 62 deletions.
2 changes: 1 addition & 1 deletion api_proxy.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import (
"fmt"
"net/url"

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

type ProxyClientMessage struct {
Expand Down
8 changes: 4 additions & 4 deletions api_proxy_easyjson.go

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

20 changes: 5 additions & 15 deletions api_signaling.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ import (
"strings"
"time"

"github.com/golang-jwt/jwt/v4"
"github.com/golang-jwt/jwt/v5"
"github.com/pion/sdp/v3"
)

Expand Down Expand Up @@ -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 {
Expand All @@ -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
}

Expand All @@ -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
}

Expand Down
14 changes: 7 additions & 7 deletions api_signaling_easyjson.go

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

3 changes: 2 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -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=
Expand Down
51 changes: 37 additions & 14 deletions hub.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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()
)

Expand Down Expand Up @@ -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
Expand All @@ -1367,20 +1375,35 @@ func (h *Hub) processHelloV2(ctx context.Context, client HandlerClient, message
}
authTokenClaims = claims
}

issuedAt, err := authTokenClaims.GetIssuedAt()
if err != nil {
return nil, nil, InvalidToken
}
expiresAt, err := authTokenClaims.GetExpirationTime()
if err != nil {
return nil, nil, InvalidToken
}
now := time.Now()
if !authTokenClaims.VerifyIssuedAt(now, true) {
if issuedAt != nil && expiresAt != nil && expiresAt.Before(issuedAt.Time) {
return nil, nil, TokenExpired
} else if issuedAt == nil {
return nil, nil, TokenNotValidYet
}
if !authTokenClaims.VerifyExpiresAt(now, true) {
} 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
Expand Down
11 changes: 6 additions & 5 deletions hub_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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))

Expand Down Expand Up @@ -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()
Expand Down Expand Up @@ -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))

Expand Down
2 changes: 1 addition & 1 deletion mcu_proxy.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
)

Expand Down
2 changes: 1 addition & 1 deletion proxy/proxy_remote.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
25 changes: 17 additions & 8 deletions proxy/proxy_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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"
Expand Down Expand Up @@ -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 {
}, 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
}
}
if err != nil {

return nil, reason, TokenAuthFailed
}

Expand All @@ -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
}

Expand Down
2 changes: 1 addition & 1 deletion proxy/proxy_server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
Loading

0 comments on commit eea69ca

Please sign in to comment.