Skip to content

Commit

Permalink
CBG-4017 Move to go-oidc v3.11.0, go-jose v4.0.2 (#7036)
Browse files Browse the repository at this point in the history
  • Loading branch information
adamcfraser authored Aug 5, 2024
1 parent a1af5aa commit c2dce28
Show file tree
Hide file tree
Showing 16 changed files with 79 additions and 66 deletions.
6 changes: 4 additions & 2 deletions auth/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@ import (

"github.com/couchbase/sync_gateway/base"
ch "github.com/couchbase/sync_gateway/channels"
"github.com/go-jose/go-jose/v4/jwt"
pkgerrors "github.com/pkg/errors"
"golang.org/x/crypto/bcrypt"
"gopkg.in/square/go-jose.v2/jwt"
)

// Authenticator manages user authentication for a database.
Expand Down Expand Up @@ -780,7 +780,9 @@ func (auth *Authenticator) AuthenticateUser(username string, password string) (U
}

func (auth *Authenticator) AuthenticateUntrustedJWT(rawToken string, oidcProviders OIDCProviderMap, localJWT LocalJWTProviderMap, callbackURLFunc OIDCCallbackURLFunc) (User, PrincipalConfig, error) {
token, err := jwt.ParseSigned(rawToken)

// Parse using the full list of supported algorithms, algorithm checking done during verify
token, err := jwt.ParseSigned(rawToken, SupportedAlgorithmsSlice)
if err != nil {
base.DebugfCtx(auth.LogCtx, base.KeyAuth, "Error parsing JWT in AuthenticateUntrustedJWT: %v", err)
return nil, PrincipalConfig{}, err
Expand Down
40 changes: 20 additions & 20 deletions auth/auth_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,10 @@ import (
sgbucket "github.com/couchbase/sg-bucket"
"github.com/couchbase/sync_gateway/base"
ch "github.com/couchbase/sync_gateway/channels"
"github.com/go-jose/go-jose/v4/jwt"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"golang.org/x/crypto/bcrypt"
"gopkg.in/square/go-jose.v2/jwt"
)

func NewTestAuthenticator(t testing.TB, dataStore sgbucket.DataStore, channelComputer ChannelComputer, opts AuthenticatorOptions) *Authenticator {
Expand Down Expand Up @@ -837,7 +837,7 @@ func TestAuthenticateTrustedJWT(t *testing.T) {

t.Run("token with issuer but no clientID config", func(t *testing.T) {
builder := jwt.Signed(signer).Claims(jwt.Claims{Issuer: issuerGoogleAccounts})
token, err := builder.CompactSerialize()
token, err := builder.Serialize()
require.NoError(t, err, "Error serializing token using compact serialization format")
provider := oidcProviderForTest(t, &OIDCProvider{
Name: providerGoogle.Name,
Expand Down Expand Up @@ -876,7 +876,7 @@ func TestAuthenticateTrustedJWT(t *testing.T) {
wantUsername, err := getJWTUsername(provider.common(), &Identity{Subject: claims.Subject})
assert.NoError(t, err, "Error retrieving OpenID Connect username")
builder := jwt.Signed(signer).Claims(claims)
token, err := builder.CompactSerialize()
token, err := builder.Serialize()
require.NoError(t, err, "Error serializing token using compact serialization format")
user, _, expiry, err := auth.AuthenticateTrustedJWT(token, provider, callbackURLFunc)
assert.NoError(t, err, "Error authenticating with trusted JWT")
Expand Down Expand Up @@ -905,7 +905,7 @@ func TestAuthenticateTrustedJWT(t *testing.T) {
Expiry: jwt.NewNumericDate(time.Now().Add(5 * time.Minute)),
}
builder := jwt.Signed(signer).Claims(claims)
token, err := builder.CompactSerialize()
token, err := builder.Serialize()
require.NoError(t, err, "Error serializing token using compact serialization format")
user, _, expiry, err := auth.AuthenticateTrustedJWT(token, provider, callbackURLFunc)
assert.Error(t, err, "Error verifying issuer claim from token")
Expand Down Expand Up @@ -934,7 +934,7 @@ func TestAuthenticateTrustedJWT(t *testing.T) {
Expiry: jwt.NewNumericDate(time.Now().Add(5 * time.Minute)),
}
builder := jwt.Signed(signer).Claims(claims)
token, err := builder.CompactSerialize()
token, err := builder.Serialize()
require.NoError(t, err, "Error serializing token using compact serialization format")
user, _, expiry, err := auth.AuthenticateTrustedJWT(token, provider, callbackURLFunc)
assert.Error(t, err, "Error verifying audience claim from token")
Expand Down Expand Up @@ -962,7 +962,7 @@ func TestAuthenticateTrustedJWT(t *testing.T) {
Expiry: jwt.NewNumericDate(time.Now().Add(5 * time.Minute)),
}
builder := jwt.Signed(signer).Claims(claims)
token, err := builder.CompactSerialize()
token, err := builder.Serialize()
require.NoError(t, err, "Error serializing token using compact serialization format")
user, _, expiry, err := auth.AuthenticateTrustedJWT(token, provider, callbackURLFunc)
assert.Error(t, err, "Error verifying audience claim from token")
Expand Down Expand Up @@ -991,7 +991,7 @@ func TestAuthenticateTrustedJWT(t *testing.T) {
Expiry: jwt.NewNumericDate(time.Now().Add(-1 * time.Minute)),
}
builder := jwt.Signed(signer).Claims(claims)
token, err := builder.CompactSerialize()
token, err := builder.Serialize()
require.NoError(t, err, "Error serializing token using compact serialization format")
user, _, expiry, err := auth.AuthenticateTrustedJWT(token, provider, callbackURLFunc)
assert.Error(t, err, "Can't authenticate with expired token")
Expand Down Expand Up @@ -1024,7 +1024,7 @@ func TestAuthenticateTrustedJWT(t *testing.T) {
wantUsername, err := getJWTUsername(provider.common(), &Identity{Subject: claims.Subject})
assert.NoError(t, err, "Error retrieving OpenID Connect username")
builder := jwt.Signed(signer).Claims(claims)
token, err := builder.CompactSerialize()
token, err := builder.Serialize()
require.NoError(t, err, "Error serializing token using compact serialization format")
user, _, expiry, err := auth.AuthenticateTrustedJWT(token, provider, callbackURLFunc)
assert.NoError(t, err, "Error authenticating with trusted token")
Expand Down Expand Up @@ -1054,7 +1054,7 @@ func TestAuthenticateTrustedJWT(t *testing.T) {
NotBefore: jwt.NewNumericDate(time.Now().Add(2 * time.Minute)),
}
builder := jwt.Signed(signer).Claims(claims)
token, err := builder.CompactSerialize()
token, err := builder.Serialize()
require.NoError(t, err, "Error serializing token using compact serialization format")
user, _, expiry, err := auth.AuthenticateTrustedJWT(token, provider, callbackURLFunc)
assert.Error(t, err, "Token with expired nbf (not before) time")
Expand Down Expand Up @@ -1082,7 +1082,7 @@ func TestAuthenticateTrustedJWT(t *testing.T) {
Expiry: jwt.NewNumericDate(time.Now().Add(5 * time.Minute)),
}
builder := jwt.Signed(signer).Claims(claims)
token, err := builder.CompactSerialize()
token, err := builder.Serialize()
require.NoError(t, err, "Error serializing token using compact serialization format")
user, _, expiry, err := auth.AuthenticateTrustedJWT(token, provider, callbackURLFunc)
assert.Error(t, err, "Token must contain valid subject claim")
Expand Down Expand Up @@ -1121,7 +1121,7 @@ func TestAuthenticateTrustedJWT(t *testing.T) {

claimEmail := map[string]interface{}{"email": wantUserEmail}
builder := jwt.Signed(signer).Claims(claims).Claims(claimEmail)
token, err := builder.CompactSerialize()
token, err := builder.Serialize()
require.NoError(t, err, "Error serializing token using compact serialization format")
user, _, expiry, err := auth.AuthenticateTrustedJWT(token, provider, callbackURLFunc)
assert.NoError(t, err, "Error authenticating with trusted JWT")
Expand Down Expand Up @@ -1162,7 +1162,7 @@ func TestAuthenticateTrustedJWT(t *testing.T) {

claimEmail := map[string]interface{}{"email": "emily@"}
builder := jwt.Signed(signer).Claims(claims).Claims(claimEmail)
token, err := builder.CompactSerialize()
token, err := builder.Serialize()
require.NoError(t, err, "Error serializing token using compact serialization format")
user, _, expiry, err := auth.AuthenticateTrustedJWT(token, provider, callbackURLFunc)
assert.NoError(t, err, "Error authenticating with trusted JWT")
Expand Down Expand Up @@ -1197,7 +1197,7 @@ func TestAuthenticateTrustedJWT(t *testing.T) {

claimEmail := map[string]interface{}{"email": "layla@"}
builder := jwt.Signed(signer).Claims(claims).Claims(claimEmail)
token, err := builder.CompactSerialize()
token, err := builder.Serialize()
require.NoError(t, err, "Error serializing token using compact serialization format")
user, _, expiry, err := auth.AuthenticateTrustedJWT(token, provider, callbackURLFunc)
assert.NoError(t, err, "Error authenticating with trusted JWT")
Expand Down Expand Up @@ -1352,7 +1352,7 @@ func TestAuthenticateUntrustedJWT(t *testing.T) {
t.Run("multiple providers token with no issuer no audience", func(t *testing.T) {
providers := OIDCProviderMap{providerGoogle.Name: providerGoogle, providerFacebook.Name: providerFacebook}
builder := jwt.Signed(signer).Claims(jwt.Claims{})
token, err := builder.CompactSerialize()
token, err := builder.Serialize()
require.NoError(t, err, "Error serializing token using compact serialization format")
user, _, err := auth.AuthenticateUntrustedJWT(token, providers, LocalJWTProviderMap{}, callbackURLFunc)
require.Error(t, err, "Error getting issuer and audience from token")
Expand All @@ -1362,7 +1362,7 @@ func TestAuthenticateUntrustedJWT(t *testing.T) {
t.Run("multiple providers token with issuer no audience", func(t *testing.T) {
providers := OIDCProviderMap{providerGoogle.Name: providerGoogle, providerFacebook.Name: providerFacebook}
builder := jwt.Signed(signer).Claims(jwt.Claims{Issuer: issuerGoogleAccounts})
token, err := builder.CompactSerialize()
token, err := builder.Serialize()
require.NoError(t, err, "Error serializing token using compact serialization format")
user, _, err := auth.AuthenticateUntrustedJWT(token, providers, LocalJWTProviderMap{}, callbackURLFunc)
require.Error(t, err, "Error getting issuer and audience from token")
Expand All @@ -1372,7 +1372,7 @@ func TestAuthenticateUntrustedJWT(t *testing.T) {
t.Run("multiple providers token with issuer empty audience", func(t *testing.T) {
providers := OIDCProviderMap{providerGoogle.Name: providerGoogle, providerFacebook.Name: providerFacebook}
builder := jwt.Signed(signer).Claims(jwt.Claims{Issuer: issuerGoogleAccounts, Audience: jwt.Audience{}})
token, err := builder.CompactSerialize()
token, err := builder.Serialize()
require.NoError(t, err, "Error serializing token using compact serialization format")
user, _, err := auth.AuthenticateUntrustedJWT(token, providers, LocalJWTProviderMap{}, callbackURLFunc)
require.Error(t, err, "Error getting issuer and audience from token")
Expand All @@ -1382,7 +1382,7 @@ func TestAuthenticateUntrustedJWT(t *testing.T) {
t.Run("multiple providers token with audience no issuer", func(t *testing.T) {
providers := OIDCProviderMap{providerGoogle.Name: providerGoogle, providerFacebook.Name: providerFacebook}
builder := jwt.Signed(signer).Claims(jwt.Claims{Audience: jwt.Audience{"aud1", "aud2", "aud3"}})
token, err := builder.CompactSerialize()
token, err := builder.Serialize()
require.NoError(t, err, "Error serializing token using compact serialization format")
user, _, err := auth.AuthenticateUntrustedJWT(token, providers, LocalJWTProviderMap{}, callbackURLFunc)
require.Error(t, err, "Error getting issuer and audience from token")
Expand All @@ -1392,7 +1392,7 @@ func TestAuthenticateUntrustedJWT(t *testing.T) {
t.Run("multiple providers token with issuer mismatch", func(t *testing.T) {
providers := OIDCProviderMap{providerGoogle.Name: providerGoogle, providerFacebook.Name: providerFacebook}
builder := jwt.Signed(signer).Claims(jwt.Claims{Issuer: issuerAmazonAccounts, Audience: jwt.Audience{"aud1"}})
token, err := builder.CompactSerialize()
token, err := builder.Serialize()
require.NoError(t, err, "Error serializing token using compact serialization format")
user, _, err := auth.AuthenticateUntrustedJWT(token, providers, LocalJWTProviderMap{}, callbackURLFunc)
require.Error(t, err, "No provider found against the configured issuer")
Expand All @@ -1402,7 +1402,7 @@ func TestAuthenticateUntrustedJWT(t *testing.T) {
t.Run("multiple providers token with audience mismatch", func(t *testing.T) {
providers := OIDCProviderMap{providerGoogle.Name: providerGoogle, providerFacebook.Name: providerFacebook}
builder := jwt.Signed(signer).Claims(jwt.Claims{Issuer: issuerGoogleAccounts, Audience: jwt.Audience{"aud2"}})
token, err := builder.CompactSerialize()
token, err := builder.Serialize()
require.NoError(t, err, "Error serializing token using compact serialization format")
user, _, err := auth.AuthenticateUntrustedJWT(token, providers, LocalJWTProviderMap{}, callbackURLFunc)
require.Error(t, err, "No provider found against the configured issuer")
Expand Down Expand Up @@ -1431,7 +1431,7 @@ func TestAuthenticateUntrustedJWT(t *testing.T) {
Expiry: jwt.NewNumericDate(time.Now().Add(5 * time.Minute)),
}
builder := jwt.Signed(signer).Claims(claims)
token, err := builder.CompactSerialize()
token, err := builder.Serialize()
require.NoError(t, err, "Error serializing token using compact serialization format")
user, _, err := auth.AuthenticateUntrustedJWT(token, providers, LocalJWTProviderMap{}, callbackURLFunc)
assert.Error(t, err, "Error authenticating with trusted JWT")
Expand Down
21 changes: 18 additions & 3 deletions auth/jwt.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@ import (
"fmt"
"net/url"

"github.com/coreos/go-oidc"
"github.com/coreos/go-oidc/v3/oidc"
"github.com/couchbase/sync_gateway/base"
"gopkg.in/square/go-jose.v2"
"github.com/go-jose/go-jose/v4"
)

type jwtAuthenticator interface {
Expand All @@ -39,6 +39,20 @@ var SupportedAlgorithms = map[jose.SignatureAlgorithm]bool{
jose.PS512: true,
}

// Full list of supported algorithms is used to initially parse the JWT. The more restricted list (based
// on the provider) is used to verify
var SupportedAlgorithmsSlice = []jose.SignatureAlgorithm{
jose.RS256,
jose.RS384,
jose.RS512,
jose.ES256,
jose.ES384,
jose.ES512,
jose.PS256,
jose.PS384,
jose.PS512,
}

// JWTConfigCommon groups together configuration options common to both OIDC and local JWT authentication.
type JWTConfigCommon struct {
Issuer string `json:"issuer"` // OIDC Issuer
Expand Down Expand Up @@ -93,7 +107,8 @@ const keyUseSigning = "sig"
type JSONWebKeys []jose.JSONWebKey

func (j JSONWebKeys) VerifySignature(ctx context.Context, jwt string) (payload []byte, err error) {
jws, err := jose.ParseSigned(jwt)
// Parse using the full list of supported algorithms, prior to restricting based on JWKS
jws, err := jose.ParseSigned(jwt, SupportedAlgorithmsSlice)
if err != nil {
return nil, err
}
Expand Down
6 changes: 3 additions & 3 deletions auth/jwt_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@ import (
"time"

"github.com/couchbase/sync_gateway/base"
"github.com/go-jose/go-jose/v4"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"gopkg.in/square/go-jose.v2"
)

func dummyCallbackURL(_ string, _ bool) string {
Expand Down Expand Up @@ -185,9 +185,9 @@ func TestJWTVerifyToken(t *testing.T) {
}), anyError))

// header: alg=none
t.Run("valid JWT with alg none", test(baseProvider, `eyJhbGciOiJub25lIn0.eyJhdWQiOlsidGVzdEF1ZCJdLCJpc3MiOiJ0ZXN0SXNzdWVyIn0.`, "id token signed with unsupported algorithm"))
t.Run("valid JWT with alg none", test(baseProvider, `eyJhbGciOiJub25lIn0.eyJhdWQiOlsidGVzdEF1ZCJdLCJpc3MiOiJ0ZXN0SXNzdWVyIn0.`, "unexpected signature algorithm"))
// header: alg=HS256
t.Run("valid JWT with alg HS256", test(baseProvider, `eyJhbGciOiJIUzI1NiJ9.eyJhdWQiOlsidGVzdEF1ZCJdLCJpc3MiOiJ0ZXN0SXNzdWVyIn0.aPSuXUVKN1FNS53mw0Xw3a-SU2GVS98gHWEVrzTnQYM`, "id token signed with unsupported algorithm"))
t.Run("valid JWT with alg HS256", test(baseProvider, `eyJhbGciOiJIUzI1NiJ9.eyJhdWQiOlsidGVzdEF1ZCJdLCJpc3MiOiJ0ZXN0SXNzdWVyIn0.aPSuXUVKN1FNS53mw0Xw3a-SU2GVS98gHWEVrzTnQYM`, "unexpected signature algorithm"))
// header: alg=RS256, kid=rsa
t.Run("valid JWT with no signature", test(baseProvider, `eyJhbGciOiJSUzI1NiIsImtpZCI6InJzYSJ9.eyJhdWQiOlsidGVzdEF1ZCJdLCJpc3MiOiJ0ZXN0SXNzdWVyIn0.`, "failed to verify signature"))

Expand Down
6 changes: 3 additions & 3 deletions auth/jwt_test_utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@ package auth
import (
"testing"

"github.com/go-jose/go-jose/v4"
"github.com/go-jose/go-jose/v4/jwt"
"github.com/stretchr/testify/require"
"gopkg.in/square/go-jose.v2"
"gopkg.in/square/go-jose.v2/jwt"
)

// These are not in jwt_test.go to allow use in tests from other packages.
Expand All @@ -37,7 +37,7 @@ func CreateTestJWT(t *testing.T, alg jose.SignatureAlgorithm, key interface{}, h
}, signerOpts)
require.NoError(t, err, "failed to create signer")

tok, err := jwt.Signed(signer).Claims(claims).CompactSerialize()
tok, err := jwt.Signed(signer).Claims(claims).Serialize()
require.NoError(t, err, "failed to serialize JWT")
return tok
}
6 changes: 3 additions & 3 deletions auth/oidc.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,12 @@ import (
"sync"
"time"

"github.com/coreos/go-oidc"
"github.com/coreos/go-oidc/v3/oidc"
"github.com/couchbase/sync_gateway/base"
"github.com/go-jose/go-jose/v4"
"github.com/go-jose/go-jose/v4/jwt"
pkgerrors "github.com/pkg/errors"
"golang.org/x/oauth2"
"gopkg.in/square/go-jose.v2"
"gopkg.in/square/go-jose.v2/jwt"
)

const (
Expand Down
10 changes: 5 additions & 5 deletions auth/oidc_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,13 @@ import (
"testing"
"time"

"github.com/coreos/go-oidc"
"github.com/coreos/go-oidc/v3/oidc"
"github.com/couchbase/sync_gateway/base"
ch "github.com/couchbase/sync_gateway/channels"
"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"
"gopkg.in/square/go-jose.v2"
"gopkg.in/square/go-jose.v2/jwt"
)

// oidcProviderForTest automatically stops the provider once the test has stopped.
Expand Down Expand Up @@ -453,10 +453,10 @@ func TestGetJWTIssuer(t *testing.T) {

claims := jwt.Claims{Issuer: wantIssuer, Audience: wantAudience}
builder := jwt.Signed(signer).Claims(claims)
token, err := builder.CompactSerialize()
token, err := builder.Serialize()
require.NoError(t, err, "Failed to serialize JSON Web Token")

jwt, err := jwt.ParseSigned(token)
jwt, err := jwt.ParseSigned(token, SupportedAlgorithmsSlice)
require.NoError(t, err, "Failed to decode raw JSON Web Token")
issuer, audiences, err := getIssuerWithAudience(jwt)
require.NoError(t, err)
Expand Down
4 changes: 2 additions & 2 deletions auth/oidc_verify.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ import (
"strings"
"time"

"github.com/coreos/go-oidc"
"gopkg.in/square/go-jose.v2/jwt"
"github.com/coreos/go-oidc/v3/oidc"
"github.com/go-jose/go-jose/v4/jwt"

"github.com/couchbase/sync_gateway/base"
pkgerrors "github.com/pkg/errors"
Expand Down
5 changes: 2 additions & 3 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ go 1.22
require (
dario.cat/mergo v1.0.0
github.com/KimMachineGun/automemlimit v0.6.1
github.com/coreos/go-oidc v2.2.1+incompatible
github.com/coreos/go-oidc/v3 v3.11.0
github.com/couchbase/cbgt v1.3.9
github.com/couchbase/clog v0.1.0
github.com/couchbase/go-blip v0.0.0-20231212195435-3490e96d30e3
Expand All @@ -19,6 +19,7 @@ require (
github.com/couchbaselabs/rosmar v0.0.0-20240610211258-c856107e8e78
github.com/elastic/gosigar v0.14.3
github.com/felixge/fgprof v0.9.4
github.com/go-jose/go-jose/v4 v4.0.2
github.com/google/uuid v1.6.0
github.com/gorilla/mux v1.8.1
github.com/json-iterator/go v1.1.12
Expand All @@ -38,7 +39,6 @@ require (
golang.org/x/net v0.27.0
golang.org/x/oauth2 v0.21.0
gopkg.in/natefinch/lumberjack.v2 v2.2.1
gopkg.in/square/go-jose.v2 v2.6.0
)

require (
Expand Down Expand Up @@ -79,7 +79,6 @@ require (
github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect
github.com/pquerna/cachecontrol v0.2.0 // indirect
github.com/prometheus/procfs v0.10.1 // indirect
github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 // indirect
github.com/rogpeppe/go-internal v1.11.0 // indirect
Expand Down
Loading

0 comments on commit c2dce28

Please sign in to comment.