Skip to content

Commit

Permalink
fixes after rebase
Browse files Browse the repository at this point in the history
  • Loading branch information
reinkrul committed Dec 11, 2023
1 parent 4d796c1 commit 12641ed
Show file tree
Hide file tree
Showing 7 changed files with 52 additions and 87 deletions.
45 changes: 15 additions & 30 deletions auth/api/iam/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,6 @@ func (r Wrapper) HandleTokenRequest(ctx context.Context, request HandleTokenRequ
if err != nil {
return nil, err
}

switch request.Body.GrantType {
case "authorization_code":
// Options:
Expand All @@ -140,13 +139,6 @@ func (r Wrapper) HandleTokenRequest(ctx context.Context, request HandleTokenRequ
Code: oauth.UnsupportedGrantType,
Description: "not implemented yet",
}
case "vp_token-bearer":
// Options:
// - service-to-service vp_token flow
return nil, oauth.OAuth2Error{
Code: oauth.UnsupportedGrantType,
Description: "not implemented yet",
}
case "urn:ietf:params:oauth:grant-type:pre-authorized_code":
// Options:
// - OpenID4VCI
Expand Down Expand Up @@ -324,7 +316,6 @@ func (r Wrapper) OAuthAuthorizationServerMetadata(ctx context.Context, request O

func (r Wrapper) GetWebDID(_ context.Context, request GetWebDIDRequestObject) (GetWebDIDResponseObject, error) {
ownDID := r.idToDID(request.Id)

document, err := r.vdr.ResolveManaged(ownDID)
if err != nil {
if resolver.IsFunctionalResolveError(err) {
Expand All @@ -338,19 +329,12 @@ func (r Wrapper) GetWebDID(_ context.Context, request GetWebDIDRequestObject) (G

// OAuthClientMetadata returns the OAuth2 Client metadata for the request.Id if it is managed by this node.
func (r Wrapper) OAuthClientMetadata(ctx context.Context, request OAuthClientMetadataRequestObject) (OAuthClientMetadataResponseObject, error) {
ownDID := idToDID(request.Id)
owned, err := r.vdr.IsOwner(ctx, ownDID)
_, err := r.idToOwnedDID(ctx, request.Id)
if err != nil {
log.Logger().WithField("did", ownDID.String()).Errorf("oauth metadata: failed to assert ownership of did: %s", err.Error())
return nil, core.Error(500, err.Error())
}
if !owned {
return nil, core.NotFoundError("did not owned")
return nil, err
}

identity := r.auth.PublicURL().JoinPath("iam", request.Id)

return OAuthClientMetadata200JSONResponse(clientMetadata(*identity)), nil
return OAuthClientMetadata200JSONResponse(clientMetadata(*r.identityURL(request.Id))), nil
}
func (r Wrapper) PresentationDefinition(_ context.Context, request PresentationDefinitionRequestObject) (PresentationDefinitionResponseObject, error) {
if len(request.Params.Scope) == 0 {
Expand All @@ -371,7 +355,7 @@ func (r Wrapper) PresentationDefinition(_ context.Context, request PresentationD
}

func (r Wrapper) idToOwnedDID(ctx context.Context, id string) (*did.DID, error) {
ownDID := idToDID(id)
ownDID := r.idToDID(id)
owned, err := r.vdr.IsOwner(ctx, ownDID)
if err != nil {
if resolver.IsFunctionalResolveError(err) {
Expand Down Expand Up @@ -406,19 +390,20 @@ func createSession(params map[string]string, ownDID did.DID) *Session {
}
return session
}

// idToDID converts the tenant-specific part of a did:web DID (e.g. 123)
// to a fully qualified did:web DID (e.g. did:web:example.com:123), using the configured Nuts node URL.
func (r Wrapper) idToDID(id string) did.DID {
url := r.auth.PublicURL().JoinPath("iam", id)
did, _ := didweb.URLToDID(*url)
return *did
identityURL := r.identityURL(id)
result, _ := didweb.URLToDID(*identityURL)
return *result
}

func idToNutsDID(id string) did.DID {
return did.DID{
// should be changed to web when migrated to web DID
Method: "nuts",
ID: id,
DecodedID: id,
}
// identityURL the tenant-specific part of a did:web DID (e.g. 123)
// to an identity URL (e.g. did:web:example.com:123), which is used as base URL for resolving metadata and its did:web DID,
// using the configured Nuts node URL.
func (r Wrapper) identityURL(id string) *url.URL {
return r.auth.PublicURL().JoinPath("iam", id)
}

func (r *Wrapper) accessTokenStore() storage.SessionStore {
Expand Down
52 changes: 16 additions & 36 deletions auth/api/iam/api_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,6 @@ import (
"time"
)

var nutsDID = did.MustParseDID("did:nuts:123")
var webDID = did.MustParseDID("did:web:example.com:iam:123")
var webIDPart = "123"

Expand Down Expand Up @@ -142,32 +141,13 @@ func TestWrapper_GetWebDID(t *testing.T) {
func TestWrapper_GetOAuthClientMetadata(t *testing.T) {
t.Run("ok", func(t *testing.T) {
ctx := newTestClient(t)
ctx.vdr.EXPECT().IsOwner(nil, nutsDID).Return(true, nil)
ctx.vdr.EXPECT().IsOwner(nil, webDID).Return(true, nil)

res, err := ctx.client.OAuthClientMetadata(nil, OAuthClientMetadataRequestObject{Id: nutsDID.ID})
res, err := ctx.client.OAuthClientMetadata(nil, OAuthClientMetadataRequestObject{Id: webIDPart})

require.NoError(t, err)
assert.IsType(t, OAuthClientMetadata200JSONResponse{}, res)
})
t.Run("error - did not managed by this node", func(t *testing.T) {
ctx := newTestClient(t)
ctx.vdr.EXPECT().IsOwner(nil, nutsDID)

res, err := ctx.client.OAuthClientMetadata(nil, OAuthClientMetadataRequestObject{Id: nutsDID.ID})

assert.Equal(t, 404, statusCodeFrom(err))
assert.Nil(t, res)
})
t.Run("error - internal error 500", func(t *testing.T) {
ctx := newTestClient(t)
ctx.vdr.EXPECT().IsOwner(nil, nutsDID).Return(false, errors.New("unknown error"))

res, err := ctx.client.OAuthClientMetadata(nil, OAuthClientMetadataRequestObject{Id: nutsDID.ID})

assert.Equal(t, 500, statusCodeFrom(err))
assert.EqualError(t, err, "unknown error")
assert.Nil(t, res)
})
}
func TestWrapper_PresentationDefinition(t *testing.T) {
webDID := did.MustParseDID("did:web:example.com:iam:123")
Expand Down Expand Up @@ -211,24 +191,24 @@ func TestWrapper_PresentationDefinition(t *testing.T) {
func TestWrapper_HandleAuthorizeRequest(t *testing.T) {
t.Run("missing redirect_uri", func(t *testing.T) {
ctx := newTestClient(t)
ctx.vdr.EXPECT().IsOwner(gomock.Any(), nutsDID).Return(true, nil)
ctx.vdr.EXPECT().IsOwner(gomock.Any(), webDID).Return(true, nil)

res, err := ctx.client.HandleAuthorizeRequest(requestContext(map[string]string{}), HandleAuthorizeRequestRequestObject{
Id: nutsDID.ID,
Id: webIDPart,
})

requireOAuthError(t, err, oauth.InvalidRequest, "redirect_uri is required")
assert.Nil(t, res)
})
t.Run("unsupported response type", func(t *testing.T) {
ctx := newTestClient(t)
ctx.vdr.EXPECT().IsOwner(gomock.Any(), nutsDID).Return(true, nil)
ctx.vdr.EXPECT().IsOwner(gomock.Any(), webDID).Return(true, nil)

res, err := ctx.client.HandleAuthorizeRequest(requestContext(map[string]string{
"redirect_uri": "https://example.com",
"response_type": "unsupported",
}), HandleAuthorizeRequestRequestObject{
Id: nutsDID.ID,
Id: webIDPart,
})

requireOAuthError(t, err, oauth.UnsupportedResponseType, "")
Expand All @@ -239,10 +219,10 @@ func TestWrapper_HandleAuthorizeRequest(t *testing.T) {
func TestWrapper_HandleTokenRequest(t *testing.T) {
t.Run("unsupported grant type", func(t *testing.T) {
ctx := newTestClient(t)
ctx.vdr.EXPECT().IsOwner(gomock.Any(), nutsDID).Return(true, nil)
ctx.vdr.EXPECT().IsOwner(gomock.Any(), webDID).Return(true, nil)

res, err := ctx.client.HandleTokenRequest(nil, HandleTokenRequestRequestObject{
Id: nutsDID.ID,
Id: webIDPart,
Body: &HandleTokenRequestFormdataRequestBody{
GrantType: "unsupported",
},
Expand Down Expand Up @@ -476,33 +456,33 @@ func TestWrapper_middleware(t *testing.T) {
func TestWrapper_idToOwnedDID(t *testing.T) {
t.Run("ok", func(t *testing.T) {
ctx := newTestClient(t)
ctx.vdr.EXPECT().IsOwner(nil, nutsDID).Return(true, nil)
ctx.vdr.EXPECT().IsOwner(nil, webDID).Return(true, nil)

_, err := ctx.client.idToOwnedDID(nil, nutsDID.ID)
_, err := ctx.client.idToOwnedDID(nil, webIDPart)

assert.NoError(t, err)
})
t.Run("error - did not managed by this node", func(t *testing.T) {
ctx := newTestClient(t)
ctx.vdr.EXPECT().IsOwner(nil, nutsDID)
ctx.vdr.EXPECT().IsOwner(nil, webDID)

_, err := ctx.client.idToOwnedDID(nil, nutsDID.ID)
_, err := ctx.client.idToOwnedDID(nil, webIDPart)

assert.EqualError(t, err, "invalid_request - issuer DID not owned by the server")
})
t.Run("DID does not exist (functional resolver error)", func(t *testing.T) {
ctx := newTestClient(t)
ctx.vdr.EXPECT().IsOwner(nil, nutsDID).Return(false, resolver.ErrNotFound)
ctx.vdr.EXPECT().IsOwner(nil, webDID).Return(false, resolver.ErrNotFound)

_, err := ctx.client.idToOwnedDID(nil, nutsDID.ID)
_, err := ctx.client.idToOwnedDID(nil, webIDPart)

assert.EqualError(t, err, "invalid_request - invalid issuer DID: unable to find the DID document")
})
t.Run("other resolver error", func(t *testing.T) {
ctx := newTestClient(t)
ctx.vdr.EXPECT().IsOwner(nil, nutsDID).Return(false, errors.New("unknown error"))
ctx.vdr.EXPECT().IsOwner(nil, webDID).Return(false, errors.New("unknown error"))

_, err := ctx.client.idToOwnedDID(nil, nutsDID.ID)
_, err := ctx.client.idToOwnedDID(nil, webIDPart)

assert.EqualError(t, err, "DID resolution failed: unknown error")
})
Expand Down
8 changes: 3 additions & 5 deletions auth/api/iam/s2s_vptoken.go
Original file line number Diff line number Diff line change
Expand Up @@ -299,17 +299,15 @@ func (r *Wrapper) validatePresentationAudience(presentation vc.VerifiablePresent
audience = []string{*proof.Domain}
}
}
// Callers use the did:web variant, so we need to check against that instead of did:nuts
webDID := nutsToWebDID(issuer, *r.auth.PublicURL())
for _, aud := range audience {
if aud == webDID.String() {
if aud == issuer.String() {
return nil
}
}
return oauth.OAuth2Error{
Code: oauth.InvalidRequest,
Description: "presentation audience is missing or does not match",
InternalError: fmt.Errorf("expected: %s, got: %v", webDID.String(), audience),
Description: "presentation audience/domain is missing or does not match",
InternalError: fmt.Errorf("expected: %s, got: %v", issuer, audience),
}
}

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 @@ -310,7 +310,7 @@ func TestWrapper_handleS2SAccessTokenRequest(t *testing.T) {

resp, err := ctx.client.handleS2SAccessTokenRequest(issuerDID, requestedScope, submissionJSON, presentation.Raw())

assert.EqualError(t, err, "invalid_request - presentation audience is missing or does not match")
assert.EqualError(t, err, "invalid_request - expected: did:web:example.com:iam:123, got: [] - presentation audience/domain is missing or does not match")
assert.Nil(t, resp)
})
t.Run("not matching", func(t *testing.T) {
Expand All @@ -321,7 +321,7 @@ func TestWrapper_handleS2SAccessTokenRequest(t *testing.T) {

resp, err := ctx.client.handleS2SAccessTokenRequest(issuerDID, requestedScope, submissionJSON, presentation.Raw())

assert.EqualError(t, err, "invalid_request - presentation audience is missing or does not match")
assert.EqualError(t, err, "invalid_request - expected: did:web:example.com:iam:123, got: [did:example:other] - presentation audience/domain is missing or does not match")
assert.Nil(t, resp)
})
})
Expand Down
24 changes: 12 additions & 12 deletions vcr/credential/util_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ func TestPresenterIsCredentialSubject(t *testing.T) {
subjectDID := did.MustParseDID("did:test:123")
keyID := ssi.MustParseURI("did:test:123#1")
t.Run("ok", func(t *testing.T) {
vp := vc.VerifiablePresentation{
vp := test.ParsePresentation(t, vc.VerifiablePresentation{
Proof: []interface{}{
proof.LDProof{
Type: "JsonWebSignature2020",
Expand All @@ -82,7 +82,7 @@ func TestPresenterIsCredentialSubject(t *testing.T) {
CredentialSubject: []interface{}{map[string]interface{}{"id": subjectDID}},
},
},
}
})
is, err := PresenterIsCredentialSubject(vp)
assert.NoError(t, err)
assert.Equal(t, subjectDID, *is)
Expand All @@ -94,7 +94,7 @@ func TestPresenterIsCredentialSubject(t *testing.T) {
assert.Nil(t, actual)
})
t.Run("no VC subject", func(t *testing.T) {
vp := vc.VerifiablePresentation{
vp := test.ParsePresentation(t, vc.VerifiablePresentation{
Proof: []interface{}{
proof.LDProof{
Type: "JsonWebSignature2020",
Expand All @@ -104,13 +104,13 @@ func TestPresenterIsCredentialSubject(t *testing.T) {
VerifiableCredential: []vc.VerifiableCredential{
{},
},
}
})
is, err := PresenterIsCredentialSubject(vp)
assert.EqualError(t, err, "unable to get subject DID from VC: there must be at least 1 credentialSubject")
assert.Nil(t, is)
})
t.Run("no VC subject ID", func(t *testing.T) {
vp := vc.VerifiablePresentation{
vp := test.ParsePresentation(t, vc.VerifiablePresentation{
Proof: []interface{}{
proof.LDProof{
Type: "JsonWebSignature2020",
Expand All @@ -122,13 +122,13 @@ func TestPresenterIsCredentialSubject(t *testing.T) {
CredentialSubject: []interface{}{map[string]interface{}{}},
},
},
}
})
is, err := PresenterIsCredentialSubject(vp)
assert.EqualError(t, err, "unable to get subject DID from VC: credential subjects have no ID")
assert.Nil(t, is)
})
t.Run("proof verification method does not equal VC subject ID", func(t *testing.T) {
vp := vc.VerifiablePresentation{
vp := test.ParsePresentation(t, vc.VerifiablePresentation{
Proof: []interface{}{
proof.LDProof{
Type: "JsonWebSignature2020",
Expand All @@ -140,13 +140,13 @@ func TestPresenterIsCredentialSubject(t *testing.T) {
CredentialSubject: []interface{}{map[string]interface{}{"id": did.MustParseDID("did:test:456")}},
},
},
}
})
is, err := PresenterIsCredentialSubject(vp)
assert.NoError(t, err)
assert.Nil(t, is)
})
t.Run("proof type is unsupported", func(t *testing.T) {
vp := vc.VerifiablePresentation{
vp := test.ParsePresentation(t, vc.VerifiablePresentation{
Proof: []interface{}{
true,
},
Expand All @@ -155,13 +155,13 @@ func TestPresenterIsCredentialSubject(t *testing.T) {
CredentialSubject: []interface{}{map[string]interface{}{"id": subjectDID}},
},
},
}
})
is, err := PresenterIsCredentialSubject(vp)
assert.EqualError(t, err, "invalid LD-proof for presentation: json: cannot unmarshal bool into Go value of type proof.LDProof")
assert.Nil(t, is)
})
t.Run("too many proofs", func(t *testing.T) {
vp := vc.VerifiablePresentation{
vp := test.ParsePresentation(t, vc.VerifiablePresentation{
Proof: []interface{}{
proof.LDProof{},
proof.LDProof{},
Expand All @@ -171,7 +171,7 @@ func TestPresenterIsCredentialSubject(t *testing.T) {
CredentialSubject: []interface{}{map[string]interface{}{"id": subjectDID}},
},
},
}
})
is, err := PresenterIsCredentialSubject(vp)
assert.EqualError(t, err, "presentation should have exactly 1 proof, got 2")
assert.Nil(t, is)
Expand Down
5 changes: 3 additions & 2 deletions vcr/pe/presentation_definition_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -672,8 +672,9 @@ func Test_matchFilter(t *testing.T) {
}

func TestPresentationDefinition_ResolveConstraintsFields(t *testing.T) {
jwtCredential := credential.JWTNutsOrganizationCredential(t)
jsonldCredential := credential.JWTNutsOrganizationCredential(t)
subjectDID := did.MustParseDID("did:web:example.com")
jwtCredential := credential.JWTNutsOrganizationCredential(t, subjectDID)
jsonldCredential := credential.JWTNutsOrganizationCredential(t, subjectDID)
definition := definitions().JSONLDorJWT
t.Run("match JWT", func(t *testing.T) {
credentialMap := map[string]vc.VerifiableCredential{
Expand Down
1 change: 1 addition & 0 deletions vcr/test/test.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ func CreateJWTPresentation(t *testing.T, subjectDID did.DID, tokenVisitor func(t
jwt.JwtIDKey: subjectDID.String() + "#" + uuid.NewString(),
jwt.NotBeforeKey: time.Now().Unix(),
jwt.ExpirationKey: time.Now().Add(5 * time.Second).Unix(),
"nonce": crypto.GenerateNonce(),
"vp": vc.VerifiablePresentation{
Type: []ssi.URI{vc.VerifiablePresentationTypeV1URI()},
VerifiableCredential: credentials,
Expand Down

0 comments on commit 12641ed

Please sign in to comment.