Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

changed OAS to reflect path component is the ID part of a DID #2493

Merged
merged 3 commits into from
Sep 20, 2023
Merged
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
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
Loading