From 38d018a0f3b19fbec4a1468881b67855e6beb7e6 Mon Sep 17 00:00:00 2001 From: Gerard Snaauw Date: Tue, 14 May 2024 13:47:12 +0200 Subject: [PATCH] pr feedback --- auth/api/iam/api.go | 45 +++++++-------- auth/api/iam/api_test.go | 38 ++++++------ auth/api/iam/generated.go | 92 +++++++++++++++--------------- auth/api/iam/jar.go | 4 +- auth/api/iam/jar_test.go | 10 ++-- auth/api/iam/metadata.go | 2 + auth/client/iam/client.go | 8 +-- auth/client/iam/client_test.go | 12 ++-- auth/client/iam/interface.go | 9 ++- auth/client/iam/mock.go | 27 +++++++-- auth/client/iam/openid4vp.go | 36 ++++++------ auth/client/iam/openid4vp_test.go | 41 +++++++++---- auth/oauth/error.go | 6 +- docs/_static/auth/iam.partial.yaml | 22 +++---- 14 files changed, 194 insertions(+), 158 deletions(-) diff --git a/auth/api/iam/api.go b/auth/api/iam/api.go index 7ab5726403..743ec48f8c 100644 --- a/auth/api/iam/api.go +++ b/auth/api/iam/api.go @@ -380,9 +380,9 @@ func (r Wrapper) handleAuthorizeRequest(ctx context.Context, ownDID did.DID, req } } -// GetRequestJWT returns the Request Object referenced as 'request_uri' in an authorization request. +// RequestJWTByGet returns the Request Object referenced as 'request_uri' in an authorization request. // RFC9101: The OAuth 2.0 Authorization Framework: JWT-Secured Authorization Request (JAR). -func (r Wrapper) GetRequestJWT(ctx context.Context, request GetRequestJWTRequestObject) (GetRequestJWTResponseObject, error) { +func (r Wrapper) RequestJWTByGet(ctx context.Context, request RequestJWTByGetRequestObject) (RequestJWTByGetResponseObject, error) { ro := new(jarRequest) err := r.authzRequestObjectStore().Get(request.Id, ro) if err != nil { @@ -394,12 +394,11 @@ func (r Wrapper) GetRequestJWT(ctx context.Context, request GetRequestJWTRequest // compare raw strings, don't waste a db call to see if we own the request.Did. if ro.Client.String() != request.Did { return nil, oauth.OAuth2Error{ - Code: oauth.InvalidRequest, - Description: "client_id does not match request", - InternalError: errors.New("DID does not match client_id for requestID"), + Code: oauth.InvalidRequest, + Description: "client_id does not match request", } } - if ro.RequestURIMethod != "get" { + if ro.RequestURIMethod != "get" { // case sensitive // TODO: wallet does not support `request_uri_method=post`. Signing the current jarRequest would leave it without 'aud'. // is this acceptable, should it fail, or does it default to using staticAuthorizationServerMetadata. return nil, oauth.OAuth2Error{ @@ -414,20 +413,20 @@ func (r Wrapper) GetRequestJWT(ctx context.Context, request GetRequestJWTRequest if err != nil { return nil, oauth.OAuth2Error{ Code: oauth.ServerError, - Description: "failed to sign authorization RequestObject", - InternalError: err, + Description: "unable to create RequestObjectByGet", + InternalError: fmt.Errorf("failed to sign authorization RequestObjectByGet :%w", err), } } - return GetRequestJWT200ApplicationoauthAuthzReqJwtResponse{ + return RequestJWTByGet200ApplicationoauthAuthzReqJwtResponse{ Body: bytes.NewReader([]byte(token)), ContentLength: int64(len(token)), }, nil } -// PostRequestJWT returns the Request Object referenced as 'request_uri' in an authorization request. +// RequestJWTByPost returns the Request Object referenced as 'request_uri' in an authorization request. // Extension of OpenID 4 Verifiable Presentations (OpenID4VP) on // RFC9101: The OAuth 2.0 Authorization Framework: JWT-Secured Authorization Request (JAR). -func (r Wrapper) PostRequestJWT(ctx context.Context, request PostRequestJWTRequestObject) (PostRequestJWTResponseObject, error) { +func (r Wrapper) RequestJWTByPost(ctx context.Context, request RequestJWTByPostRequestObject) (RequestJWTByPostResponseObject, error) { ro := new(jarRequest) err := r.authzRequestObjectStore().Get(request.Id, ro) if err != nil { @@ -439,12 +438,11 @@ func (r Wrapper) PostRequestJWT(ctx context.Context, request PostRequestJWTReque // compare raw strings, don't waste a db call to see if we own the request.Did. if ro.Client.String() != request.Did { return nil, oauth.OAuth2Error{ - Code: oauth.InvalidRequest, - Description: "client_id does not match request", - InternalError: errors.New("DID does not match client_id for requestID"), + Code: oauth.InvalidRequest, + Description: "client_id does not match request", } } - if ro.RequestURIMethod != "post" { + if ro.RequestURIMethod != "post" { // case sensitive return nil, oauth.OAuth2Error{ Code: oauth.InvalidRequest, Description: "used request_uri_method 'post' on a 'get' request_uri", @@ -467,11 +465,11 @@ func (r Wrapper) PostRequestJWT(ctx context.Context, request PostRequestJWTReque if err != nil { return nil, oauth.OAuth2Error{ Code: oauth.ServerError, - Description: "failed to sign authorization RequestObject", - InternalError: err, + Description: "unable to create RequestObjectByGet", + InternalError: fmt.Errorf("failed to sign authorization RequestObjectByGet :%w", err), } } - return PostRequestJWT200ApplicationoauthAuthzReqJwtResponse{ + return RequestJWTByPost200ApplicationoauthAuthzReqJwtResponse{ Body: bytes.NewReader([]byte(token)), ContentLength: int64(len(token)), }, nil @@ -880,8 +878,6 @@ func (r Wrapper) openidIssuerEndpoints(ctx context.Context, issuerDid did.DID) ( // - nonce // any of these params can be overridden by the requestObjectModifier. func (r Wrapper) CreateAuthorizationRequest(ctx context.Context, client did.DID, server *did.DID, modifier requestObjectModifier) (*url.URL, error) { - // if the server is unknown/nil we are talking to a wallet. - // by default requireSignedRequestObject=true to make sure the produced Authorization Request URL does not exceed request URL limit on mobile devices metadata := new(oauth.AuthorizationServerMetadata) if server != nil { // we want to make a call according to ยง4.1.1 of RFC6749, https://www.rfc-editor.org/rfc/rfc6749.html#section-4.1.1 @@ -891,16 +887,17 @@ func (r Wrapper) CreateAuthorizationRequest(ctx context.Context, client did.DID, if err != nil { return nil, fmt.Errorf("failed to retrieve remote OAuth Authorization Server metadata: %w", err) } + if len(metadata.AuthorizationEndpoint) == 0 { + return nil, fmt.Errorf("no authorization endpoint found in metadata for %s", *server) + } } else { - // use static configuration until while we try to determine the wallet that will answer the authorization request. (user wallet / QR code flow) + // if the server is unknown/nil we are talking to a wallet. + // use static configuration while we try to determine the wallet that will answer the authorization request. (user wallet / QR code flow) *metadata = staticAuthorizationServerMetadata() // TODO: metadata.RequireSignedRequestObject == false. // This means we send both a request_uri and add all params to the authorization request as query params. // The resulting url is too long and will be rejected by mobile devices. } - if len(metadata.AuthorizationEndpoint) == 0 { - return nil, fmt.Errorf("no authorization endpoint found in metadata for %s", *server) - } endpoint, err := url.Parse(metadata.AuthorizationEndpoint) if err != nil { return nil, fmt.Errorf("failed to parse authorization endpoint URL: %w", err) diff --git a/auth/api/iam/api_test.go b/auth/api/iam/api_test.go index 2b4c1d7717..c382610eef 100644 --- a/auth/api/iam/api_test.go +++ b/auth/api/iam/api_test.go @@ -951,10 +951,10 @@ func TestWrapper_GetRequestJWT(t *testing.T) { require.NoError(t, ctx.client.authzRequestObjectStore().Put(requestID, ro)) ctx.jar.EXPECT().Sign(cont, ro.Claims).Return(expectedToken, nil) - response, err := ctx.client.GetRequestJWT(cont, GetRequestJWTRequestObject{Did: webDID.String(), Id: requestID}) + response, err := ctx.client.RequestJWTByGet(cont, RequestJWTByGetRequestObject{Did: webDID.String(), Id: requestID}) assert.NoError(t, err) - assert.Equal(t, GetRequestJWT200ApplicationoauthAuthzReqJwtResponse{ + assert.Equal(t, RequestJWTByGet200ApplicationoauthAuthzReqJwtResponse{ Body: bytes.NewReader([]byte(expectedToken)), ContentLength: 10, }, response) @@ -962,7 +962,7 @@ func TestWrapper_GetRequestJWT(t *testing.T) { t.Run("error - not found", func(t *testing.T) { ctx := newTestClient(t) - response, err := ctx.client.GetRequestJWT(nil, GetRequestJWTRequestObject{Id: "unknownID"}) + response, err := ctx.client.RequestJWTByGet(nil, RequestJWTByGetRequestObject{Id: "unknownID"}) assert.Nil(t, response) assert.EqualError(t, err, "invalid_request - request object not found") @@ -972,10 +972,10 @@ func TestWrapper_GetRequestJWT(t *testing.T) { ro := jar{}.Create(webDID, &holderDID, func(claims map[string]string) {}) require.NoError(t, ctx.client.authzRequestObjectStore().Put(requestID, ro)) - response, err := ctx.client.GetRequestJWT(cont, GetRequestJWTRequestObject{Did: holderDID.String(), Id: requestID}) + response, err := ctx.client.RequestJWTByGet(cont, RequestJWTByGetRequestObject{Did: holderDID.String(), Id: requestID}) assert.Nil(t, response) - assert.EqualError(t, err, "invalid_request - DID does not match client_id for requestID - client_id does not match request") + assert.EqualError(t, err, "invalid_request - client_id does not match request") }) t.Run("error - wrong request_uri_method used", func(t *testing.T) { ctx := newTestClient(t) @@ -983,7 +983,7 @@ func TestWrapper_GetRequestJWT(t *testing.T) { ro.RequestURIMethod = "post" require.NoError(t, ctx.client.authzRequestObjectStore().Put(requestID, ro)) - response, err := ctx.client.GetRequestJWT(cont, GetRequestJWTRequestObject{Did: webDID.String(), Id: requestID}) + response, err := ctx.client.RequestJWTByGet(cont, RequestJWTByGetRequestObject{Did: webDID.String(), Id: requestID}) assert.Nil(t, response) assert.EqualError(t, err, "invalid_request - wrong 'request_uri_method' authorization server or wallet probably does not support 'request_uri_method' - used request_uri_method 'get' on a 'post' request_uri") @@ -994,10 +994,10 @@ func TestWrapper_GetRequestJWT(t *testing.T) { require.NoError(t, ctx.client.authzRequestObjectStore().Put(requestID, ro)) ctx.jar.EXPECT().Sign(cont, ro.Claims).Return("", errors.New("fail")) - response, err := ctx.client.GetRequestJWT(cont, GetRequestJWTRequestObject{Did: webDID.String(), Id: requestID}) + response, err := ctx.client.RequestJWTByGet(cont, RequestJWTByGetRequestObject{Did: webDID.String(), Id: requestID}) assert.Nil(t, response) - assert.EqualError(t, err, "server_error - fail - failed to sign authorization RequestObject") + assert.EqualError(t, err, "server_error - failed to sign authorization RequestObjectByGet :fail - unable to create RequestObjectByGet") }) } @@ -1021,10 +1021,10 @@ func TestWrapper_PostRequestJWT(t *testing.T) { require.NoError(t, ctx.client.authzRequestObjectStore().Put(requestID, ro)) ctx.jar.EXPECT().Sign(cont, ro.Claims).Return(expectedToken, nil) - response, err := ctx.client.PostRequestJWT(cont, PostRequestJWTRequestObject{Did: webDID.String(), Id: requestID}) + response, err := ctx.client.RequestJWTByPost(cont, RequestJWTByPostRequestObject{Did: webDID.String(), Id: requestID}) assert.NoError(t, err) - assert.Equal(t, PostRequestJWT200ApplicationoauthAuthzReqJwtResponse{ + assert.Equal(t, RequestJWTByPost200ApplicationoauthAuthzReqJwtResponse{ Body: bytes.NewReader([]byte(expectedToken)), ContentLength: 10, }, response) @@ -1035,15 +1035,15 @@ func TestWrapper_PostRequestJWT(t *testing.T) { ro := newReqObj("mario", wallet_nonce) require.NoError(t, ctx.client.authzRequestObjectStore().Put(requestID, ro)) ctx.jar.EXPECT().Sign(cont, ro.Claims).Return(expectedToken, nil) - body := PostRequestJWTFormdataRequestBody(PostRequestJWTFormdataBody{ + body := RequestJWTByPostFormdataRequestBody(RequestJWTByPostFormdataBody{ WalletMetadata: &oauth.AuthorizationServerMetadata{Issuer: "mario"}, WalletNonce: &wallet_nonce, }) - response, err := ctx.client.PostRequestJWT(cont, PostRequestJWTRequestObject{Did: webDID.String(), Id: requestID, Body: &body}) + response, err := ctx.client.RequestJWTByPost(cont, RequestJWTByPostRequestObject{Did: webDID.String(), Id: requestID, Body: &body}) assert.NoError(t, err) - assert.Equal(t, PostRequestJWT200ApplicationoauthAuthzReqJwtResponse{ + assert.Equal(t, RequestJWTByPost200ApplicationoauthAuthzReqJwtResponse{ Body: bytes.NewReader([]byte(expectedToken)), ContentLength: 10, }, response) @@ -1051,7 +1051,7 @@ func TestWrapper_PostRequestJWT(t *testing.T) { t.Run("error - not found", func(t *testing.T) { ctx := newTestClient(t) - response, err := ctx.client.PostRequestJWT(nil, PostRequestJWTRequestObject{Id: "unknownID"}) + response, err := ctx.client.RequestJWTByPost(nil, RequestJWTByPostRequestObject{Id: "unknownID"}) assert.Nil(t, response) assert.EqualError(t, err, "invalid_request - request object not found") @@ -1060,10 +1060,10 @@ func TestWrapper_PostRequestJWT(t *testing.T) { ctx := newTestClient(t) require.NoError(t, ctx.client.authzRequestObjectStore().Put(requestID, newReqObj("", ""))) - response, err := ctx.client.PostRequestJWT(cont, PostRequestJWTRequestObject{Did: holderDID.String(), Id: requestID}) + response, err := ctx.client.RequestJWTByPost(cont, RequestJWTByPostRequestObject{Did: holderDID.String(), Id: requestID}) assert.Nil(t, response) - assert.EqualError(t, err, "invalid_request - DID does not match client_id for requestID - client_id does not match request") + assert.EqualError(t, err, "invalid_request - client_id does not match request") }) t.Run("error - wrong request_uri_method used", func(t *testing.T) { ctx := newTestClient(t) @@ -1071,7 +1071,7 @@ func TestWrapper_PostRequestJWT(t *testing.T) { ro.RequestURIMethod = "get" require.NoError(t, ctx.client.authzRequestObjectStore().Put(requestID, ro)) - response, err := ctx.client.PostRequestJWT(cont, PostRequestJWTRequestObject{Did: webDID.String(), Id: requestID}) + response, err := ctx.client.RequestJWTByPost(cont, RequestJWTByPostRequestObject{Did: webDID.String(), Id: requestID}) assert.Nil(t, response) assert.EqualError(t, err, "invalid_request - used request_uri_method 'post' on a 'get' request_uri") @@ -1082,10 +1082,10 @@ func TestWrapper_PostRequestJWT(t *testing.T) { require.NoError(t, ctx.client.authzRequestObjectStore().Put(requestID, ro)) ctx.jar.EXPECT().Sign(cont, ro.Claims).Return("", errors.New("fail")) - response, err := ctx.client.PostRequestJWT(cont, PostRequestJWTRequestObject{Did: webDID.String(), Id: requestID}) + response, err := ctx.client.RequestJWTByPost(cont, RequestJWTByPostRequestObject{Did: webDID.String(), Id: requestID}) assert.Nil(t, response) - assert.EqualError(t, err, "server_error - fail - failed to sign authorization RequestObject") + assert.EqualError(t, err, "server_error - failed to sign authorization RequestObjectByGet :fail - unable to create RequestObjectByGet") }) } diff --git a/auth/api/iam/generated.go b/auth/api/iam/generated.go index a91d48978e..5ec71b0ebd 100644 --- a/auth/api/iam/generated.go +++ b/auth/api/iam/generated.go @@ -243,8 +243,8 @@ type PresentationDefinitionParams struct { WalletOwnerType *WalletOwnerType `form:"wallet_owner_type,omitempty" json:"wallet_owner_type,omitempty"` } -// PostRequestJWTFormdataBody defines parameters for PostRequestJWT. -type PostRequestJWTFormdataBody struct { +// RequestJWTByPostFormdataBody defines parameters for RequestJWTByPost. +type RequestJWTByPostFormdataBody struct { // WalletMetadata OAuth2 Authorization Server Metadata // Contain properties from several specifications and may grow over time WalletMetadata *OAuthAuthorizationServerMetadata `form:"wallet_metadata,omitempty" json:"wallet_metadata,omitempty"` @@ -299,8 +299,8 @@ type RequestServiceAccessTokenJSONRequestBody = ServiceAccessTokenRequest // RequestUserAccessTokenJSONRequestBody defines body for RequestUserAccessToken for application/json ContentType. type RequestUserAccessTokenJSONRequestBody = UserAccessTokenRequest -// PostRequestJWTFormdataRequestBody defines body for PostRequestJWT for application/x-www-form-urlencoded ContentType. -type PostRequestJWTFormdataRequestBody PostRequestJWTFormdataBody +// RequestJWTByPostFormdataRequestBody defines body for RequestJWTByPost for application/x-www-form-urlencoded ContentType. +type RequestJWTByPostFormdataRequestBody RequestJWTByPostFormdataBody // HandleAuthorizeResponseFormdataRequestBody defines body for HandleAuthorizeResponse for application/x-www-form-urlencoded ContentType. type HandleAuthorizeResponseFormdataRequestBody HandleAuthorizeResponseFormdataBody @@ -591,10 +591,10 @@ type ServerInterface interface { PresentationDefinition(ctx echo.Context, did string, params PresentationDefinitionParams) error // Get Request Object referenced in an authorization request to the Authorization Server. // (GET /oauth2/{did}/request.jwt/{id}) - GetRequestJWT(ctx echo.Context, did string, id string) error + RequestJWTByGet(ctx echo.Context, did string, id string) error // Provide missing information to Client to finish Authorization request's Request Object, which is then returned. // (POST /oauth2/{did}/request.jwt/{id}) - PostRequestJWT(ctx echo.Context, did string, id string) error + RequestJWTByPost(ctx echo.Context, did string, id string) error // Used by wallets to post the authorization response or error to. // (POST /oauth2/{did}/response) HandleAuthorizeResponse(ctx echo.Context, did string) error @@ -925,8 +925,8 @@ func (w *ServerInterfaceWrapper) PresentationDefinition(ctx echo.Context) error return err } -// GetRequestJWT converts echo context to params. -func (w *ServerInterfaceWrapper) GetRequestJWT(ctx echo.Context) error { +// RequestJWTByGet converts echo context to params. +func (w *ServerInterfaceWrapper) RequestJWTByGet(ctx echo.Context) error { var err error // ------------- Path parameter "did" ------------- var did string @@ -944,12 +944,12 @@ func (w *ServerInterfaceWrapper) GetRequestJWT(ctx echo.Context) error { ctx.Set(JwtBearerAuthScopes, []string{}) // Invoke the callback with all the unmarshaled arguments - err = w.Handler.GetRequestJWT(ctx, did, id) + err = w.Handler.RequestJWTByGet(ctx, did, id) return err } -// PostRequestJWT converts echo context to params. -func (w *ServerInterfaceWrapper) PostRequestJWT(ctx echo.Context) error { +// RequestJWTByPost converts echo context to params. +func (w *ServerInterfaceWrapper) RequestJWTByPost(ctx echo.Context) error { var err error // ------------- Path parameter "did" ------------- var did string @@ -967,7 +967,7 @@ func (w *ServerInterfaceWrapper) PostRequestJWT(ctx echo.Context) error { ctx.Set(JwtBearerAuthScopes, []string{}) // Invoke the callback with all the unmarshaled arguments - err = w.Handler.PostRequestJWT(ctx, did, id) + err = w.Handler.RequestJWTByPost(ctx, did, id) return err } @@ -1068,8 +1068,8 @@ func RegisterHandlersWithBaseURL(router EchoRouter, si ServerInterface, baseURL router.GET(baseURL+"/oauth2/:did/callback", wrapper.Callback) router.GET(baseURL+"/oauth2/:did/oauth-client", wrapper.OAuthClientMetadata) router.GET(baseURL+"/oauth2/:did/presentation_definition", wrapper.PresentationDefinition) - router.GET(baseURL+"/oauth2/:did/request.jwt/:id", wrapper.GetRequestJWT) - router.POST(baseURL+"/oauth2/:did/request.jwt/:id", wrapper.PostRequestJWT) + router.GET(baseURL+"/oauth2/:did/request.jwt/:id", wrapper.RequestJWTByGet) + router.POST(baseURL+"/oauth2/:did/request.jwt/:id", wrapper.RequestJWTByPost) router.POST(baseURL+"/oauth2/:did/response", wrapper.HandleAuthorizeResponse) router.POST(baseURL+"/oauth2/:did/token", wrapper.HandleTokenRequest) router.GET(baseURL+"/statuslist/:did/:page", wrapper.StatusList) @@ -1650,21 +1650,21 @@ func (response PresentationDefinitiondefaultApplicationProblemPlusJSONResponse) return json.NewEncoder(w).Encode(response.Body) } -type GetRequestJWTRequestObject struct { +type RequestJWTByGetRequestObject struct { Did string `json:"did"` Id string `json:"id"` } -type GetRequestJWTResponseObject interface { - VisitGetRequestJWTResponse(w http.ResponseWriter) error +type RequestJWTByGetResponseObject interface { + VisitRequestJWTByGetResponse(w http.ResponseWriter) error } -type GetRequestJWT200ApplicationoauthAuthzReqJwtResponse struct { +type RequestJWTByGet200ApplicationoauthAuthzReqJwtResponse struct { Body io.Reader ContentLength int64 } -func (response GetRequestJWT200ApplicationoauthAuthzReqJwtResponse) VisitGetRequestJWTResponse(w http.ResponseWriter) error { +func (response RequestJWTByGet200ApplicationoauthAuthzReqJwtResponse) VisitRequestJWTByGetResponse(w http.ResponseWriter) error { w.Header().Set("Content-Type", "application/oauth-authz-req+jwt") if response.ContentLength != 0 { w.Header().Set("Content-Length", fmt.Sprint(response.ContentLength)) @@ -1678,7 +1678,7 @@ func (response GetRequestJWT200ApplicationoauthAuthzReqJwtResponse) VisitGetRequ return err } -type GetRequestJWTdefaultApplicationProblemPlusJSONResponse struct { +type RequestJWTByGetdefaultApplicationProblemPlusJSONResponse struct { Body struct { // Detail A human-readable explanation specific to this occurrence of the problem. Detail string `json:"detail"` @@ -1692,29 +1692,29 @@ type GetRequestJWTdefaultApplicationProblemPlusJSONResponse struct { StatusCode int } -func (response GetRequestJWTdefaultApplicationProblemPlusJSONResponse) VisitGetRequestJWTResponse(w http.ResponseWriter) error { +func (response RequestJWTByGetdefaultApplicationProblemPlusJSONResponse) VisitRequestJWTByGetResponse(w http.ResponseWriter) error { w.Header().Set("Content-Type", "application/problem+json") w.WriteHeader(response.StatusCode) return json.NewEncoder(w).Encode(response.Body) } -type PostRequestJWTRequestObject struct { +type RequestJWTByPostRequestObject struct { Did string `json:"did"` Id string `json:"id"` - Body *PostRequestJWTFormdataRequestBody + Body *RequestJWTByPostFormdataRequestBody } -type PostRequestJWTResponseObject interface { - VisitPostRequestJWTResponse(w http.ResponseWriter) error +type RequestJWTByPostResponseObject interface { + VisitRequestJWTByPostResponse(w http.ResponseWriter) error } -type PostRequestJWT200ApplicationoauthAuthzReqJwtResponse struct { +type RequestJWTByPost200ApplicationoauthAuthzReqJwtResponse struct { Body io.Reader ContentLength int64 } -func (response PostRequestJWT200ApplicationoauthAuthzReqJwtResponse) VisitPostRequestJWTResponse(w http.ResponseWriter) error { +func (response RequestJWTByPost200ApplicationoauthAuthzReqJwtResponse) VisitRequestJWTByPostResponse(w http.ResponseWriter) error { w.Header().Set("Content-Type", "application/oauth-authz-req+jwt") if response.ContentLength != 0 { w.Header().Set("Content-Length", fmt.Sprint(response.ContentLength)) @@ -1728,7 +1728,7 @@ func (response PostRequestJWT200ApplicationoauthAuthzReqJwtResponse) VisitPostRe return err } -type PostRequestJWTdefaultApplicationProblemPlusJSONResponse struct { +type RequestJWTByPostdefaultApplicationProblemPlusJSONResponse struct { Body struct { // Detail A human-readable explanation specific to this occurrence of the problem. Detail string `json:"detail"` @@ -1742,7 +1742,7 @@ type PostRequestJWTdefaultApplicationProblemPlusJSONResponse struct { StatusCode int } -func (response PostRequestJWTdefaultApplicationProblemPlusJSONResponse) VisitPostRequestJWTResponse(w http.ResponseWriter) error { +func (response RequestJWTByPostdefaultApplicationProblemPlusJSONResponse) VisitRequestJWTByPostResponse(w http.ResponseWriter) error { w.Header().Set("Content-Type", "application/problem+json") w.WriteHeader(response.StatusCode) @@ -1888,10 +1888,10 @@ type StrictServerInterface interface { PresentationDefinition(ctx context.Context, request PresentationDefinitionRequestObject) (PresentationDefinitionResponseObject, error) // Get Request Object referenced in an authorization request to the Authorization Server. // (GET /oauth2/{did}/request.jwt/{id}) - GetRequestJWT(ctx context.Context, request GetRequestJWTRequestObject) (GetRequestJWTResponseObject, error) + RequestJWTByGet(ctx context.Context, request RequestJWTByGetRequestObject) (RequestJWTByGetResponseObject, error) // Provide missing information to Client to finish Authorization request's Request Object, which is then returned. // (POST /oauth2/{did}/request.jwt/{id}) - PostRequestJWT(ctx context.Context, request PostRequestJWTRequestObject) (PostRequestJWTResponseObject, error) + RequestJWTByPost(ctx context.Context, request RequestJWTByPostRequestObject) (RequestJWTByPostResponseObject, error) // Used by wallets to post the authorization response or error to. // (POST /oauth2/{did}/response) HandleAuthorizeResponse(ctx context.Context, request HandleAuthorizeResponseRequestObject) (HandleAuthorizeResponseResponseObject, error) @@ -2350,41 +2350,41 @@ func (sh *strictHandler) PresentationDefinition(ctx echo.Context, did string, pa return nil } -// GetRequestJWT operation middleware -func (sh *strictHandler) GetRequestJWT(ctx echo.Context, did string, id string) error { - var request GetRequestJWTRequestObject +// RequestJWTByGet operation middleware +func (sh *strictHandler) RequestJWTByGet(ctx echo.Context, did string, id string) error { + var request RequestJWTByGetRequestObject request.Did = did request.Id = id handler := func(ctx echo.Context, request interface{}) (interface{}, error) { - return sh.ssi.GetRequestJWT(ctx.Request().Context(), request.(GetRequestJWTRequestObject)) + return sh.ssi.RequestJWTByGet(ctx.Request().Context(), request.(RequestJWTByGetRequestObject)) } for _, middleware := range sh.middlewares { - handler = middleware(handler, "GetRequestJWT") + handler = middleware(handler, "RequestJWTByGet") } response, err := handler(ctx, request) if err != nil { return err - } else if validResponse, ok := response.(GetRequestJWTResponseObject); ok { - return validResponse.VisitGetRequestJWTResponse(ctx.Response()) + } else if validResponse, ok := response.(RequestJWTByGetResponseObject); ok { + return validResponse.VisitRequestJWTByGetResponse(ctx.Response()) } else if response != nil { return fmt.Errorf("unexpected response type: %T", response) } return nil } -// PostRequestJWT operation middleware -func (sh *strictHandler) PostRequestJWT(ctx echo.Context, did string, id string) error { - var request PostRequestJWTRequestObject +// RequestJWTByPost operation middleware +func (sh *strictHandler) RequestJWTByPost(ctx echo.Context, did string, id string) error { + var request RequestJWTByPostRequestObject request.Did = did request.Id = id if form, err := ctx.FormParams(); err == nil { - var body PostRequestJWTFormdataRequestBody + var body RequestJWTByPostFormdataRequestBody if err := runtime.BindForm(&body, form, nil, nil); err != nil { return err } @@ -2394,18 +2394,18 @@ func (sh *strictHandler) PostRequestJWT(ctx echo.Context, did string, id string) } handler := func(ctx echo.Context, request interface{}) (interface{}, error) { - return sh.ssi.PostRequestJWT(ctx.Request().Context(), request.(PostRequestJWTRequestObject)) + return sh.ssi.RequestJWTByPost(ctx.Request().Context(), request.(RequestJWTByPostRequestObject)) } for _, middleware := range sh.middlewares { - handler = middleware(handler, "PostRequestJWT") + handler = middleware(handler, "RequestJWTByPost") } response, err := handler(ctx, request) if err != nil { return err - } else if validResponse, ok := response.(PostRequestJWTResponseObject); ok { - return validResponse.VisitPostRequestJWTResponse(ctx.Response()) + } else if validResponse, ok := response.(RequestJWTByPostResponseObject); ok { + return validResponse.VisitRequestJWTByPostResponse(ctx.Response()) } else if response != nil { return fmt.Errorf("unexpected response type: %T", response) } diff --git a/auth/api/iam/jar.go b/auth/api/iam/jar.go index 0a42032364..16cbd87e22 100644 --- a/auth/api/iam/jar.go +++ b/auth/api/iam/jar.go @@ -120,7 +120,7 @@ func (j jar) Parse(ctx context.Context, ownDID did.DID, q url.Values) (oauthPara } else if requestURI := q.Get(oauth.RequestURIParam); requestURI != "" { switch q.Get(oauth.RequestURIMethodParam) { case "", "get": // empty string means client does not support request_uri_method, use 'get' - rawRequestObject, err = j.auth.IAMClient().RequestObject(ctx, requestURI, "get", nil) + rawRequestObject, err = j.auth.IAMClient().RequestObjectByGet(ctx, requestURI) if err != nil { return nil, oauth.OAuth2Error{Code: oauth.InvalidRequestURI, Description: "failed to get Request Object", InternalError: err} } @@ -130,7 +130,7 @@ func (j jar) Parse(ctx context.Context, ownDID did.DID, q url.Values) (oauthPara // DB error return nil, err } - rawRequestObject, err = j.auth.IAMClient().RequestObject(ctx, requestURI, "post", md) + rawRequestObject, err = j.auth.IAMClient().RequestObjectByPost(ctx, requestURI, *md) if err != nil { return nil, oauth.OAuth2Error{Code: oauth.InvalidRequestURI, Description: "failed to get Request Object", InternalError: err} } diff --git a/auth/api/iam/jar_test.go b/auth/api/iam/jar_test.go index 06b8c0f602..1a13c22075 100644 --- a/auth/api/iam/jar_test.go +++ b/auth/api/iam/jar_test.go @@ -115,7 +115,7 @@ func TestJar_Parse(t *testing.T) { ctx := newJarTestCtx(t) t.Run("request_uri_method", func(t *testing.T) { t.Run("ok - get", func(t *testing.T) { - ctx.iamClient.EXPECT().RequestObject(context.Background(), "request_uri", "get", nil).Return(token, nil) + ctx.iamClient.EXPECT().RequestObjectByGet(context.Background(), "request_uri").Return(token, nil) ctx.keyResolver.EXPECT().ResolveKeyByID(key.KID(), nil, resolver.AssertionMethod).Return(key.Public(), nil) res, err := ctx.jar.Parse(context.Background(), verifierDID, @@ -129,7 +129,7 @@ func TestJar_Parse(t *testing.T) { require.NotNil(t, res) }) t.Run("ok - param not supported", func(t *testing.T) { - ctx.iamClient.EXPECT().RequestObject(context.Background(), "request_uri", "get", nil).Return(token, nil) + ctx.iamClient.EXPECT().RequestObjectByGet(context.Background(), "request_uri").Return(token, nil) ctx.keyResolver.EXPECT().ResolveKeyByID(key.KID(), nil, resolver.AssertionMethod).Return(key.Public(), nil) res, err := ctx.jar.Parse(context.Background(), verifierDID, @@ -144,7 +144,7 @@ func TestJar_Parse(t *testing.T) { }) t.Run("ok - post", func(t *testing.T) { md, _ := authorizationServerMetadata(verifierDID) - ctx.iamClient.EXPECT().RequestObject(context.Background(), "request_uri", "post", md).Return(token, nil) + ctx.iamClient.EXPECT().RequestObjectByPost(context.Background(), "request_uri", *md).Return(token, nil) ctx.keyResolver.EXPECT().ResolveKeyByID(key.KID(), nil, resolver.AssertionMethod).Return(key.Public(), nil) res, err := ctx.jar.Parse(context.Background(), verifierDID, @@ -183,7 +183,7 @@ func TestJar_Parse(t *testing.T) { }) t.Run("server error", func(t *testing.T) { t.Run("get", func(t *testing.T) { - ctx.iamClient.EXPECT().RequestObject(context.Background(), "request_uri", "get", nil).Return("", errors.New("server error")) + ctx.iamClient.EXPECT().RequestObjectByGet(context.Background(), "request_uri").Return("", errors.New("server error")) res, err := ctx.jar.Parse(context.Background(), verifierDID, map[string][]string{ oauth.RequestURIParam: {"request_uri"}, @@ -194,7 +194,7 @@ func TestJar_Parse(t *testing.T) { }) t.Run("post", func(t *testing.T) { md, _ := authorizationServerMetadata(verifierDID) - ctx.iamClient.EXPECT().RequestObject(context.Background(), "request_uri", "post", md).Return("", errors.New("server error")) + ctx.iamClient.EXPECT().RequestObjectByPost(context.Background(), "request_uri", *md).Return("", errors.New("server error")) res, err := ctx.jar.Parse(context.Background(), verifierDID, map[string][]string{ oauth.RequestURIParam: {"request_uri"}, diff --git a/auth/api/iam/metadata.go b/auth/api/iam/metadata.go index ed7d5e2ad7..aec2a6ab28 100644 --- a/auth/api/iam/metadata.go +++ b/auth/api/iam/metadata.go @@ -37,6 +37,7 @@ func authorizationServerMetadata(ownedDID did.DID) (*oauth.AuthorizationServerMe return _authzMetadataBase(ownedDID), nil } +// _authzMetadataDidWeb should not be used directly, use authorizationServerMetadata instead. func _authzMetadataDidWeb(ownedDID did.DID) (*oauth.AuthorizationServerMetadata, error) { identity, err := didweb.DIDToURL(ownedDID) if err != nil { @@ -55,6 +56,7 @@ func _authzMetadataDidWeb(ownedDID did.DID) (*oauth.AuthorizationServerMetadata, return metadata, nil } +// _authzMetadataBase should not be used directly, use authorizationServerMetadata instead. func _authzMetadataBase(ownedDID did.DID) *oauth.AuthorizationServerMetadata { presentationDefinitionURISupported := true return &oauth.AuthorizationServerMetadata{ diff --git a/auth/client/iam/client.go b/auth/client/iam/client.go index 7ff81fa423..6daed69778 100644 --- a/auth/client/iam/client.go +++ b/auth/client/iam/client.go @@ -89,8 +89,8 @@ func (hb HTTPClient) PresentationDefinition(ctx context.Context, presentationDef return &presentationDefinition, hb.doRequest(ctx, request, &presentationDefinition) } -// RequestObject retrieves the Authorization Request Object from the requestURI using the GET method -func (hb HTTPClient) RequestObject(ctx context.Context, requestURI string) (string, error) { +// RequestObjectByGet retrieves the Authorization Request Object from the requestURI using the GET method +func (hb HTTPClient) RequestObjectByGet(ctx context.Context, requestURI string) (string, error) { request, err := http.NewRequestWithContext(ctx, http.MethodGet, requestURI, nil) if err != nil { return "", err @@ -112,9 +112,9 @@ func (hb HTTPClient) RequestObject(ctx context.Context, requestURI string) (stri return string(data), err } -// RequestObjectPost retrieves the Authorization Request Object from the requestURI using the POST method. +// RequestObjectByPost retrieves the Authorization Request Object from the requestURI using the POST method. // additional request parameters (wallet_metadata and wallet_nonce) are provided as url.Values. -func (hb HTTPClient) RequestObjectPost(ctx context.Context, requestURI string, form url.Values) (string, error) { +func (hb HTTPClient) RequestObjectByPost(ctx context.Context, requestURI string, form url.Values) (string, error) { request, err := http.NewRequestWithContext(ctx, http.MethodPost, requestURI, strings.NewReader(form.Encode())) if err != nil { return "", err diff --git a/auth/client/iam/client_test.go b/auth/client/iam/client_test.go index b1e0f94cfd..980cb98836 100644 --- a/auth/client/iam/client_test.go +++ b/auth/client/iam/client_test.go @@ -242,7 +242,7 @@ func TestHTTPClient_RequestObject(t *testing.T) { handler := http2.Handler{StatusCode: http.StatusOK, ResponseData: responseData} tlsServer, client := testServerAndClient(t, &handler) - response, err := client.RequestObject(ctx, tlsServer.URL) + response, err := client.RequestObjectByGet(ctx, tlsServer.URL) require.NoError(t, err) assert.Equal(t, responseData, response) @@ -250,7 +250,7 @@ func TestHTTPClient_RequestObject(t *testing.T) { t.Run("error - invalid request_uri", func(t *testing.T) { _, client := testServerAndClient(t, &http2.Handler{}) - response, err := client.RequestObject(ctx, ":") + response, err := client.RequestObjectByGet(ctx, ":") assert.EqualError(t, err, "parse \":\": missing protocol scheme") assert.Empty(t, response) @@ -259,7 +259,7 @@ func TestHTTPClient_RequestObject(t *testing.T) { handler := http2.Handler{StatusCode: http.StatusNotFound, ResponseData: "throw this away"} tlsServer, client := testServerAndClient(t, &handler) - response, err := client.RequestObject(ctx, tlsServer.URL) + response, err := client.RequestObjectByGet(ctx, tlsServer.URL) var httpErr core.HttpError require.ErrorAs(t, err, &httpErr) @@ -277,7 +277,7 @@ func TestHTTPClient_RequestObjectPost(t *testing.T) { handler := http2.Handler{StatusCode: http.StatusOK, ResponseData: responseData} tlsServer, client := testServerAndClient(t, &handler) - response, err := client.RequestObjectPost(ctx, tlsServer.URL, url.Values{}) + response, err := client.RequestObjectByPost(ctx, tlsServer.URL, url.Values{}) require.NoError(t, err) assert.Equal(t, responseData, response) @@ -285,7 +285,7 @@ func TestHTTPClient_RequestObjectPost(t *testing.T) { t.Run("error - invalid request_uri", func(t *testing.T) { _, client := testServerAndClient(t, &http2.Handler{}) - response, err := client.RequestObjectPost(ctx, ":", url.Values{}) + response, err := client.RequestObjectByPost(ctx, ":", url.Values{}) assert.EqualError(t, err, "parse \":\": missing protocol scheme") assert.Empty(t, response) @@ -294,7 +294,7 @@ func TestHTTPClient_RequestObjectPost(t *testing.T) { handler := http2.Handler{StatusCode: http.StatusNotFound, ResponseData: "throw this away"} tlsServer, client := testServerAndClient(t, &handler) - response, err := client.RequestObjectPost(ctx, tlsServer.URL, url.Values{}) + response, err := client.RequestObjectByPost(ctx, tlsServer.URL, url.Values{}) var httpErr core.HttpError require.ErrorAs(t, err, &httpErr) diff --git a/auth/client/iam/interface.go b/auth/client/iam/interface.go index 966e6934a8..ed29a9ac29 100644 --- a/auth/client/iam/interface.go +++ b/auth/client/iam/interface.go @@ -51,7 +51,10 @@ type Client interface { OpenIdCredentialIssuerMetadata(ctx context.Context, webDID did.DID) (*oauth.OpenIDCredentialIssuerMetadata, error) VerifiableCredentials(ctx context.Context, credentialEndpoint string, accessToken string, proofJWT string) (*CredentialResponse, error) - // RequestObject is returned from the authorization request's 'request_uri' endpoint defined in RFC9101 - // If 'request_uri_method' is 'post', a 'wallet_metadata' object can be provided to send with the request, OpenID4VP. - RequestObject(ctx context.Context, requestURI, requestURIMethod string, walletMetadata *oauth.AuthorizationServerMetadata) (string, error) + // RequestObjectByGet retrieves the RequestObjectByGet from the authorization request's 'request_uri' endpoint using a GET method as defined in RFC9101/OpenID4VP. + // This method is used when there is no 'request_uri_method', or its value is 'get'. + RequestObjectByGet(ctx context.Context, requestURI string) (string, error) + // RequestObjectByPost retrieves the RequestObjectByGet from the authorization request's 'request_uri' endpoint using a POST method as defined in RFC9101/OpenID4VP. + // This method is used when the 'request_uri_method' is 'post'. + RequestObjectByPost(ctx context.Context, requestURI string, walletMetadata oauth.AuthorizationServerMetadata) (string, error) } diff --git a/auth/client/iam/mock.go b/auth/client/iam/mock.go index a7c4898cea..6b58014a54 100644 --- a/auth/client/iam/mock.go +++ b/auth/client/iam/mock.go @@ -163,19 +163,34 @@ func (mr *MockClientMockRecorder) PresentationDefinition(ctx, endpoint any) *gom return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PresentationDefinition", reflect.TypeOf((*MockClient)(nil).PresentationDefinition), ctx, endpoint) } -// RequestObject mocks base method. -func (m *MockClient) RequestObject(ctx context.Context, requestURI, requestURIMethod string, walletMetadata *oauth.AuthorizationServerMetadata) (string, error) { +// RequestObjectByGet mocks base method. +func (m *MockClient) RequestObjectByGet(ctx context.Context, requestURI string) (string, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "RequestObject", ctx, requestURI, requestURIMethod, walletMetadata) + ret := m.ctrl.Call(m, "RequestObjectByGet", ctx, requestURI) ret0, _ := ret[0].(string) ret1, _ := ret[1].(error) return ret0, ret1 } -// RequestObject indicates an expected call of RequestObject. -func (mr *MockClientMockRecorder) RequestObject(ctx, requestURI, requestURIMethod, walletMetadata any) *gomock.Call { +// RequestObjectByGet indicates an expected call of RequestObjectByGet. +func (mr *MockClientMockRecorder) RequestObjectByGet(ctx, requestURI any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RequestObject", reflect.TypeOf((*MockClient)(nil).RequestObject), ctx, requestURI, requestURIMethod, walletMetadata) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RequestObjectByGet", reflect.TypeOf((*MockClient)(nil).RequestObjectByGet), ctx, requestURI) +} + +// RequestObjectByPost mocks base method. +func (m *MockClient) RequestObjectByPost(ctx context.Context, requestURI string, walletMetadata oauth.AuthorizationServerMetadata) (string, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "RequestObjectByPost", ctx, requestURI, walletMetadata) + ret0, _ := ret[0].(string) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// RequestObjectByPost indicates an expected call of RequestObjectByPost. +func (mr *MockClientMockRecorder) RequestObjectByPost(ctx, requestURI, walletMetadata any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RequestObjectByPost", reflect.TypeOf((*MockClient)(nil).RequestObjectByPost), ctx, requestURI, walletMetadata) } // RequestRFC021AccessToken mocks base method. diff --git a/auth/client/iam/openid4vp.go b/auth/client/iam/openid4vp.go index c4777a4475..2b18338398 100644 --- a/auth/client/iam/openid4vp.go +++ b/auth/client/iam/openid4vp.go @@ -133,32 +133,30 @@ func (c *OpenID4VPClient) AuthorizationServerMetadata(ctx context.Context, webdi return metadata, nil } -func (c *OpenID4VPClient) RequestObject(ctx context.Context, requestURI, requestURIMethod string, walletMetadata *oauth.AuthorizationServerMetadata) (string, error) { +func (c *OpenID4VPClient) RequestObjectByGet(ctx context.Context, requestURI string) (string, error) { iamClient := c.httpClient parsedURL, err := core.ParsePublicURL(requestURI, c.strictMode) if err != nil { return "", fmt.Errorf("invalid request_uri: %w", err) } - var requestObject string - switch requestURIMethod { - case "", "get": - // the wallet/client acts as authorization server - requestObject, err = iamClient.RequestObject(ctx, parsedURL.String()) - case "post": - // TODO: consider adding a 'wallet_nonce' - form := url.Values{} - if walletMetadata != nil { - metadataBytes, err := json.Marshal(*walletMetadata) - if err != nil { - return "", fmt.Errorf("failed to serialize wallet_metadata: %w", err) - } - form.Set(oauth.WalletMetadataParam, string(metadataBytes)) - } - requestObject, err = iamClient.RequestObjectPost(ctx, parsedURL.String(), form) - default: - err = fmt.Errorf("unsupported request_uri_method: %s", requestURIMethod) + requestObject, err := iamClient.RequestObjectByGet(ctx, parsedURL.String()) + if err != nil { + return "", fmt.Errorf("failed to retrieve JAR Request Object: %w", err) } + return requestObject, nil +} +func (c *OpenID4VPClient) RequestObjectByPost(ctx context.Context, requestURI string, walletMetadata oauth.AuthorizationServerMetadata) (string, error) { + iamClient := c.httpClient + parsedURL, err := core.ParsePublicURL(requestURI, c.strictMode) + if err != nil { + return "", fmt.Errorf("invalid request_uri: %w", err) + } + + // TODO: consider adding a 'wallet_nonce' + metadataBytes, _ := json.Marshal(walletMetadata) + form := url.Values{oauth.WalletMetadataParam: {string(metadataBytes)}} + requestObject, err := iamClient.RequestObjectByPost(ctx, parsedURL.String(), form) if err != nil { return "", fmt.Errorf("failed to retrieve JAR Request Object: %w", err) } diff --git a/auth/client/iam/openid4vp_test.go b/auth/client/iam/openid4vp_test.go index 70d1ea0c5e..a755346d23 100644 --- a/auth/client/iam/openid4vp_test.go +++ b/auth/client/iam/openid4vp_test.go @@ -321,32 +321,53 @@ func TestRelyingParty_RequestRFC021AccessToken(t *testing.T) { }) } -func TestIAMClient_RequestObject(t *testing.T) { - t.Run("ok - get", func(t *testing.T) { +func TestIAMClient_RequestObjectByGet(t *testing.T) { + t.Run("ok", func(t *testing.T) { ctx := createClientServerTestContext(t) requestURI := ctx.tlsServer.URL + "/request.jwt" - response, err := ctx.client.RequestObject(context.Background(), requestURI, "get", nil) + response, err := ctx.client.RequestObjectByGet(context.Background(), requestURI) require.NoError(t, err) assert.Equal(t, "Request Object", response) }) - t.Run("ok - post", func(t *testing.T) { + t.Run("error - invalid request_uri", func(t *testing.T) { + ctx := createClientServerTestContext(t) + + response, err := ctx.client.RequestObjectByGet(context.Background(), ":") + + assert.EqualError(t, err, "invalid request_uri: parse \":\": missing protocol scheme") + assert.Empty(t, response) + }) + t.Run("error - failed to get access token", func(t *testing.T) { ctx := createClientServerTestContext(t) + ctx.requestObjectJWT = nil requestURI := ctx.tlsServer.URL + "/request.jwt" - response, err := ctx.client.RequestObject(context.Background(), requestURI, "post", &oauth.AuthorizationServerMetadata{Issuer: "me"}) + response, err := ctx.client.RequestObjectByGet(context.Background(), requestURI) + + assert.EqualError(t, err, "failed to retrieve JAR Request Object: server returned HTTP 404 (expected: 200)") + assert.Empty(t, response) + }) +} + +func TestIAMClient_RequestObjectByPost(t *testing.T) { + metadata := oauth.AuthorizationServerMetadata{Issuer: "me"} + t.Run("ok", func(t *testing.T) { + ctx := createClientServerTestContext(t) + requestURI := ctx.tlsServer.URL + "/request.jwt" + + response, err := ctx.client.RequestObjectByPost(context.Background(), requestURI, metadata) require.NoError(t, err) assert.Equal(t, "Request Object", response) }) - t.Run("error - unsupported request_uri_method", func(t *testing.T) { + t.Run("error - invalid request_uri", func(t *testing.T) { ctx := createClientServerTestContext(t) - requestURI := ctx.tlsServer.URL + "/request.jwt" - response, err := ctx.client.RequestObject(context.Background(), requestURI, "fail", nil) + response, err := ctx.client.RequestObjectByPost(context.Background(), ":", metadata) - assert.EqualError(t, err, "failed to retrieve JAR Request Object: unsupported request_uri_method: fail") + assert.EqualError(t, err, "invalid request_uri: parse \":\": missing protocol scheme") assert.Empty(t, response) }) t.Run("error - failed to get access token", func(t *testing.T) { @@ -354,7 +375,7 @@ func TestIAMClient_RequestObject(t *testing.T) { ctx.requestObjectJWT = nil requestURI := ctx.tlsServer.URL + "/request.jwt" - response, err := ctx.client.RequestObject(context.Background(), requestURI, "get", nil) + response, err := ctx.client.RequestObjectByPost(context.Background(), requestURI, metadata) assert.EqualError(t, err, "failed to retrieve JAR Request Object: server returned HTTP 404 (expected: 200)") assert.Empty(t, response) diff --git a/auth/oauth/error.go b/auth/oauth/error.go index 1949083dc4..6269d86546 100644 --- a/auth/oauth/error.go +++ b/auth/oauth/error.go @@ -57,11 +57,11 @@ const ( InvalidScope ErrorCode = "invalid_scope" // InvalidPresentationDefinitionURI is returned when the requested presentation definition URI is invalid or can't be reached. InvalidPresentationDefinitionURI ErrorCode = "invalid_presentation_definition_uri" - // InvalidRequestObject is returned when the JAR Request Object signature validation or decryption fails. RFC9101 + // InvalidRequestObject is returned when the JAR Request Object signature validation or decryption fails. (RFC9101) InvalidRequestObject ErrorCode = "invalid_request_object" - // InvalidRequestURI is returned whn the request_uri in the authorization request returns an error or contains invalid data. RFC9101 + // InvalidRequestURI is returned whn the request_uri in the authorization request returns an error or contains invalid data. (RFC9101) InvalidRequestURI ErrorCode = "invalid_request_uri" - // InvalidRequestURIMethod is returned when the request_uri_method is not 'get' or 'post'. OpenID4VP + // InvalidRequestURIMethod is returned when the request_uri_method is not 'get' or 'post'. (OpenID4VP) InvalidRequestURIMethod ErrorCode = "invalid_request_uri_method" ) diff --git a/docs/_static/auth/iam.partial.yaml b/docs/_static/auth/iam.partial.yaml index 6b6227f17b..92c6b36646 100644 --- a/docs/_static/auth/iam.partial.yaml +++ b/docs/_static/auth/iam.partial.yaml @@ -45,7 +45,7 @@ paths: /oauth2/{did}/token: post: summary: Used by to request access- or refresh tokens. - description: | + description: | Specified by https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0.html#name-token-endpoint. Requires the use of PKCE as specified by https://datatracker.ietf.org/doc/html/rfc7636 and optionally DPoP as specified by https://datatracker.ietf.org/doc/html/rfc9449. operationId: handleTokenRequest @@ -162,7 +162,7 @@ paths: Get the Request Object containing the OAuth 2.0 authorization request parameters, including extension parameters. The Request Object is a JWT with signature (JWS). See [RFC9101] The OAuth 2.0 Authorization Framework: JWT-Secured Authorization Request (JAR) for details. - operationId: getRequestJWT + operationId: requestJWTByGet tags: - oauth2 responses: @@ -176,7 +176,7 @@ paths: $ref: '../common/error_response.yaml' post: summary: Provide missing information to Client to finish Authorization request's Request Object, which is then returned. - operationId: postRequestJWT + operationId: requestJWTByPost tags: - oauth2 requestBody: @@ -277,7 +277,7 @@ paths: required: true schema: type: string - description: | + description: | The scope for which a presentation definition is requested. Multiple scopes can be specified by separating them with a space. example: usecase patient:x:read - name: wallet_owner_type @@ -296,7 +296,7 @@ paths: /oauth2/{did}/response: post: summary: Used by wallets to post the authorization response or error to. - description: | + description: | Specified by https://openid.net/specs/openid-4-verifiable-presentations-1_0.html#name-response-mode-direct_postjw The response is either an error response with error, error_description and state filled or a submission with vp_token and presentation_submission filled. When an error is posted, the state is used to fetch the holder's callbackURI from the verifiers client state. @@ -344,10 +344,10 @@ paths: # TODO: What format to use? (codegenerator breaks on aliases) # See issue https://github.com/nuts-foundation/nuts-node/issues/2365 # create aliases for the specced path -# /iam/{did}/oauth-authorization-server: -# $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}' + # /iam/{did}/oauth-authorization-server: + # $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/{id}: get: tags: @@ -458,7 +458,7 @@ paths: summary: Get the StatusList2021Credential for the given DID and page description: > Returns the StatusList2021Credential as specified in https://www.w3.org/TR/2023/WD-vc-status-list-20230427/ - + error returns: * 404 - id or page not found; possibly be non-existing, deactivated, or not managed by this node * 500 - internal server error @@ -488,7 +488,7 @@ paths: * invalid_request - one of the provided parameters is wrong. * server_error - internal processing of the Oid4VCI flow has a system error. * access_denied - an access problem occurred with the internal processing of the Oid4VCI flow. - + If the system is somehow not able to return a redirect, the following HTTP status codes will be returned: * 500 - an system error occurred during processing