Skip to content

Commit

Permalink
changed OAS to reflect path component is the ID part of a DID (#2493)
Browse files Browse the repository at this point in the history
  • Loading branch information
woutslakhorst authored Sep 20, 2023
1 parent 993d127 commit 974e7aa
Show file tree
Hide file tree
Showing 4 changed files with 169 additions and 256 deletions.
79 changes: 26 additions & 53 deletions auth/api/iam/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ import (
"context"
"embed"
"errors"
"fmt"
"github.com/labstack/echo/v4"
"github.com/nuts-foundation/go-did/did"
"github.com/nuts-foundation/nuts-node/audit"
Expand All @@ -35,7 +34,6 @@ import (
vdr "github.com/nuts-foundation/nuts-node/vdr/types"
"html/template"
"net/http"
"strings"
"sync"
)

Expand Down Expand Up @@ -148,11 +146,7 @@ func (r Wrapper) HandleTokenRequest(ctx context.Context, request HandleTokenRequ

// HandleAuthorizeRequest handles calls to the authorization endpoint for starting an authorization code flow.
func (r Wrapper) HandleAuthorizeRequest(ctx context.Context, request HandleAuthorizeRequestRequestObject) (HandleAuthorizeRequestResponseObject, error) {
ownDID, err := did.ParseDID(request.Did)
if err != nil {
// TODO: Redirect instead
return nil, err
}
ownDID := idToDID(request.Id)
// Create session object to be passed to handler

// Workaround: deepmap codegen doesn't support dynamic query parameters.
Expand All @@ -162,7 +156,7 @@ func (r Wrapper) HandleAuthorizeRequest(ctx context.Context, request HandleAutho
for key, value := range httpRequest.URL.Query() {
params[key] = value[0]
}
session := createSession(params, *ownDID)
session := createSession(params, ownDID)
if session.RedirectURI == "" {
// TODO: Spec says that the redirect URI is optional, but it's not clear what to do if it's not provided.
// Threat models say it's unsafe to omit redirect_uri.
Expand Down Expand Up @@ -208,85 +202,55 @@ func (r Wrapper) HandleAuthorizeRequest(ctx context.Context, request HandleAutho

// GetOAuthAuthorizationServerMetadata returns the Authorization Server's metadata
func (r Wrapper) GetOAuthAuthorizationServerMetadata(ctx context.Context, request GetOAuthAuthorizationServerMetadataRequestObject) (GetOAuthAuthorizationServerMetadataResponseObject, error) {
id, err := did.ParseDID(request.Did)
if err != nil {
return nil, core.InvalidInputError("authz server metadata: %w", err)
}
ownDID := idToDID(request.Id)

if id.Method != "nuts" {
return nil, core.InvalidInputError("authz server metadata: only did:nuts is supported")
}

owned, err := r.vdr.IsOwner(ctx, *id)
owned, err := r.vdr.IsOwner(ctx, ownDID)
if err != nil {
if didservice.IsFunctionalResolveError(err) {
return nil, core.NotFoundError("authz server metadata: %w", err)
}
log.Logger().WithField("did", id.String()).Errorf("authz server metadata: failed to assert ownership of did: %s", err.Error())
log.Logger().WithField("did", ownDID.String()).Errorf("authz server metadata: failed to assert ownership of did: %s", err.Error())
return nil, core.Error(500, "authz server metadata: %w", err)
}
if !owned {
return nil, core.NotFoundError("authz server metadata: did not owned")
}

identity := r.auth.PublicURL().JoinPath(apiPath, id.WithoutURL().String())
identity := r.auth.PublicURL().JoinPath("iam", request.Id)

return GetOAuthAuthorizationServerMetadata200JSONResponse(authorizationServerMetadata(*identity)), nil
}

func (r Wrapper) GetWebDID(ctx context.Context, request GetWebDIDRequestObject) (GetWebDIDResponseObject, error) {
baseURL := *(r.auth.PublicURL().JoinPath(apiPath))
nutsDID, err := did.ParseDID("did:nuts:" + request.Did)
if err != nil {
return nil, err
}
ownDID := idToDID(request.Id)

document, err := r.vdr.DeriveWebDIDDocument(ctx, baseURL, *nutsDID)
document, err := r.vdr.DeriveWebDIDDocument(ctx, baseURL, ownDID)
if err != nil {
if didservice.IsFunctionalResolveError(err) {
return GetWebDID404Response{}, nil
}
log.Logger().WithError(err).Errorf("Could not resolve Nuts DID: %s", nutsDID.String())
log.Logger().WithError(err).Errorf("Could not resolve Nuts DID: %s", ownDID.String())
return nil, errors.New("unable to resolve DID")
}
return GetWebDID200JSONResponse(*document), nil
}

// GetOAuthClientMetadata returns the OAuth2 Client metadata for the request.Id if it is managed by this node.
func (r Wrapper) GetOAuthClientMetadata(ctx context.Context, request GetOAuthClientMetadataRequestObject) (GetOAuthClientMetadataResponseObject, error) {
if err := r.validateAsNutsFingerprint(ctx, request.Id); err != nil {
return nil, fmt.Errorf("client metadata: %w", err)
}

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

return GetOAuthClientMetadata200JSONResponse(clientMetadata(*identity)), nil
}

func (r Wrapper) validateAsNutsFingerprint(ctx context.Context, fingerprint string) error {
// convert fingerprint to did:nuts
if strings.HasPrefix(fingerprint, "did:") {
return core.InvalidInputError("id contains full did")
}
nutsDID, err := did.ParseDID("did:nuts:" + fingerprint)
ownDID := idToDID(request.Id)
owned, err := r.vdr.IsOwner(ctx, ownDID)
if err != nil {
return core.InvalidInputError(err.Error())
}

// assert ownership of did
owned, err := r.vdr.IsOwner(ctx, *nutsDID)
if err != nil {
if didservice.IsFunctionalResolveError(err) {
return core.NotFoundError(err.Error())
}
log.Logger().WithField("did", nutsDID.String()).Errorf("oauth metadata: failed to assert ownership of did: %s", err.Error())
return core.Error(500, err.Error())
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 core.NotFoundError("did not owned")
return nil, core.NotFoundError("did not owned")
}

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

return GetOAuthClientMetadata200JSONResponse(clientMetadata(*identity)), nil
}

func createSession(params map[string]string, ownDID did.DID) *Session {
Expand All @@ -304,3 +268,12 @@ func createSession(params map[string]string, ownDID did.DID) *Session {
}
return session
}

func idToDID(id string) did.DID {
return did.DID{
// should be changed to web when migrated to web DID
Method: "nuts",
ID: id,
DecodedID: id,
}
}
85 changes: 13 additions & 72 deletions auth/api/iam/api_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,44 +34,24 @@ import (
)

func TestWrapper_GetOAuthAuthorizationServerMetadata(t *testing.T) {
testDID := did.MustParseDID("did:nuts:123")

t.Run("ok", func(t *testing.T) {
// 200
ctx := newTestClient(t)
did := did.MustParseDID("did:nuts:123")
ctx.vdr.EXPECT().IsOwner(nil, did).Return(true, nil)
ctx.vdr.EXPECT().IsOwner(nil, testDID).Return(true, nil)

res, err := ctx.client.GetOAuthAuthorizationServerMetadata(nil, GetOAuthAuthorizationServerMetadataRequestObject{Did: did.String()})
res, err := ctx.client.GetOAuthAuthorizationServerMetadata(nil, GetOAuthAuthorizationServerMetadataRequestObject{Id: "123"})

require.NoError(t, err)
assert.IsType(t, GetOAuthAuthorizationServerMetadata200JSONResponse{}, res)
})
t.Run("error - not a did", func(t *testing.T) {
//400
ctx := newTestClient(t)

res, err := ctx.client.GetOAuthAuthorizationServerMetadata(nil, GetOAuthAuthorizationServerMetadataRequestObject{})

assert.Equal(t, 400, statusCodeFrom(err))
assert.EqualError(t, err, "authz server metadata: invalid DID")
assert.Nil(t, res)
})
t.Run("error - not a did:nuts", func(t *testing.T) {
//400
ctx := newTestClient(t)

res, err := ctx.client.GetOAuthAuthorizationServerMetadata(nil, GetOAuthAuthorizationServerMetadataRequestObject{Did: "did:web:example.com"})

assert.Equal(t, 400, statusCodeFrom(err))
assert.EqualError(t, err, "authz server metadata: only did:nuts is supported")
assert.Nil(t, res)
})
t.Run("error - did not managed by this node", func(t *testing.T) {
//404
ctx := newTestClient(t)
did := did.MustParseDID("did:nuts:123")
ctx.vdr.EXPECT().IsOwner(nil, did)
ctx.vdr.EXPECT().IsOwner(nil, testDID)

res, err := ctx.client.GetOAuthAuthorizationServerMetadata(nil, GetOAuthAuthorizationServerMetadataRequestObject{Did: did.String()})
res, err := ctx.client.GetOAuthAuthorizationServerMetadata(nil, GetOAuthAuthorizationServerMetadataRequestObject{Id: "123"})

assert.Equal(t, 404, statusCodeFrom(err))
assert.EqualError(t, err, "authz server metadata: did not owned")
Expand All @@ -80,10 +60,9 @@ func TestWrapper_GetOAuthAuthorizationServerMetadata(t *testing.T) {
t.Run("error - did does not exist", func(t *testing.T) {
//404
ctx := newTestClient(t)
did := did.MustParseDID("did:nuts:123")
ctx.vdr.EXPECT().IsOwner(nil, did).Return(false, vdr.ErrNotFound)
ctx.vdr.EXPECT().IsOwner(nil, testDID).Return(false, vdr.ErrNotFound)

res, err := ctx.client.GetOAuthAuthorizationServerMetadata(nil, GetOAuthAuthorizationServerMetadataRequestObject{Did: did.String()})
res, err := ctx.client.GetOAuthAuthorizationServerMetadata(nil, GetOAuthAuthorizationServerMetadataRequestObject{Id: "123"})

assert.Equal(t, 404, statusCodeFrom(err))
assert.EqualError(t, err, "authz server metadata: unable to find the DID document")
Expand All @@ -92,10 +71,9 @@ func TestWrapper_GetOAuthAuthorizationServerMetadata(t *testing.T) {
t.Run("error - internal error 500", func(t *testing.T) {
//500
ctx := newTestClient(t)
did := did.MustParseDID("did:nuts:123")
ctx.vdr.EXPECT().IsOwner(nil, did).Return(false, errors.New("unknown error"))
ctx.vdr.EXPECT().IsOwner(nil, testDID).Return(false, errors.New("unknown error"))

res, err := ctx.client.GetOAuthAuthorizationServerMetadata(nil, GetOAuthAuthorizationServerMetadataRequestObject{Did: did.String()})
res, err := ctx.client.GetOAuthAuthorizationServerMetadata(nil, GetOAuthAuthorizationServerMetadataRequestObject{Id: "123"})

assert.Equal(t, 500, statusCodeFrom(err))
assert.EqualError(t, err, "authz server metadata: unknown error")
Expand Down Expand Up @@ -146,71 +124,34 @@ func TestWrapper_GetWebDID(t *testing.T) {
}

func TestWrapper_GetOAuthClientMetadata(t *testing.T) {
did := did.MustParseDID("did:nuts:123")
t.Run("ok", func(t *testing.T) {
// 200
ctx := newTestClient(t)
did := did.MustParseDID("did:nuts:123")
ctx.vdr.EXPECT().IsOwner(nil, did).Return(true, nil)

res, err := ctx.client.GetOAuthClientMetadata(nil, GetOAuthClientMetadataRequestObject{Id: did.ID})

require.NoError(t, err)
assert.IsType(t, GetOAuthClientMetadata200JSONResponse{}, res)
})
t.Run("error - not a did", func(t *testing.T) {
//400
ctx := newTestClient(t)

res, err := ctx.client.GetOAuthClientMetadata(nil, GetOAuthClientMetadataRequestObject{})

assert.Equal(t, 400, statusCodeFrom(err))
assert.EqualError(t, err, "client metadata: invalid DID")
assert.Nil(t, res)
})
t.Run("error - contains full did:nuts", func(t *testing.T) {
//400
ctx := newTestClient(t)

res, err := ctx.client.GetOAuthClientMetadata(nil, GetOAuthClientMetadataRequestObject{Id: "did:nuts:123"})

assert.Equal(t, 400, statusCodeFrom(err))
assert.EqualError(t, err, "client metadata: id contains full did")
assert.Nil(t, res)
})
t.Run("error - did not managed by this node", func(t *testing.T) {
//404
ctx := newTestClient(t)
did := did.MustParseDID("did:nuts:123")
ctx.vdr.EXPECT().IsOwner(nil, did)

res, err := ctx.client.GetOAuthClientMetadata(nil, GetOAuthClientMetadataRequestObject{Id: did.ID})

assert.Equal(t, 404, statusCodeFrom(err))
assert.EqualError(t, err, "client metadata: did not owned")
assert.Nil(t, res)
})
t.Run("error - did does not exist", func(t *testing.T) {
//404
ctx := newTestClient(t)
did := did.MustParseDID("did:nuts:123")
ctx.vdr.EXPECT().IsOwner(nil, did).Return(false, vdr.ErrNotFound)

res, err := ctx.client.GetOAuthClientMetadata(nil, GetOAuthClientMetadataRequestObject{Id: did.ID})

assert.Equal(t, 404, statusCodeFrom(err))
assert.EqualError(t, err, "client metadata: unable to find the DID document")
assert.EqualError(t, err, "did not owned")
assert.Nil(t, res)
})
t.Run("error - internal error 500", func(t *testing.T) {
//500
ctx := newTestClient(t)
did := did.MustParseDID("did:nuts:123")
ctx.vdr.EXPECT().IsOwner(nil, did).Return(false, errors.New("unknown error"))

res, err := ctx.client.GetOAuthClientMetadata(nil, GetOAuthClientMetadataRequestObject{Id: did.ID})

assert.Equal(t, 500, statusCodeFrom(err))
assert.EqualError(t, err, "client metadata: unknown error")
assert.EqualError(t, err, "unknown error")
assert.Nil(t, res)
})
}
Expand Down
Loading

0 comments on commit 974e7aa

Please sign in to comment.