Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
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
60 changes: 0 additions & 60 deletions .circleci/config.yml

This file was deleted.

4 changes: 3 additions & 1 deletion .github/workflows/build-images.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,9 @@ jobs:
with:
context: .
file: Dockerfile
platforms: linux/amd64,linux/arm64
# On PRs we only verify the build doesn't break; arm64 under QEMU
# is ~5-8x slower and adds no signal there. Multi-arch on push/tag.
platforms: ${{ github.event_name == 'pull_request' && 'linux/amd64' || 'linux/amd64,linux/arm64' }}
push: ${{ github.event_name != 'pull_request' }}
tags: ${{ steps.docker_meta.outputs.tags }}
build-args: |
Expand Down
4 changes: 4 additions & 0 deletions .github/workflows/e2e-tests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ jobs:
platforms: linux/amd64
push: ${{ github.event_name == 'pull_request' || github.event_name == 'push' }}
tags: ghcr.io/nuts-foundation/nuts-node-ci:${{ env.SHA }}
cache-from: type=gha,scope=e2e
cache-to: type=gha,scope=e2e,mode=max
secrets: |
GIT_AUTH_TOKEN=${{ secrets.PACKAGE_SECRET }}

Expand All @@ -72,6 +74,8 @@ jobs:
platforms: linux/amd64
push: ${{ github.event_name == 'pull_request' || github.event_name == 'push' }}
tags: ghcr.io/nuts-foundation/nuts-node-ci:${{ env.SHA }}
cache-from: type=gha,scope=e2e
cache-to: type=gha,scope=e2e,mode=max

- name: Run E2E tests
run: |
Expand Down
41 changes: 41 additions & 0 deletions .github/workflows/go-test.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
name: 'Go test'

on:
push:
branches:
- 'master'
- 'V*'
pull_request:

jobs:
test:
runs-on: ubuntu-latest
permissions:
contents: read
id-token: write
steps:
- name: Checkout
uses: actions/checkout@v6
with:
persist-credentials: false

- name: Set up Go
uses: actions/setup-go@v6
with:
go-version-file: 'go.mod'

- name: Test
run: go test ./... -race -coverprofile=c_raw.out

- name: Filter coverage
run: grep -v generated c_raw.out | grep -v mock | grep -v test > c.out

- name: Upload coverage to Qlty
# Skip on PRs from forks: GitHub does not grant id-token: write to
# cross-repository workflow runs, so the OIDC upload cannot authenticate.
if: github.event_name == 'push' || github.event.pull_request.head.repo.full_name == github.repository
uses: qltysh/qlty-action/coverage@a19242102d17e497f437d7466aa01b528537e899 # v2.2.0
with:
oidc: true
files: c.out
format: coverprofile
4 changes: 2 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# golang alpine
FROM golang:1.26.1-alpine AS builder
FROM golang:1.26.5-alpine AS builder

ARG TARGETARCH
ARG TARGETOS
Expand All @@ -17,11 +17,11 @@
COPY go.sum .
RUN go mod download && go mod verify

COPY . .

Check warning on line 20 in Dockerfile

View workflow job for this annotation

GitHub Actions / e2e-test

Attempting to Copy file that is excluded by .dockerignore

CopyIgnoredFile: Attempting to Copy file "." that is excluded by .dockerignore More info: https://docs.docker.com/go/dockerfile/rule/copy-ignored-file/
RUN GOOS=$TARGETOS GOARCH=$TARGETARCH go build -ldflags="-w -s -X 'github.com/nuts-foundation/nuts-node/core.GitCommit=${GIT_COMMIT}' -X 'github.com/nuts-foundation/nuts-node/core.GitBranch=${GIT_BRANCH}' -X 'github.com/nuts-foundation/nuts-node/core.GitVersion=${GIT_VERSION}'" -o /opt/nuts/nuts

# alpine
FROM alpine:3.23.3
FROM alpine:3.23.4
RUN apk update \
&& apk add --no-cache \
tzdata \
Expand Down
12 changes: 8 additions & 4 deletions auth/api/iam/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,8 @@ import (
"time"

"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 @@ -410,7 +410,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 @@ -661,7 +665,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 @@ -1503,7 +1503,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()
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"
)
Expand Down Expand Up @@ -158,7 +159,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 @@ -188,7 +189,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,14 +23,14 @@ 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"

"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 @@ -109,7 +109,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 @@ -306,7 +306,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 @@ -335,7 +335,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
2 changes: 1 addition & 1 deletion auth/api/iam/openid4vci_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -272,7 +272,7 @@ func TestWrapper_handleOpenID4VCICallback(t *testing.T) {
callback, err := ctx.client.handleOpenID4VCICallback(nil, code, &session)

assert.Nil(t, callback)
assert.EqualError(t, err, "server_error - error while parsing the credential: super invalid, error: invalid JWT")
assert.EqualError(t, err, "server_error - error while parsing the credential: super invalid, error: jwt.Parse: failed to parse token: unknown payload type (payload is not JWT?)")
})
t.Run("fail_verify", func(t *testing.T) {
ctx := newTestClient(t)
Expand Down
Loading
Loading