Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 8 additions & 4 deletions auth/api/iam/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,8 @@ import (
"github.com/nuts-foundation/nuts-node/core/to"

"github.com/labstack/echo/v4"
"github.com/lestrrat-go/jwx/v2/jwk"
"github.com/lestrrat-go/jwx/v2/jwt"
"github.com/lestrrat-go/jwx/v3/jwk"
"github.com/lestrrat-go/jwx/v3/jwt"
"github.com/nuts-foundation/go-did/did"
"github.com/nuts-foundation/nuts-node/audit"
"github.com/nuts-foundation/nuts-node/auth"
Expand Down Expand Up @@ -402,7 +402,11 @@ func (r Wrapper) introspectAccessToken(input string) (*ExtendedTokenIntrospectio
// SHA256 hashing won't fail.
var cnf *Cnf
if token.DPoP != nil {
hash, _ := token.DPoP.Headers.JWK().Thumbprint(crypto.SHA256)
key, ok := token.DPoP.Headers.JWK()
if !ok {
return nil, errors.New("DPoP header is missing the jwk")
}
hash, _ := key.Thumbprint(crypto.SHA256)
base64Hash := base64.RawURLEncoding.EncodeToString(hash)
cnf = &Cnf{Jkt: base64Hash}
}
Expand Down Expand Up @@ -653,7 +657,7 @@ func (r Wrapper) OpenIDConfiguration(ctx context.Context, request OpenIDConfigur
}
}
// create JWK and add to set
jwkKey, err := jwk.FromRaw(key)
jwkKey, err := jwk.Import(key)
if err != nil {
return nil, oauth.OAuth2Error{
Code: oauth.ServerError,
Expand Down
8 changes: 4 additions & 4 deletions auth/api/iam/api_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,9 @@ import (
"time"

"github.com/labstack/echo/v4"
"github.com/lestrrat-go/jwx/v2/jwa"
"github.com/lestrrat-go/jwx/v2/jws"
"github.com/lestrrat-go/jwx/v2/jwt"
"github.com/lestrrat-go/jwx/v3/jwa"
"github.com/lestrrat-go/jwx/v3/jws"
"github.com/lestrrat-go/jwx/v3/jwt"
ssi "github.com/nuts-foundation/go-did"
"github.com/nuts-foundation/go-did/did"
"github.com/nuts-foundation/go-did/vc"
Expand Down Expand Up @@ -1538,7 +1538,7 @@ func createIssuerCredential(issuerDID did.DID, holderDID did.DID) *vc.Verifiable
for key, val := range claims {
request.Set(key, val)
}
sign, err := jwt.Sign(request, jwt.WithKey(jwa.ES256, privateKey, jws.WithProtectedHeaders(hdrs)))
sign, err := jwt.Sign(request, jwt.WithKey(jwa.ES256(), privateKey, jws.WithProtectedHeaders(hdrs)))
return string(sign), err
}

Expand Down
9 changes: 5 additions & 4 deletions auth/api/iam/dpop.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,8 +76,8 @@ func (r Wrapper) ValidateDPoPProof(_ context.Context, request ValidateDPoPProofR
return ValidateDPoPProof200JSONResponse{Reason: &reason}, nil
}
// check if ath claim matches hash of access_token
ath, ok := dpopToken.Token.Get(dpop.ATHKey)
if !ok {
var ath string
if err := dpopToken.Token.Get(dpop.ATHKey, &ath); err != nil {
reason := "missing ath claim"
return ValidateDPoPProof200JSONResponse{Reason: &reason}, nil
}
Expand All @@ -87,13 +87,14 @@ func (r Wrapper) ValidateDPoPProof(_ context.Context, request ValidateDPoPProofR
return ValidateDPoPProof200JSONResponse{Reason: &reason}, nil
}
// check if the jti is already used, if not add it to the store for the duration of the access token lifetime
jti, _ := dpopToken.Token.JwtID()
Comment thread
reinkrul marked this conversation as resolved.
var target struct{}
if err := r.useNonceOnceStore().Get(dpopToken.Token.JwtID(), &target); err != nil {
if err := r.useNonceOnceStore().Get(jti, &target); err != nil {
if !errors.Is(err, storage.ErrNotFound) {
log.Logger().WithError(err).Error("ValidateDPoPProof: failed to retrieve jti usage state")
return nil, err
}
if err := r.useNonceOnceStore().Put(dpopToken.Token.JwtID(), target); err != nil {
if err := r.useNonceOnceStore().Put(jti, target); err != nil {
log.Logger().WithError(err).Error("ValidateDPoPProof: failed to store jti usage state")
return nil, err
}
Expand Down
14 changes: 8 additions & 6 deletions auth/api/iam/dpop_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ import (
"net/http"
"testing"

"github.com/lestrrat-go/jwx/v2/jwa"
"github.com/lestrrat-go/jwx/v3/jwa"
ssi "github.com/nuts-foundation/go-did"
"github.com/nuts-foundation/go-did/did"
"github.com/nuts-foundation/nuts-node/auth/oauth"
Expand Down Expand Up @@ -172,7 +172,7 @@ func TestWrapper_ValidateDPoPProof(t *testing.T) {
require.NoError(t, err)
require.IsType(t, ValidateDPoPProof200JSONResponse{}, resp)
assert.False(t, resp.(ValidateDPoPProof200JSONResponse).Valid)
assert.Equal(t, "failed to parse DPoP header: invalid DPoP token\ninvalid compact serialization format: invalid number of segments", *resp.(ValidateDPoPProof200JSONResponse).Reason)
assert.Equal(t, "failed to parse DPoP header: invalid DPoP token\njws.ParseString: failed to parse string: jws.Parse: failed to parse compact format: jws.Parse: invalid compact serialization format: jwsbb: invalid number of segments", *resp.(ValidateDPoPProof200JSONResponse).Reason)
})
t.Run("invalid accestoken", func(t *testing.T) {
ctx := newTestClient(t)
Expand All @@ -188,7 +188,8 @@ func TestWrapper_ValidateDPoPProof(t *testing.T) {
})
t.Run("already used once", func(t *testing.T) {
ctx := newTestClient(t)
_ = ctx.client.useNonceOnceStore().Put(dpopProof.Token.JwtID(), struct{}{})
jti, _ := dpopProof.Token.JwtID()
_ = ctx.client.useNonceOnceStore().Put(jti, struct{}{})

resp, err := ctx.client.ValidateDPoPProof(nil, request)

Expand Down Expand Up @@ -229,9 +230,10 @@ func newSignedTestDPoP() (*dpop.DPoP, *dpop.DPoP, string) {
withProof := newTestDPoP()
keyPair, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
_ = withProof.GenerateProof("token")
_, _ = withProof.Sign("kid", keyPair, jwa.ES256)
_, _ = dpopToken.Sign("kid", keyPair, jwa.ES256)
thumbprintBytes, _ := dpopToken.Headers.JWK().Thumbprint(crypto2.SHA256)
_, _ = withProof.Sign("kid", keyPair, jwa.ES256())
_, _ = dpopToken.Sign("kid", keyPair, jwa.ES256())
jwkKey, _ := dpopToken.Headers.JWK()
thumbprintBytes, _ := jwkKey.Thumbprint(crypto2.SHA256)
thumbprint := base64.RawURLEncoding.EncodeToString(thumbprintBytes)
return dpopToken, withProof, thumbprint
}
9 changes: 5 additions & 4 deletions auth/api/iam/jar.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,13 @@ import (
"context"
"crypto"
"errors"
"github.com/lestrrat-go/jwx/v2/jwk"
"github.com/lestrrat-go/jwx/v2/jwt"
"github.com/lestrrat-go/jwx/v3/jwk"
"github.com/lestrrat-go/jwx/v3/jwt"
"github.com/nuts-foundation/go-did/did"
"github.com/nuts-foundation/nuts-node/auth"
"github.com/nuts-foundation/nuts-node/auth/oauth"
cryptoNuts "github.com/nuts-foundation/nuts-node/crypto"
"github.com/nuts-foundation/nuts-node/crypto/jwx"
"github.com/nuts-foundation/nuts-node/vdr/resolver"
"net/url"
"time"
Expand Down Expand Up @@ -183,7 +184,7 @@ func (j jar) validate(ctx context.Context, rawToken string, clientId string) (oa
if err != nil {
return nil, oauth.OAuth2Error{Code: oauth.InvalidRequestObject, Description: "request signature validation failed", InternalError: err}
}
claimsAsMap, err := token.AsMap(ctx)
claimsAsMap, err := jwx.ClaimsAsMap(token)
if err != nil {
// very unlikely
return nil, oauth.OAuth2Error{Code: oauth.InvalidRequestObject, Description: "invalid request parameter", InternalError: err}
Expand Down Expand Up @@ -213,7 +214,7 @@ func compareThumbprint(configurationKey jwk.Key, publicKey crypto.PublicKey) err
if err != nil {
return err
}
signerKey, err := jwk.FromRaw(publicKey)
signerKey, err := jwk.Import(publicKey)
if err != nil {
return err
}
Expand Down
14 changes: 7 additions & 7 deletions auth/api/iam/jar_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,15 @@ import (
"crypto"
"errors"
"fmt"
"github.com/lestrrat-go/jwx/v2/jwk"
"github.com/lestrrat-go/jwx/v3/jwk"
"github.com/nuts-foundation/nuts-node/crypto/storage/spi"
"github.com/nuts-foundation/nuts-node/test"
"testing"
"time"

"github.com/lestrrat-go/jwx/v2/jwa"
"github.com/lestrrat-go/jwx/v2/jws"
"github.com/lestrrat-go/jwx/v2/jwt"
"github.com/lestrrat-go/jwx/v3/jwa"
"github.com/lestrrat-go/jwx/v3/jws"
"github.com/lestrrat-go/jwx/v3/jwt"
"github.com/nuts-foundation/go-did/did"
"github.com/nuts-foundation/nuts-node/auth"
"github.com/nuts-foundation/nuts-node/auth/client/iam"
Expand Down Expand Up @@ -126,7 +126,7 @@ func TestJar_Parse(t *testing.T) {
// setup did document and keys
privateKey, _ := spi.GenerateKeyPair()
kid := fmt.Sprintf("%s#%s", holderDID.String(), "key")
jwkKey, _ := jwk.FromRaw(privateKey.Public())
jwkKey, _ := jwk.Import(privateKey.Public())
jwkKey.Set(jwk.KeyIDKey, kid)
jwkSet := jwk.NewSet()
_ = jwkSet.AddKey(jwkKey)
Expand Down Expand Up @@ -329,7 +329,7 @@ func TestJar_Parse(t *testing.T) {
t.Run("error - openID configuration key mismatch", func(t *testing.T) {
ctx := newJarTestCtx(t)
alternateKey, _ := spi.GenerateKeyPair()
jwkKey, _ := jwk.FromRaw(alternateKey.Public())
jwkKey, _ := jwk.Import(alternateKey.Public())
jwkKey.Set(jwk.KeyIDKey, kid)
jwkSet := jwk.NewSet()
_ = jwkSet.AddKey(jwkKey)
Expand Down Expand Up @@ -358,7 +358,7 @@ func createSignedRequestObject(t testing.TB, kid string, privateKey crypto.Priva
}
headers := jws.NewHeaders()
require.NoError(t, headers.Set(jws.KeyIDKey, kid))
return jwt.Sign(request, jwt.WithKey(jwa.ES256, privateKey, jws.WithProtectedHeaders(headers)))
return jwt.Sign(request, jwt.WithKey(jwa.ES256(), privateKey, jws.WithProtectedHeaders(headers)))
}

type testJarCtx struct {
Expand Down
10 changes: 5 additions & 5 deletions auth/api/iam/metadata.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,12 @@
package iam

import (
"github.com/lestrrat-go/jwx/v2/jwk"
"github.com/lestrrat-go/jwx/v3/jwk"
"net/url"
"strings"
"time"

"github.com/lestrrat-go/jwx/v2/jwa"
"github.com/lestrrat-go/jwx/v3/jwa"
"github.com/nuts-foundation/nuts-node/auth/oauth"
"github.com/nuts-foundation/nuts-node/core"
"github.com/nuts-foundation/nuts-node/core/to"
Expand Down Expand Up @@ -67,10 +67,10 @@ func staticAuthorizationServerMetadata() oauth.AuthorizationServerMetadata {
ClientIdSchemesSupported: clientIdSchemesSupported,
ResponseTypesSupported: []string{oauth.VPTokenResponseType},
VPFormatsSupported: map[string]map[string][]string{
"jwt_vp_json": {"alg_values_supported": []string{string(jwa.ES256)}},
"jwt_vc_json": {"alg_values_supported": []string{string(jwa.ES256)}},
"jwt_vp_json": {"alg_values_supported": []string{jwa.ES256().String()}},
"jwt_vc_json": {"alg_values_supported": []string{jwa.ES256().String()}},
},
RequestObjectSigningAlgValuesSupported: []string{string(jwa.ES256)},
RequestObjectSigningAlgValuesSupported: []string{jwa.ES256().String()},
}
}

Expand Down
2 changes: 1 addition & 1 deletion auth/api/iam/openid4vci.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ import (
"net/url"
"time"

"github.com/lestrrat-go/jwx/v2/jwt"
"github.com/lestrrat-go/jwx/v3/jwt"
"github.com/nuts-foundation/go-did/did"
"github.com/nuts-foundation/go-did/vc"
"github.com/nuts-foundation/nuts-node/auth/oauth"
Expand Down
7 changes: 3 additions & 4 deletions auth/api/iam/openid4vp.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,8 @@ import (
"strings"
"time"

"github.com/lestrrat-go/jwx/v2/jwk"
"github.com/lestrrat-go/jwx/v2/jwt"
"github.com/lestrrat-go/jwx/v3/jwk"
"github.com/lestrrat-go/jwx/v3/jwt"
"github.com/nuts-foundation/go-did/did"
"github.com/nuts-foundation/go-did/vc"
"github.com/nuts-foundation/nuts-node/auth/oauth"
Expand Down Expand Up @@ -598,8 +598,7 @@ func extractChallenge(presentation vc.VerifiablePresentation) (string, error) {
var nonce string
switch presentation.Format() {
case vc.JWTPresentationProofFormat:
nonceRaw, _ := presentation.JWT().Get("nonce")
nonce, _ = nonceRaw.(string)
_ = presentation.JWT().Get("nonce", &nonce)
case vc.JSONLDPresentationProofFormat:
proof, err := credential.ParseLDProof(presentation)
if err != nil {
Expand Down
2 changes: 1 addition & 1 deletion auth/api/iam/openid4vp_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ import (
"github.com/nuts-foundation/nuts-node/http/user"
"github.com/nuts-foundation/nuts-node/vcr/verifier"

"github.com/lestrrat-go/jwx/v2/jwt"
"github.com/lestrrat-go/jwx/v3/jwt"
"github.com/nuts-foundation/go-did/did"
"github.com/nuts-foundation/go-did/vc"
"github.com/nuts-foundation/nuts-node/auth/oauth"
Expand Down
7 changes: 7 additions & 0 deletions auth/api/iam/params.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,13 @@ func (ssp oauthParameters) get(key string) string {
if len(typedValue) == 1 {
return typedValue[0]
}
case []interface{}:
// JWT claims decoded from JSON (e.g. via jwx.ClaimsAsMap) carry arrays as []interface{}.
if len(typedValue) == 1 {
if str, ok := typedValue[0].(string); ok {
return str
}
}
}
return ""
}
44 changes: 44 additions & 0 deletions auth/api/iam/params_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/*
* Copyright (C) 2024 Nuts community
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/

package iam

import (
"testing"

"github.com/stretchr/testify/assert"
)

func TestOauthParameters_get(t *testing.T) {
params := oauthParameters{
"string": "value",
"stringSlice": []string{"value"},
"interfaceSlice": []interface{}{"value"},
"multiSlice": []interface{}{"a", "b"},
"nonStringSlice": []interface{}{1},
"number": 1,
}
assert.Equal(t, "value", params.get("string"))
assert.Equal(t, "value", params.get("stringSlice"))
// JWT claims decoded from JSON (e.g. the "aud" claim via jwx.ClaimsAsMap) arrive as []interface{}.
assert.Equal(t, "value", params.get("interfaceSlice"))
assert.Empty(t, params.get("multiSlice"))
assert.Empty(t, params.get("nonStringSlice"))
assert.Empty(t, params.get("number"))
assert.Empty(t, params.get("absent"))
}
3 changes: 1 addition & 2 deletions auth/api/iam/s2s_vptoken.go
Original file line number Diff line number Diff line change
Expand Up @@ -226,8 +226,7 @@ func extractNonce(presentation vc.VerifiablePresentation) (string, error) {
var nonce string
switch presentation.Format() {
case vc.JWTPresentationProofFormat:
nonceRaw, _ := presentation.JWT().Get("nonce")
nonce, _ = nonceRaw.(string)
_ = presentation.JWT().Get("nonce", &nonce)
Comment thread
reinkrul marked this conversation as resolved.
case vc.JSONLDPresentationProofFormat:
proof, err := credential.ParseLDProof(presentation)
if err != nil {
Expand Down
4 changes: 2 additions & 2 deletions auth/api/iam/s2s_vptoken_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ import (
"testing"
"time"

"github.com/lestrrat-go/jwx/v2/jwt"
"github.com/lestrrat-go/jwx/v3/jwt"
ssi "github.com/nuts-foundation/go-did"
"github.com/nuts-foundation/go-did/did"
"github.com/nuts-foundation/go-did/vc"
Expand Down Expand Up @@ -182,7 +182,7 @@ func TestWrapper_handleS2SAccessTokenRequest(t *testing.T) {
ctx := newTestClient(t)
resp, err := ctx.client.handleS2SAccessTokenRequest(context.Background(), clientID, issuerSubjectID, requestedScope, submissionJSON, "[true, false]")

assert.EqualError(t, err, "invalid_request - assertion parameter is invalid: unable to parse PEX envelope as verifiable presentation: invalid JWT")
assert.EqualError(t, err, "invalid_request - assertion parameter is invalid: unable to parse PEX envelope as verifiable presentation: jwt.Parse: failed to parse token: unknown payload type (payload is not JWT?)")
assert.Nil(t, resp)
})
t.Run("not all VPs have the same credential subject ID", func(t *testing.T) {
Expand Down
6 changes: 3 additions & 3 deletions auth/api/iam/user_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ import (
"testing"
"time"

"github.com/lestrrat-go/jwx/v2/jwa"
"github.com/lestrrat-go/jwx/v2/jwk"
"github.com/lestrrat-go/jwx/v3/jwa"
"github.com/lestrrat-go/jwx/v3/jwk"
ssi "github.com/nuts-foundation/go-did"
"github.com/nuts-foundation/go-did/did"
"github.com/nuts-foundation/go-did/vc"
Expand Down Expand Up @@ -128,7 +128,7 @@ func TestWrapper_handleUserLanding(t *testing.T) {
sessionKey, err := jwk.ParseKey(userSession.Wallet.JWK)
require.NoError(t, err)
assert.NotEmpty(t, sessionKey.KeyID)
assert.Equal(t, jwa.EC, sessionKey.KeyType())
assert.Equal(t, jwa.EC(), sessionKey.KeyType())
// check for details of issued NutsEmployeeCredential
assert.Equal(t, "NutsEmployeeCredential", employeeCredentialTemplate.Type[0].String())
employeeCredentialSubject := employeeCredentialTemplate.CredentialSubject[0]
Expand Down
2 changes: 1 addition & 1 deletion auth/api/iam/validation.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ func (r Wrapper) validatePresentationAudience(presentation vc.VerifiablePresenta
var audience []string
switch presentation.Format() {
case vc.JWTPresentationProofFormat:
audience = presentation.JWT().Audience()
audience, _ = presentation.JWT().Audience()
Comment thread
reinkrul marked this conversation as resolved.
case vc.JSONLDPresentationProofFormat:
proof, err := credential.ParseLDProof(presentation)
if err != nil {
Expand Down
Loading
Loading