Skip to content

Commit

Permalink
Merge branch 'master' into iam/s2s-token-request
Browse files Browse the repository at this point in the history
  • Loading branch information
reinkrul committed Dec 8, 2023
2 parents a8d456c + 7541636 commit ffed13f
Show file tree
Hide file tree
Showing 25 changed files with 543 additions and 141 deletions.
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# golang alpine
FROM golang:1.21.4-alpine as builder
FROM golang:1.21.5-alpine as builder

ARG TARGETARCH
ARG TARGETOS
Expand Down
6 changes: 3 additions & 3 deletions auth/api/iam/metadata.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,8 @@ func authorizationServerMetadata(identity url.URL) oauth.AuthorizationServerMeta
GrantTypesSupported: grantTypesSupported,
PreAuthorizedGrantAnonymousAccessSupported: true,
PresentationDefinitionEndpoint: identity.JoinPath("presentation_definition").String(),
VPFormats: vpFormatsSupported,
VPFormatsSupported: vpFormatsSupported,
VPFormats: oauth.DefaultOpenIDSupportedFormats(),
VPFormatsSupported: oauth.DefaultOpenIDSupportedFormats(),
ClientIdSchemesSupported: clientIdSchemesSupported,
}
}
Expand All @@ -75,7 +75,7 @@ func clientMetadata(identity url.URL) OAuthClientMetadata {
SoftwareID: softwareID, // nuts-node-refimpl
SoftwareVersion: softwareVersion, // version tag or "unknown"
//CredentialOfferEndpoint: "",
VPFormats: vpFormatsSupported,
VPFormats: oauth.DefaultOpenIDSupportedFormats(),
ClientIdScheme: "did",
}
}
6 changes: 3 additions & 3 deletions auth/api/iam/metadata_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,8 +87,8 @@ func Test_authorizationServerMetadata(t *testing.T) {
GrantTypesSupported: []string{"authorization_code", "vp_token", "urn:ietf:params:oauth:grant-type:pre-authorized_code"},
PreAuthorizedGrantAnonymousAccessSupported: true,
PresentationDefinitionEndpoint: identity + "/presentation_definition",
VPFormats: vpFormatsSupported,
VPFormatsSupported: vpFormatsSupported,
VPFormats: oauth.DefaultOpenIDSupportedFormats(),
VPFormatsSupported: oauth.DefaultOpenIDSupportedFormats(),
ClientIdSchemesSupported: []string{"did"},
}
assert.Equal(t, expected, authorizationServerMetadata(*identityURL))
Expand All @@ -108,7 +108,7 @@ func Test_clientMetadata(t *testing.T) {
SoftwareID: "nuts-node-refimpl",
SoftwareVersion: "testVersion",
CredentialOfferEndpoint: "",
VPFormats: vpFormatsSupported,
VPFormats: oauth.DefaultOpenIDSupportedFormats(),
ClientIdScheme: "did",
}
assert.Equal(t, expected, clientMetadata(url.URL{}))
Expand Down
22 changes: 0 additions & 22 deletions auth/api/iam/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,28 +100,6 @@ const (

var grantTypesSupported = []string{grantTypeAuthorizationCode, grantTypeVPToken, grantTypePreAuthorizedCode}

// algValuesSupported contains a list of supported cipher suites for jwt_vc_json & jwt_vp_json presentation formats
// Recommended list of options https://www.iana.org/assignments/jose/jose.xhtml#web-signature-encryption-algorithms
// TODO: validate list, should reflect current recommendations from https://www.ncsc.nl
var algValuesSupported = []string{"PS256", "PS384", "PS512", "ES256", "ES384", "ES512"}

// proofTypeValuesSupported contains a list of supported cipher suites for ldp_vc & ldp_vp presentation formats
// Recommended list of options https://w3c-ccg.github.io/ld-cryptosuite-registry/
var proofTypeValuesSupported = []string{"JsonWebSignature2020"}

// vpFormatsSupported defines the supported formats and is used in the
// - Authorization Server's metadata field `vp_formats_supported`
// - Client's metadata field `vp_formats`
//
// TODO: spec is very unclear about this part.
// See https://github.com/nuts-foundation/nuts-node/issues/2447
var vpFormatsSupported = map[string]map[string][]string{
"jwt_vp_json": {"alg_values_supported": algValuesSupported},
"jwt_vc_json": {"alg_values_supported": algValuesSupported},
"ldp_vc": {"proof_type_values_supported": proofTypeValuesSupported},
"ldp_vp": {"proof_type_values_supported": proofTypeValuesSupported},
}

// clientIdSchemesSupported lists the supported client_id_scheme
// https://openid.bitbucket.io/connect/openid-4-verifiable-presentations-1_0.html#name-verifier-metadata-managemen
var clientIdSchemesSupported = []string{"did"}
Expand Down
25 changes: 25 additions & 0 deletions auth/oauth/openid.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package oauth

// algValuesSupported contains a list of supported cipher suites for jwt_vc_json & jwt_vp_json presentation formats
// Recommended list of options https://www.iana.org/assignments/jose/jose.xhtml#web-signature-encryption-algorithms
// TODO: validate list, should reflect current recommendations from https://www.ncsc.nl
var algValuesSupported = []string{"PS256", "PS384", "PS512", "ES256", "ES384", "ES512"}

// proofTypeValuesSupported contains a list of supported cipher suites for ldp_vc & ldp_vp presentation formats
// Recommended list of options https://w3c-ccg.github.io/ld-cryptosuite-registry/
var proofTypeValuesSupported = []string{"JsonWebSignature2020"}

// DefaultOpenIDSupportedFormats returns the OpenID formats supported by the Nuts node and is used in the
// - Authorization Server's metadata field `vp_formats_supported`
// - Client's metadata field `vp_formats`
//
// TODO: spec is very unclear about this part.
// See https://github.com/nuts-foundation/nuts-node/issues/2447
func DefaultOpenIDSupportedFormats() map[string]map[string][]string {
return map[string]map[string][]string{
"jwt_vp_json": {"alg_values_supported": algValuesSupported},
"jwt_vc_json": {"alg_values_supported": algValuesSupported},
"ldp_vc": {"proof_type_values_supported": proofTypeValuesSupported},
"ldp_vp": {"proof_type_values_supported": proofTypeValuesSupported},
}
}
46 changes: 28 additions & 18 deletions auth/services/oauth/relying_party.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import (
"crypto/tls"
"errors"
"fmt"
"github.com/nuts-foundation/nuts-node/openid4vc"
"github.com/nuts-foundation/go-did/vc"
"net/http"
"net/url"
"strings"
Expand Down Expand Up @@ -155,10 +155,23 @@ func (s *relyingParty) RequestRFC021AccessToken(ctx context.Context, requester d
// If no presentation definition matches, return a 412 "no matching credentials" error
builder := presentationDefinition.PresentationSubmissionBuilder()
builder.AddWallet(requester, walletCredentials)
format, err := determineFormat(metadata.VPFormats)
if err != nil {
return nil, err

// Find supported VP format, matching support from:
// - what the local Nuts node supports
// - the presentation definition "claimed format designation" (optional)
// - the verifier's metadata (optional)
formatCandidates := credential.OpenIDSupportedFormats(oauth.DefaultOpenIDSupportedFormats())
if metadata.VPFormats != nil {
formatCandidates = formatCandidates.Match(credential.OpenIDSupportedFormats(metadata.VPFormats))
}
if presentationDefinition.Format != nil {
formatCandidates = formatCandidates.Match(credential.DIFClaimFormats(*presentationDefinition.Format))
}
format := chooseVPFormat(formatCandidates.Map)
if format == "" {
return nil, errors.New("requester, verifier (authorization server metadata) and presentation definition don't share a supported VP format")
}
// TODO: format parameters (alg, proof_type, etc.) are ignored, but should be used in the actual signing
submission, signInstructions, err := builder.Build(format)
if err != nil {
return nil, fmt.Errorf("failed to match presentation definition: %w", err)
Expand Down Expand Up @@ -193,21 +206,18 @@ func (s *relyingParty) RequestRFC021AccessToken(ctx context.Context, requester d
}, nil
}

func determineFormat(formats map[string]map[string][]string) (string, error) {
for format := range formats {
switch format {
case openid4vc.VerifiablePresentationJSONLDFormat:
return format, nil
case openid4vc.VerifiablePresentationJWTFormat:
fallthrough
case "jwt_vp_json":
return openid4vc.VerifiablePresentationJWTFormat, nil
default:
continue
}
func chooseVPFormat(formats map[string]map[string][]string) string {
// They are in preferred order
if _, ok := formats[vc.JWTPresentationProofFormat]; ok {
return vc.JWTPresentationProofFormat
}

return "", errors.New("authorization server metadata does not contain any supported VP formats")
if _, ok := formats["jwt_vp_json"]; ok {
return vc.JWTPresentationProofFormat
}
if _, ok := formats[vc.JSONLDPresentationProofFormat]; ok {
return vc.JSONLDPresentationProofFormat
}
return ""
}

var timeFunc = time.Now
Expand Down
33 changes: 20 additions & 13 deletions auth/services/oauth/relying_party_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,18 @@ func TestRelyingParty_RequestRFC021AccessToken(t *testing.T) {
assert.Equal(t, "token", response.AccessToken)
assert.Equal(t, "bearer", response.TokenType)
})
t.Run("authorization server supported VP formats don't match", func(t *testing.T) {
ctx := createOAuthRPContext(t)
ctx.authzServerMetadata.VPFormats = map[string]map[string][]string{
"unsupported": nil,
}
ctx.wallet.EXPECT().List(gomock.Any(), walletDID).Return(credentials, nil)

response, err := ctx.relyingParty.RequestRFC021AccessToken(context.Background(), walletDID, ctx.verifierDID, scopes)

assert.EqualError(t, err, "requester, verifier (authorization server metadata) and presentation definition don't share a supported VP format")
assert.Nil(t, response)
})
t.Run("error - access denied", func(t *testing.T) {
oauthError := oauth.OAuth2Error{
Code: "invalid_scope",
Expand Down Expand Up @@ -210,25 +222,23 @@ func TestRelyingParty_RequestRFC021AccessToken(t *testing.T) {
})
}

func Test_determineFormat(t *testing.T) {
func Test_chooseVPFormat(t *testing.T) {
t.Run("ok", func(t *testing.T) {
formats := map[string]map[string][]string{
"jwt_vp": {
"alg": {"ES256K"},
},
}

format, err := determineFormat(formats)
format := chooseVPFormat(formats)

assert.NoError(t, err)
assert.Equal(t, "jwt_vp", format)
})
t.Run("error - no supported format", func(t *testing.T) {
t.Run("no supported format", func(t *testing.T) {
formats := map[string]map[string][]string{}

format, err := determineFormat(formats)
format := chooseVPFormat(formats)

assert.EqualError(t, err, "authorization server metadata does not contain any supported VP formats")
assert.Empty(t, format)
})
t.Run(" jwt_vp_json returns jwt_vp", func(t *testing.T) {
Expand All @@ -238,9 +248,8 @@ func Test_determineFormat(t *testing.T) {
},
}

format, err := determineFormat(formats)
format := chooseVPFormat(formats)

assert.NoError(t, err)
assert.Equal(t, "jwt_vp", format)
})
}
Expand Down Expand Up @@ -416,7 +425,7 @@ func createRPContext(t *testing.T, tlsConfig *tls.Config) *rpTestContext {

type rpOAuthTestContext struct {
*rpTestContext
authzServerMetadata oauth.AuthorizationServerMetadata
authzServerMetadata *oauth.AuthorizationServerMetadata
handler http.HandlerFunc
tlsServer *httptest.Server
verifierDID did.DID
Expand Down Expand Up @@ -449,15 +458,13 @@ func createOAuthRPContext(t *testing.T) *rpOAuthTestContext {
]
}
`
formats := make(map[string]map[string][]string)
formats["jwt_vp"] = make(map[string][]string)
authzServerMetadata := oauth.AuthorizationServerMetadata{VPFormats: formats}
authzServerMetadata := &oauth.AuthorizationServerMetadata{VPFormats: oauth.DefaultOpenIDSupportedFormats()}
ctx := &rpOAuthTestContext{
rpTestContext: createRPContext(t, nil),
metadata: func(writer http.ResponseWriter) {
writer.Header().Add("Content-Type", "application/json")
writer.WriteHeader(http.StatusOK)
bytes, _ := json.Marshal(authzServerMetadata)
bytes, _ := json.Marshal(*authzServerMetadata)
_, _ = writer.Write(bytes)
return
},
Expand Down
30 changes: 30 additions & 0 deletions e2e-tests/storage/postgres/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
version: "3.7"
services:
node:
image: "${IMAGE_NODE_A:-nutsfoundation/nuts-node:master}"
environment:
NUTS_CONFIGFILE: /opt/nuts/nuts.yaml
volumes:
- "./nuts.yaml:/opt/nuts/nuts.yaml:ro"
- "../../tls-certs/nodeA-certificate.pem:/opt/nuts/certificate-and-key.pem:ro"
- "../../tls-certs/truststore.pem:/opt/nuts/truststore.pem:ro"
ports:
- "1323:1323"
healthcheck:
interval: 1s # Make test run quicker by checking health status more often
depends_on:
db:
condition: service_healthy
db:
image: postgres:16-alpine
restart: always
ports:
- "5432:5432"
environment:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
healthcheck:
test: [ "CMD-SHELL", "pg_isready -U postgres" ] # this makes sure the container only reports healthy it can be connected to
interval: 1s
timeout: 5s
retries: 20
16 changes: 16 additions & 0 deletions e2e-tests/storage/postgres/nuts.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
url: https://node
verbosity: debug
auth:
contractvalidators:
- dummy
irma:
autoupdateschemas: false
tls:
truststorefile: /opt/nuts/truststore.pem
certfile: /opt/nuts/certificate-and-key.pem
certkeyfile: /opt/nuts/certificate-and-key.pem
crypto:
storage: fs
storage:
sql:
connection: postgres://postgres:postgres@db:5432/postgres?sslmode=disable
23 changes: 23 additions & 0 deletions e2e-tests/storage/postgres/run-test.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#!/usr/bin/env bash

source ../../util.sh

echo "------------------------------------"
echo "Cleaning up running Docker containers and volumes, and key material..."
echo "------------------------------------"
docker compose stop
docker compose rm -f -v

echo "------------------------------------"
echo "Starting Docker containers..."
echo "------------------------------------"
docker compose up --wait
if [ $? -ne 0 ]; then
echo "ERROR: node failed to start"
exitWithDockerLogs 1
fi

echo "------------------------------------"
echo "Stopping Docker containers..."
echo "------------------------------------"
docker compose stop
7 changes: 7 additions & 0 deletions e2e-tests/storage/run-tests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,13 @@ pushd redis
./run-test.sh
popd

echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
echo "!! Running test: Postgres !!"
echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
pushd postgres
./run-test.sh
popd

echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
echo "!! Running test: Backup/Restore !!"
echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
Expand Down
Loading

0 comments on commit ffed13f

Please sign in to comment.