Skip to content

Commit fb9b9a4

Browse files
committed
all: migrate from go-jose/v2 to go-jose/v4
gopkg.in/square/go-jose.v2 is unmaintained. The canonical library is now github.com/go-jose/go-jose/v4. This is a mostly mechanical import path migration with two API changes: 1. jwt.ParseSigned() now requires an explicit []SignatureAlgorithm parameter for algorithm allowlisting — a security improvement that prevents algorithm confusion attacks. 2. jwt.Signed().Claims().CompactSerialize() is now Serialize(). 3. go-jose/v4 serializes single-element JWT audiences as a string per RFC 7519 (v2 always used an array). Updated test assertions to handle both representations.
1 parent c2c1d8d commit fb9b9a4

8 files changed

Lines changed: 27 additions & 28 deletions

File tree

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ go 1.26
44

55
require (
66
filippo.io/csrf v0.2.1
7-
gopkg.in/square/go-jose.v2 v2.6.0
7+
github.com/go-jose/go-jose/v4 v4.1.4
88
tailscale.com v1.94.2
99
)
1010

go.sum

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -56,8 +56,6 @@ github.com/creachadair/taskgroup v0.13.2 h1:3KyqakBuFsm3KkXi/9XIb0QcA8tEzLHLgaoi
5656
github.com/creachadair/taskgroup v0.13.2/go.mod h1:i3V1Zx7H8RjwljUEeUWYT30Lmb9poewSb2XI1yTwD0g=
5757
github.com/creack/pty v1.1.23 h1:4M6+isWdcStXEf15G/RbrMPOQj1dZ7HPZCGwE4kOeP0=
5858
github.com/creack/pty v1.1.23/go.mod h1:08sCNb52WyoAwi2QDyzUCTgcvVFhUzewun7wtTfvcwE=
59-
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
60-
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
6159
github.com/dblohm7/wingoes v0.0.0-20240119213807-a09d6be7affa h1:h8TfIT1xc8FWbwwpmHn1J5i43Y0uZP97GqasGCzSRJk=
6260
github.com/dblohm7/wingoes v0.0.0-20240119213807-a09d6be7affa/go.mod h1:Nx87SkVqTKd8UtT+xu7sM/l+LgXs6c0aHrlKusR+2EQ=
6361
github.com/dgryski/go-metro v0.0.0-20180109044635-280f6062b5bc h1:8WFBn63wegobsYAX0YjD+8suexZDga5CctH4CCTx2+8=
@@ -74,6 +72,8 @@ github.com/gaissmai/bart v0.18.0 h1:jQLBT/RduJu0pv/tLwXE+xKPgtWJejbxuXAR+wLJafo=
7472
github.com/gaissmai/bart v0.18.0/go.mod h1:JJzMAhNF5Rjo4SF4jWBrANuJfqY+FvsFhW7t1UZJ+XY=
7573
github.com/github/fakeca v0.1.0 h1:Km/MVOFvclqxPM9dZBC4+QE564nU4gz4iZ0D9pMw28I=
7674
github.com/github/fakeca v0.1.0/go.mod h1:+bormgoGMMuamOscx7N91aOuUST7wdaJ2rNjeohylyo=
75+
github.com/go-jose/go-jose/v4 v4.1.4 h1:moDMcTHmvE6Groj34emNPLs/qtYXRVcd6S7NHbHz3kA=
76+
github.com/go-jose/go-jose/v4 v4.1.4/go.mod h1:x4oUasVrzR7071A4TnHLGSPpNOm2a21K9Kf04k1rs08=
7777
github.com/go-json-experiment/json v0.0.0-20250813024750-ebf49471dced h1:Q311OHjMh/u5E2TITc++WlTP5We0xNseRMkHDyvhW7I=
7878
github.com/go-json-experiment/json v0.0.0-20250813024750-ebf49471dced/go.mod h1:TiCD2a1pcmjd7YnhGH0f/zKNcCD06B029pHhzV23c2M=
7979
github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE=
@@ -140,8 +140,6 @@ github.com/pires/go-proxyproto v0.8.1 h1:9KEixbdJfhrbtjpz/ZwCdWDD2Xem0NZ38qMYaAS
140140
github.com/pires/go-proxyproto v0.8.1/go.mod h1:ZKAAyp3cgy5Y5Mo4n9AlScrkCZwUy0g3Jf+slqQVcuU=
141141
github.com/pkg/sftp v1.13.6 h1:JFZT4XbOU7l77xGSpOdW+pwIMqP044IyjXX6FGyEKFo=
142142
github.com/pkg/sftp v1.13.6/go.mod h1:tz1ryNURKu77RL+GuCzmoJYxQczL3wLNNpPWagdg4Qk=
143-
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
144-
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
145143
github.com/prometheus-community/pro-bing v0.4.0 h1:YMbv+i08gQz97OZZBwLyvmmQEEzyfyrrjEaAchdy3R4=
146144
github.com/prometheus-community/pro-bing v0.4.0/go.mod h1:b7wRYZtCcPmt4Sz319BykUU241rWLe1VFXyiyWK/dH4=
147145
github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk=
@@ -152,8 +150,6 @@ github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0t
152150
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
153151
github.com/safchain/ethtool v0.3.0 h1:gimQJpsI6sc1yIqP/y8GYgiXn/NjgvpM0RNoWLVVmP0=
154152
github.com/safchain/ethtool v0.3.0/go.mod h1:SA9BwrgyAqNo7M+uaL6IYbxpm5wk3L7Mm6ocLW+CJUs=
155-
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
156-
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
157153
github.com/tailscale/certstore v0.1.1-0.20231202035212-d3fa0460f47e h1:PtWT87weP5LWHEY//SWsYkSO3RWRZo4OSWagh3YD2vQ=
158154
github.com/tailscale/certstore v0.1.1-0.20231202035212-d3fa0460f47e/go.mod h1:XrBNfAFN+pwoWuksbFS9Ccxnopa15zJGgXRFN90l3K4=
159155
github.com/tailscale/go-winio v0.0.0-20231025203758-c4f33415bf55 h1:Gzfnfk2TWrk8Jj4P4c1a3CtQyMaTVCznlkLZI++hok4=
@@ -223,10 +219,6 @@ golang.zx2c4.com/wireguard/windows v0.5.3 h1:On6j2Rpn3OEMXqBq00QEDC7bWSZrPIHKIus
223219
golang.zx2c4.com/wireguard/windows v0.5.3/go.mod h1:9TEe8TJmtwyQebdFwAkEWOPr3prrtqm+REGFifP60hI=
224220
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
225221
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
226-
gopkg.in/square/go-jose.v2 v2.6.0 h1:NGk74WTnPKBNUhNzQX7PYcTLUjoq7mzKk2OKbvwk2iI=
227-
gopkg.in/square/go-jose.v2 v2.6.0/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
228-
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
229-
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
230222
gvisor.dev/gvisor v0.0.0-20250205023644-9414b50a5633 h1:2gap+Kh/3F47cO6hAu3idFvsJ0ue6TRcEi2IUkv/F8k=
231223
gvisor.dev/gvisor v0.0.0-20250205023644-9414b50a5633/go.mod h1:5DMfjtclAbTIjbXqO1qCe2K5GKKxWz2JHvCChuTcJEM=
232224
honnef.co/go/tools v0.7.0-0.dev.0.20251022135355-8273271481d0 h1:5SXjd4ET5dYijLaf0O3aOenC0Z4ZafIWSpjUzsQaNho=

server/extraclaims_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import (
1111
"reflect"
1212
"testing"
1313

14-
"gopkg.in/square/go-jose.v2/jwt"
14+
"github.com/go-jose/go-jose/v4/jwt"
1515
"tailscale.com/types/key"
1616
"tailscale.com/types/views"
1717
)

server/helpers_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import (
1414
"sort"
1515
"testing"
1616

17-
"gopkg.in/square/go-jose.v2"
17+
"github.com/go-jose/go-jose/v4"
1818
"tailscale.com/client/local"
1919
"tailscale.com/client/tailscale/apitype"
2020
"tailscale.com/tailcfg"

server/oauth-metadata.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import (
88
"fmt"
99
"net/http"
1010

11-
"gopkg.in/square/go-jose.v2"
11+
"github.com/go-jose/go-jose/v4"
1212
"tailscale.com/types/views"
1313
)
1414

server/server.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ import (
2626
"time"
2727

2828
"filippo.io/csrf"
29-
"gopkg.in/square/go-jose.v2"
29+
"github.com/go-jose/go-jose/v4"
3030
"tailscale.com/client/local"
3131
"tailscale.com/client/tailscale/apitype"
3232
"tailscale.com/ipn"

server/token.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import (
1616
"strings"
1717
"time"
1818

19-
"gopkg.in/square/go-jose.v2/jwt"
19+
"github.com/go-jose/go-jose/v4/jwt"
2020
"tailscale.com/client/tailscale/apitype"
2121
"tailscale.com/tailcfg"
2222
"tailscale.com/types/key"
@@ -597,7 +597,7 @@ func (s *IDPServer) issueTokens(w http.ResponseWriter, r *http.Request, ar *Auth
597597
}
598598

599599
// Create an OIDC token using this issuer's signer.
600-
token, err := jwt.Signed(signer).Claims(tsClaimsWithExtra).CompactSerialize()
600+
token, err := jwt.Signed(signer).Claims(tsClaimsWithExtra).Serialize()
601601
if err != nil {
602602
writeHTTPError(w, r, http.StatusInternalServerError, ecServerError, "error creating JWT token", err)
603603
return

server/token_test.go

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@ import (
1414
"testing"
1515
"time"
1616

17-
"gopkg.in/square/go-jose.v2/jwt"
17+
jose "github.com/go-jose/go-jose/v4"
18+
"github.com/go-jose/go-jose/v4/jwt"
1819
"tailscale.com/client/tailscale/apitype"
1920
"tailscale.com/tailcfg"
2021
"tailscale.com/types/key"
@@ -50,7 +51,7 @@ func TestResourceIndicators(t *testing.T) {
5051
t.Fatalf("failed to unmarshal response: %v", err)
5152
}
5253
// Decode JWT to check audience
53-
token, err := jwt.ParseSigned(resp.IDToken)
54+
token, err := jwt.ParseSigned(resp.IDToken, []jose.SignatureAlgorithm{jose.RS256})
5455
if err != nil {
5556
t.Fatalf("failed to parse JWT: %v", err)
5657
}
@@ -87,7 +88,7 @@ func TestResourceIndicators(t *testing.T) {
8788
t.Fatalf("failed to unmarshal response: %v", err)
8889
}
8990
// Decode JWT to check audience
90-
token, err := jwt.ParseSigned(resp.IDToken)
91+
token, err := jwt.ParseSigned(resp.IDToken, []jose.SignatureAlgorithm{jose.RS256})
9192
if err != nil {
9293
t.Fatalf("failed to parse JWT: %v", err)
9394
}
@@ -1076,7 +1077,7 @@ func TestAZPClaimWithMultipleAudiences(t *testing.T) {
10761077
}
10771078

10781079
// Parse the ID token
1079-
token, err := jwt.ParseSigned(tokenResp.IDToken)
1080+
token, err := jwt.ParseSigned(tokenResp.IDToken, []jose.SignatureAlgorithm{jose.RS256})
10801081
if err != nil {
10811082
t.Fatalf("failed to parse JWT: %v", err)
10821083
}
@@ -1092,14 +1093,20 @@ func TestAZPClaimWithMultipleAudiences(t *testing.T) {
10921093
t.Fatal("aud claim not found")
10931094
}
10941095

1095-
// The JWT library always serializes audience as an array
1096-
audArray, isArray := aud.([]any)
1097-
if !isArray {
1098-
t.Errorf("expected audience to be array, got %T", aud)
1096+
// go-jose/v4 serializes a single-element audience as a string
1097+
// per RFC 7519, and multi-element as an array.
1098+
var audCount int
1099+
switch v := aud.(type) {
1100+
case string:
1101+
audCount = 1
1102+
case []any:
1103+
audCount = len(v)
1104+
default:
1105+
t.Fatalf("unexpected aud type %T", aud)
10991106
}
11001107

1101-
if len(audArray) != tt.expectedAudiences {
1102-
t.Errorf("expected %d audiences, got %d", tt.expectedAudiences, len(audArray))
1108+
if audCount != tt.expectedAudiences {
1109+
t.Errorf("expected %d audiences, got %d", tt.expectedAudiences, audCount)
11031110
}
11041111

11051112
// Check azp claim
@@ -1337,7 +1344,7 @@ func TestServeToken(t *testing.T) {
13371344
t.Fatalf("failed to unmarshal response: %v", err)
13381345
}
13391346

1340-
tok, err := jwt.ParseSigned(resp.IDToken)
1347+
tok, err := jwt.ParseSigned(resp.IDToken, []jose.SignatureAlgorithm{jose.RS256})
13411348
if err != nil {
13421349
t.Fatalf("failed to parse ID token: %v", err)
13431350
}

0 commit comments

Comments
 (0)