diff --git a/auth/api/iam/api.go b/auth/api/iam/api.go index a59f44b9ea..f2ad0a5e7b 100644 --- a/auth/api/iam/api.go +++ b/auth/api/iam/api.go @@ -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" @@ -35,7 +34,6 @@ import ( vdr "github.com/nuts-foundation/nuts-node/vdr/types" "html/template" "net/http" - "strings" "sync" ) @@ -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. @@ -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. @@ -208,45 +202,35 @@ 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 @@ -254,39 +238,19 @@ func (r Wrapper) GetWebDID(ctx context.Context, request GetWebDIDRequestObject) // 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 { @@ -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, + } +} diff --git a/auth/api/iam/api_test.go b/auth/api/iam/api_test.go index 6d33e041a9..e88713018a 100644 --- a/auth/api/iam/api_test.go +++ b/auth/api/iam/api_test.go @@ -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") @@ -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") @@ -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") @@ -146,10 +124,9 @@ 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}) @@ -157,60 +134,24 @@ func TestWrapper_GetOAuthClientMetadata(t *testing.T) { 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) }) } diff --git a/auth/api/iam/generated.go b/auth/api/iam/generated.go index dcc1c883b4..93a7586ea4 100644 --- a/auth/api/iam/generated.go +++ b/auth/api/iam/generated.go @@ -141,20 +141,20 @@ func (a HandleTokenRequestFormdataBody) MarshalJSON() ([]byte, error) { // ServerInterface represents all server handlers. type ServerInterface interface { // Get the OAuth2 Authorization Server metadata - // (GET /.well-known/oauth-authorization-server/iam/{did}) - GetOAuthAuthorizationServerMetadata(ctx echo.Context, did string) error + // (GET /.well-known/oauth-authorization-server/iam/{id}) + GetOAuthAuthorizationServerMetadata(ctx echo.Context, id string) error // Used by resource owners to initiate the authorization code flow. - // (GET /iam/{did}/authorize) - HandleAuthorizeRequest(ctx echo.Context, did string, params HandleAuthorizeRequestParams) error + // (GET /iam/{id}/authorize) + HandleAuthorizeRequest(ctx echo.Context, id string, params HandleAuthorizeRequestParams) error // Returns the did:web version of a Nuts DID document - // (GET /iam/{did}/did.json) - GetWebDID(ctx echo.Context, did string) error - // Used by to request access- or refresh tokens. - // (POST /iam/{did}/token) - HandleTokenRequest(ctx echo.Context, did string) error + // (GET /iam/{id}/did.json) + GetWebDID(ctx echo.Context, id string) error // Get the OAuth2 Client metadata // (GET /iam/{id}/oauth-client) GetOAuthClientMetadata(ctx echo.Context, id string) error + // Used by to request access- or refresh tokens. + // (POST /iam/{id}/token) + HandleTokenRequest(ctx echo.Context, id string) error // Requests an access token using the vp_token-bearer grant. // (POST /internal/auth/v2/{did}/request-access-token) RequestAccessToken(ctx echo.Context, did string) error @@ -168,28 +168,28 @@ type ServerInterfaceWrapper struct { // GetOAuthAuthorizationServerMetadata converts echo context to params. func (w *ServerInterfaceWrapper) GetOAuthAuthorizationServerMetadata(ctx echo.Context) error { var err error - // ------------- Path parameter "did" ------------- - var did string + // ------------- Path parameter "id" ------------- + var id string - err = runtime.BindStyledParameterWithLocation("simple", false, "did", runtime.ParamLocationPath, ctx.Param("did"), &did) + err = runtime.BindStyledParameterWithLocation("simple", false, "id", runtime.ParamLocationPath, ctx.Param("id"), &id) if err != nil { - return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter did: %s", err)) + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter id: %s", err)) } // Invoke the callback with all the unmarshaled arguments - err = w.Handler.GetOAuthAuthorizationServerMetadata(ctx, did) + err = w.Handler.GetOAuthAuthorizationServerMetadata(ctx, id) return err } // HandleAuthorizeRequest converts echo context to params. func (w *ServerInterfaceWrapper) HandleAuthorizeRequest(ctx echo.Context) error { var err error - // ------------- Path parameter "did" ------------- - var did string + // ------------- Path parameter "id" ------------- + var id string - err = runtime.BindStyledParameterWithLocation("simple", false, "did", runtime.ParamLocationPath, ctx.Param("did"), &did) + err = runtime.BindStyledParameterWithLocation("simple", false, "id", runtime.ParamLocationPath, ctx.Param("id"), &id) if err != nil { - return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter did: %s", err)) + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter id: %s", err)) } // Parameter object where we will unmarshal all parameters from the context @@ -202,44 +202,44 @@ func (w *ServerInterfaceWrapper) HandleAuthorizeRequest(ctx echo.Context) error } // Invoke the callback with all the unmarshaled arguments - err = w.Handler.HandleAuthorizeRequest(ctx, did, params) + err = w.Handler.HandleAuthorizeRequest(ctx, id, params) return err } // GetWebDID converts echo context to params. func (w *ServerInterfaceWrapper) GetWebDID(ctx echo.Context) error { var err error - // ------------- Path parameter "did" ------------- - var did string + // ------------- Path parameter "id" ------------- + var id string - err = runtime.BindStyledParameterWithLocation("simple", false, "did", runtime.ParamLocationPath, ctx.Param("did"), &did) + err = runtime.BindStyledParameterWithLocation("simple", false, "id", runtime.ParamLocationPath, ctx.Param("id"), &id) if err != nil { - return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter did: %s", err)) + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter id: %s", err)) } // Invoke the callback with all the unmarshaled arguments - err = w.Handler.GetWebDID(ctx, did) + err = w.Handler.GetWebDID(ctx, id) return err } -// HandleTokenRequest converts echo context to params. -func (w *ServerInterfaceWrapper) HandleTokenRequest(ctx echo.Context) error { +// GetOAuthClientMetadata converts echo context to params. +func (w *ServerInterfaceWrapper) GetOAuthClientMetadata(ctx echo.Context) error { var err error - // ------------- Path parameter "did" ------------- - var did string + // ------------- Path parameter "id" ------------- + var id string - err = runtime.BindStyledParameterWithLocation("simple", false, "did", runtime.ParamLocationPath, ctx.Param("did"), &did) + err = runtime.BindStyledParameterWithLocation("simple", false, "id", runtime.ParamLocationPath, ctx.Param("id"), &id) if err != nil { - return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter did: %s", err)) + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter id: %s", err)) } // Invoke the callback with all the unmarshaled arguments - err = w.Handler.HandleTokenRequest(ctx, did) + err = w.Handler.GetOAuthClientMetadata(ctx, id) return err } -// GetOAuthClientMetadata converts echo context to params. -func (w *ServerInterfaceWrapper) GetOAuthClientMetadata(ctx echo.Context) error { +// HandleTokenRequest converts echo context to params. +func (w *ServerInterfaceWrapper) HandleTokenRequest(ctx echo.Context) error { var err error // ------------- Path parameter "id" ------------- var id string @@ -250,7 +250,7 @@ func (w *ServerInterfaceWrapper) GetOAuthClientMetadata(ctx echo.Context) error } // Invoke the callback with all the unmarshaled arguments - err = w.Handler.GetOAuthClientMetadata(ctx, id) + err = w.Handler.HandleTokenRequest(ctx, id) return err } @@ -298,17 +298,17 @@ func RegisterHandlersWithBaseURL(router EchoRouter, si ServerInterface, baseURL Handler: si, } - router.GET(baseURL+"/.well-known/oauth-authorization-server/iam/:did", wrapper.GetOAuthAuthorizationServerMetadata) - router.GET(baseURL+"/iam/:did/authorize", wrapper.HandleAuthorizeRequest) - router.GET(baseURL+"/iam/:did/did.json", wrapper.GetWebDID) - router.POST(baseURL+"/iam/:did/token", wrapper.HandleTokenRequest) + router.GET(baseURL+"/.well-known/oauth-authorization-server/iam/:id", wrapper.GetOAuthAuthorizationServerMetadata) + router.GET(baseURL+"/iam/:id/authorize", wrapper.HandleAuthorizeRequest) + router.GET(baseURL+"/iam/:id/did.json", wrapper.GetWebDID) router.GET(baseURL+"/iam/:id/oauth-client", wrapper.GetOAuthClientMetadata) + router.POST(baseURL+"/iam/:id/token", wrapper.HandleTokenRequest) router.POST(baseURL+"/internal/auth/v2/:did/request-access-token", wrapper.RequestAccessToken) } type GetOAuthAuthorizationServerMetadataRequestObject struct { - Did string `json:"did"` + Id string `json:"id"` } type GetOAuthAuthorizationServerMetadataResponseObject interface { @@ -346,7 +346,7 @@ func (response GetOAuthAuthorizationServerMetadatadefaultApplicationProblemPlusJ } type HandleAuthorizeRequestRequestObject struct { - Did string `json:"did"` + Id string `json:"id"` Params HandleAuthorizeRequestParams } @@ -388,7 +388,7 @@ func (response HandleAuthorizeRequest302Response) VisitHandleAuthorizeRequestRes } type GetWebDIDRequestObject struct { - Did string `json:"did"` + Id string `json:"id"` } type GetWebDIDResponseObject interface { @@ -412,42 +412,6 @@ func (response GetWebDID404Response) VisitGetWebDIDResponse(w http.ResponseWrite return nil } -type HandleTokenRequestRequestObject struct { - Did string `json:"did"` - Body *HandleTokenRequestFormdataRequestBody -} - -type HandleTokenRequestResponseObject interface { - VisitHandleTokenRequestResponse(w http.ResponseWriter) error -} - -type HandleTokenRequest200JSONResponse TokenResponse - -func (response HandleTokenRequest200JSONResponse) VisitHandleTokenRequestResponse(w http.ResponseWriter) error { - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(200) - - return json.NewEncoder(w).Encode(response) -} - -type HandleTokenRequest400JSONResponse ErrorResponse - -func (response HandleTokenRequest400JSONResponse) VisitHandleTokenRequestResponse(w http.ResponseWriter) error { - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(400) - - return json.NewEncoder(w).Encode(response) -} - -type HandleTokenRequest404JSONResponse ErrorResponse - -func (response HandleTokenRequest404JSONResponse) VisitHandleTokenRequestResponse(w http.ResponseWriter) error { - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(404) - - return json.NewEncoder(w).Encode(response) -} - type GetOAuthClientMetadataRequestObject struct { Id string `json:"id"` } @@ -486,6 +450,42 @@ func (response GetOAuthClientMetadatadefaultApplicationProblemPlusJSONResponse) return json.NewEncoder(w).Encode(response.Body) } +type HandleTokenRequestRequestObject struct { + Id string `json:"id"` + Body *HandleTokenRequestFormdataRequestBody +} + +type HandleTokenRequestResponseObject interface { + VisitHandleTokenRequestResponse(w http.ResponseWriter) error +} + +type HandleTokenRequest200JSONResponse TokenResponse + +func (response HandleTokenRequest200JSONResponse) VisitHandleTokenRequestResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(200) + + return json.NewEncoder(w).Encode(response) +} + +type HandleTokenRequest400JSONResponse ErrorResponse + +func (response HandleTokenRequest400JSONResponse) VisitHandleTokenRequestResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(400) + + return json.NewEncoder(w).Encode(response) +} + +type HandleTokenRequest404JSONResponse ErrorResponse + +func (response HandleTokenRequest404JSONResponse) VisitHandleTokenRequestResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(404) + + return json.NewEncoder(w).Encode(response) +} + type RequestAccessTokenRequestObject struct { Did string `json:"did"` Body *RequestAccessTokenJSONRequestBody @@ -528,20 +528,20 @@ func (response RequestAccessTokendefaultApplicationProblemPlusJSONResponse) Visi // StrictServerInterface represents all server handlers. type StrictServerInterface interface { // Get the OAuth2 Authorization Server metadata - // (GET /.well-known/oauth-authorization-server/iam/{did}) + // (GET /.well-known/oauth-authorization-server/iam/{id}) GetOAuthAuthorizationServerMetadata(ctx context.Context, request GetOAuthAuthorizationServerMetadataRequestObject) (GetOAuthAuthorizationServerMetadataResponseObject, error) // Used by resource owners to initiate the authorization code flow. - // (GET /iam/{did}/authorize) + // (GET /iam/{id}/authorize) HandleAuthorizeRequest(ctx context.Context, request HandleAuthorizeRequestRequestObject) (HandleAuthorizeRequestResponseObject, error) // Returns the did:web version of a Nuts DID document - // (GET /iam/{did}/did.json) + // (GET /iam/{id}/did.json) GetWebDID(ctx context.Context, request GetWebDIDRequestObject) (GetWebDIDResponseObject, error) - // Used by to request access- or refresh tokens. - // (POST /iam/{did}/token) - HandleTokenRequest(ctx context.Context, request HandleTokenRequestRequestObject) (HandleTokenRequestResponseObject, error) // Get the OAuth2 Client metadata // (GET /iam/{id}/oauth-client) GetOAuthClientMetadata(ctx context.Context, request GetOAuthClientMetadataRequestObject) (GetOAuthClientMetadataResponseObject, error) + // Used by to request access- or refresh tokens. + // (POST /iam/{id}/token) + HandleTokenRequest(ctx context.Context, request HandleTokenRequestRequestObject) (HandleTokenRequestResponseObject, error) // Requests an access token using the vp_token-bearer grant. // (POST /internal/auth/v2/{did}/request-access-token) RequestAccessToken(ctx context.Context, request RequestAccessTokenRequestObject) (RequestAccessTokenResponseObject, error) @@ -560,10 +560,10 @@ type strictHandler struct { } // GetOAuthAuthorizationServerMetadata operation middleware -func (sh *strictHandler) GetOAuthAuthorizationServerMetadata(ctx echo.Context, did string) error { +func (sh *strictHandler) GetOAuthAuthorizationServerMetadata(ctx echo.Context, id string) error { var request GetOAuthAuthorizationServerMetadataRequestObject - request.Did = did + request.Id = id handler := func(ctx echo.Context, request interface{}) (interface{}, error) { return sh.ssi.GetOAuthAuthorizationServerMetadata(ctx.Request().Context(), request.(GetOAuthAuthorizationServerMetadataRequestObject)) @@ -585,10 +585,10 @@ func (sh *strictHandler) GetOAuthAuthorizationServerMetadata(ctx echo.Context, d } // HandleAuthorizeRequest operation middleware -func (sh *strictHandler) HandleAuthorizeRequest(ctx echo.Context, did string, params HandleAuthorizeRequestParams) error { +func (sh *strictHandler) HandleAuthorizeRequest(ctx echo.Context, id string, params HandleAuthorizeRequestParams) error { var request HandleAuthorizeRequestRequestObject - request.Did = did + request.Id = id request.Params = params handler := func(ctx echo.Context, request interface{}) (interface{}, error) { @@ -611,10 +611,10 @@ func (sh *strictHandler) HandleAuthorizeRequest(ctx echo.Context, did string, pa } // GetWebDID operation middleware -func (sh *strictHandler) GetWebDID(ctx echo.Context, did string) error { +func (sh *strictHandler) GetWebDID(ctx echo.Context, id string) error { var request GetWebDIDRequestObject - request.Did = did + request.Id = id handler := func(ctx echo.Context, request interface{}) (interface{}, error) { return sh.ssi.GetWebDID(ctx.Request().Context(), request.(GetWebDIDRequestObject)) @@ -635,60 +635,60 @@ func (sh *strictHandler) GetWebDID(ctx echo.Context, did string) error { return nil } -// HandleTokenRequest operation middleware -func (sh *strictHandler) HandleTokenRequest(ctx echo.Context, did string) error { - var request HandleTokenRequestRequestObject - - request.Did = did +// GetOAuthClientMetadata operation middleware +func (sh *strictHandler) GetOAuthClientMetadata(ctx echo.Context, id string) error { + var request GetOAuthClientMetadataRequestObject - if form, err := ctx.FormParams(); err == nil { - var body HandleTokenRequestFormdataRequestBody - if err := runtime.BindForm(&body, form, nil, nil); err != nil { - return err - } - request.Body = &body - } else { - return err - } + request.Id = id handler := func(ctx echo.Context, request interface{}) (interface{}, error) { - return sh.ssi.HandleTokenRequest(ctx.Request().Context(), request.(HandleTokenRequestRequestObject)) + return sh.ssi.GetOAuthClientMetadata(ctx.Request().Context(), request.(GetOAuthClientMetadataRequestObject)) } for _, middleware := range sh.middlewares { - handler = middleware(handler, "HandleTokenRequest") + handler = middleware(handler, "GetOAuthClientMetadata") } response, err := handler(ctx, request) if err != nil { return err - } else if validResponse, ok := response.(HandleTokenRequestResponseObject); ok { - return validResponse.VisitHandleTokenRequestResponse(ctx.Response()) + } else if validResponse, ok := response.(GetOAuthClientMetadataResponseObject); ok { + return validResponse.VisitGetOAuthClientMetadataResponse(ctx.Response()) } else if response != nil { return fmt.Errorf("unexpected response type: %T", response) } return nil } -// GetOAuthClientMetadata operation middleware -func (sh *strictHandler) GetOAuthClientMetadata(ctx echo.Context, id string) error { - var request GetOAuthClientMetadataRequestObject +// HandleTokenRequest operation middleware +func (sh *strictHandler) HandleTokenRequest(ctx echo.Context, id string) error { + var request HandleTokenRequestRequestObject request.Id = id + if form, err := ctx.FormParams(); err == nil { + var body HandleTokenRequestFormdataRequestBody + if err := runtime.BindForm(&body, form, nil, nil); err != nil { + return err + } + request.Body = &body + } else { + return err + } + handler := func(ctx echo.Context, request interface{}) (interface{}, error) { - return sh.ssi.GetOAuthClientMetadata(ctx.Request().Context(), request.(GetOAuthClientMetadataRequestObject)) + return sh.ssi.HandleTokenRequest(ctx.Request().Context(), request.(HandleTokenRequestRequestObject)) } for _, middleware := range sh.middlewares { - handler = middleware(handler, "GetOAuthClientMetadata") + handler = middleware(handler, "HandleTokenRequest") } response, err := handler(ctx, request) if err != nil { return err - } else if validResponse, ok := response.(GetOAuthClientMetadataResponseObject); ok { - return validResponse.VisitGetOAuthClientMetadataResponse(ctx.Response()) + } else if validResponse, ok := response.(HandleTokenRequestResponseObject); ok { + return validResponse.VisitHandleTokenRequestResponse(ctx.Response()) } else if response != nil { return fmt.Errorf("unexpected response type: %T", response) } diff --git a/docs/_static/auth/iam.yaml b/docs/_static/auth/iam.yaml index 95383f3e6d..5425e38fc7 100644 --- a/docs/_static/auth/iam.yaml +++ b/docs/_static/auth/iam.yaml @@ -5,12 +5,11 @@ info: servers: - url: "http://localhost:1323" paths: - # TODO: Decide on /iam: https://github.com/nuts-foundation/nuts-node/issues/2439 - /iam/{did}/did.json: + /iam/{id}/did.json: parameters: - - name: did + - name: id in: path - description: ID of the corresponding Nuts DID document without the `did:nuts:` prefix. + description: ID of DID. required: true example: EwVMYK2ugaMvRHUbGFBhuyF423JuNQbtpes35eHhkQic schema: @@ -18,7 +17,7 @@ paths: get: summary: "Returns the did:web version of a Nuts DID document" description: | - Returns the did:web equivalent of a Nuts DID document, if it is owned by this node. + Returns the did:web DID document, if it is owned by this node. operationId: "getWebDID" tags: - DID @@ -31,8 +30,7 @@ paths: $ref: '#/components/schemas/DIDDocument' "404": description: DID does not exist. - # TODO: Decide on /iam: https://github.com/nuts-foundation/nuts-node/issues/2439 - "/iam/{did}/token": + "/iam/{id}/token": post: summary: Used by to request access- or refresh tokens. description: Specified by https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0.html#name-token-endpoint @@ -40,12 +38,13 @@ paths: tags: - oauth2 parameters: - - name: did + - name: id in: path required: true + description: the id part of the web DID schema: type: string - example: did:nuts:123 + example: EwVMYK2ugaMvRHUbGFBhuyF423JuNQbtpes35eHhkQic requestBody: content: application/x-www-form-urlencoded: @@ -84,8 +83,7 @@ paths: application/json: schema: "$ref": "#/components/schemas/ErrorResponse" - # TODO: Decide on /iam: https://github.com/nuts-foundation/nuts-node/issues/2439 - "/iam/{did}/authorize": + "/iam/{id}/authorize": get: summary: Used by resource owners to initiate the authorization code flow. description: Specified by https://datatracker.ietf.org/doc/html/rfc6749#section-3.1 @@ -93,12 +91,13 @@ paths: tags: - oauth2 parameters: - - name: did + - name: id in: path required: true + description: the id part of the web DID schema: type: string - example: did:nuts:123 + example: EwVMYK2ugaMvRHUbGFBhuyF423JuNQbtpes35eHhkQic # Way to specify dynamic query parameters # See https://stackoverflow.com/questions/49582559/how-to-document-dynamic-query-parameter-names-in-openapi-swagger - in: query @@ -131,7 +130,7 @@ paths: # $ref: '#/paths/~1.well-known~1oauth-authorization-server~1iam~1{did}' # /iam/{did}/.well-known/oauth-authorization-server: # $ref: '#/paths/~1.well-known~1oauth-authorization-server~1iam~1{did}' - /.well-known/oauth-authorization-server/iam/{did}: + /.well-known/oauth-authorization-server/iam/{id}: get: tags: - well-known @@ -146,13 +145,13 @@ paths: * 500 - internal server error operationId: getOAuthAuthorizationServerMetadata parameters: - - name: did + - name: id in: path required: true + description: the id part of the web DID schema: - description: must be did:nuts for now type: string - example: did:nuts:123 + example: EwVMYK2ugaMvRHUbGFBhuyF423JuNQbtpes35eHhkQic responses: "200": description: OK