From 159bcc0defe12778c7fbef8b449b874cad49a371 Mon Sep 17 00:00:00 2001 From: Gerard Snaauw Date: Tue, 14 May 2024 17:47:22 +0200 Subject: [PATCH] cleanup oauth constants --- auth/api/iam/api.go | 18 ++++---- auth/api/iam/api_test.go | 34 +++++++-------- auth/api/iam/metadata.go | 4 +- auth/api/iam/metadata_test.go | 4 +- auth/api/iam/openid4vp.go | 38 ++++++++--------- auth/api/iam/openid4vp_test.go | 43 ++++++++++--------- auth/api/iam/session.go | 3 +- auth/api/iam/types.go | 72 +++---------------------------- auth/api/iam/user.go | 13 +++--- auth/api/iam/user_test.go | 5 +-- auth/client/iam/openid4vp.go | 2 +- auth/oauth/types.go | 77 ++++++++++++++++++++++++---------- 12 files changed, 140 insertions(+), 173 deletions(-) diff --git a/auth/api/iam/api.go b/auth/api/iam/api.go index 98d46d575b..feba28055a 100644 --- a/auth/api/iam/api.go +++ b/auth/api/iam/api.go @@ -337,7 +337,7 @@ func (r Wrapper) handleAuthorizeRequest(ctx context.Context, ownDID did.DID, req } switch requestObject.get(oauth.ResponseTypeParam) { - case responseTypeCode: + case oauth.CodeResponseType: // Options: // - Regular authorization code flow for EHR data access through access token, authentication of end-user using OpenID4VP. // - OpenID4VCI; authorization code flow for credential issuance to (end-user) wallet @@ -359,7 +359,7 @@ func (r Wrapper) handleAuthorizeRequest(ctx context.Context, ownDID did.DID, req Description: "client_id must be a did:web", } } - case responseTypeVPToken: + case oauth.VPTokenResponseType: // Options: // - OpenID4VP flow, vp_token is sent in Authorization Response // non-spec: if the scheme is openid4vp (URL starts with openid4vp:), the OpenID4VP request should be handled by a user wallet, rather than an organization wallet. @@ -773,13 +773,13 @@ func (r Wrapper) RequestOid4vciCredentialIssuance(ctx context.Context, request R } // Build the redirect URL, the client browser should be redirected to. redirectUrl := nutsHttp.AddQueryParams(*endpoint, map[string]string{ - "response_type": "code", - "state": state, - "client_id": requestHolder.String(), - "authorization_details": string(authorizationDetails), - "redirect_uri": redirectUri.String(), - "code_challenge": pkceParams.Challenge, - "code_challenge_method": pkceParams.ChallengeMethod, + oauth.ResponseTypeParam: oauth.CodeResponseType, + oauth.StateParam: state, + oauth.ClientIDParam: requestHolder.String(), + oauth.AuthorizationDetailsParam: string(authorizationDetails), + oauth.RedirectURIParam: redirectUri.String(), + oauth.CodeChallengeParam: pkceParams.Challenge, + oauth.CodeChallengeMethodParam: pkceParams.ChallengeMethod, }) log.Logger().Debugf("generated the following redirect_uri for did %s, to issuer %s: %s", requestHolder.String(), issuerDid.String(), redirectUri.String()) diff --git a/auth/api/iam/api_test.go b/auth/api/iam/api_test.go index b7ef1ebf68..2d98230787 100644 --- a/auth/api/iam/api_test.go +++ b/auth/api/iam/api_test.go @@ -315,7 +315,7 @@ func TestWrapper_HandleAuthorizeRequest(t *testing.T) { oauth.ClientIDParam: holderDID.String(), oauth.NonceParam: "nonce", oauth.RedirectURIParam: "https://example.com", - oauth.ResponseTypeParam: responseTypeCode, + oauth.ResponseTypeParam: oauth.CodeResponseType, oauth.ScopeParam: "test", oauth.StateParam: "state", oauth.CodeChallengeParam: "code_challenge", @@ -328,7 +328,7 @@ func TestWrapper_HandleAuthorizeRequest(t *testing.T) { expectedURL := "https://example.com/authorize?client_id=did%3Aweb%3Aexample.com%3Aiam%3Averifier&request_uri=https://example.com/oauth2/" + verifierDID.String() + "/request.jwt/&request_uri_method=get" serverMetadata := oauth.AuthorizationServerMetadata{ AuthorizationEndpoint: "https://example.com/authorize", - ClientIdSchemesSupported: []string{didScheme}, + ClientIdSchemesSupported: []string{didClientIDScheme}, VPFormats: oauth.DefaultOpenIDSupportedFormats(), RequireSignedRequestObject: true, } @@ -339,11 +339,11 @@ func TestWrapper_HandleAuthorizeRequest(t *testing.T) { params := req.Claims // check the parameters assert.NotEmpty(t, params[oauth.NonceParam]) - assert.Equal(t, didScheme, params[clientIDSchemeParam]) - assert.Equal(t, responseTypeVPToken, params[oauth.ResponseTypeParam]) - assert.Equal(t, "https://example.com/oauth2/did:web:example.com:iam:verifier/response", params[responseURIParam]) - assert.Equal(t, "https://example.com/oauth2/did:web:example.com:iam:verifier/oauth-client", params[clientMetadataURIParam]) - assert.Equal(t, responseModeDirectPost, params[responseModeParam]) + assert.Equal(t, didClientIDScheme, params[oauth.ClientIDSchemeParam]) + assert.Equal(t, oauth.VPTokenResponseType, params[oauth.ResponseTypeParam]) + assert.Equal(t, "https://example.com/oauth2/did:web:example.com:iam:verifier/response", params[oauth.ResponseURIParam]) + assert.Equal(t, "https://example.com/oauth2/did:web:example.com:iam:verifier/oauth-client", params[oauth.ClientMetadataURIParam]) + assert.Equal(t, responseModeDirectPost, params[oauth.ResponseModeParam]) assert.NotEmpty(t, params[oauth.StateParam]) return req }) @@ -370,16 +370,16 @@ func TestWrapper_HandleAuthorizeRequest(t *testing.T) { // HandleAuthorizeRequest requestParams := oauthParameters{ - oauth.ClientIDParam: verifierDID.String(), - clientIDSchemeParam: didScheme, - clientMetadataURIParam: "https://example.com/.well-known/authorization-server/iam/verifier", - oauth.NonceParam: "nonce", - presentationDefUriParam: "https://example.com/oauth2/did:web:example.com:iam:verifier/presentation_definition?scope=test", - responseURIParam: "https://example.com/oauth2/did:web:example.com:iam:verifier/response", - responseModeParam: responseModeDirectPost, - oauth.ResponseTypeParam: responseTypeVPToken, - oauth.ScopeParam: "test", - oauth.StateParam: "state", + oauth.ClientIDParam: verifierDID.String(), + oauth.ClientIDSchemeParam: didClientIDScheme, + oauth.ClientMetadataURIParam: "https://example.com/.well-known/authorization-server/iam/verifier", + oauth.NonceParam: "nonce", + oauth.PresentationDefUriParam: "https://example.com/oauth2/did:web:example.com:iam:verifier/presentation_definition?scope=test", + oauth.ResponseURIParam: "https://example.com/oauth2/did:web:example.com:iam:verifier/response", + oauth.ResponseModeParam: responseModeDirectPost, + oauth.ResponseTypeParam: oauth.VPTokenResponseType, + oauth.ScopeParam: "test", + oauth.StateParam: "state", } ctx.vdr.EXPECT().IsOwner(gomock.Any(), holderDID).Return(true, nil) ctx.jar.EXPECT().Parse(gomock.Any(), holderDID, gomock.Any()).Return(requestParams, nil) diff --git a/auth/api/iam/metadata.go b/auth/api/iam/metadata.go index dc14b738af..7fbced3368 100644 --- a/auth/api/iam/metadata.go +++ b/auth/api/iam/metadata.go @@ -72,7 +72,7 @@ func staticAuthorizationServerMetadata() oauth.AuthorizationServerMetadata { return oauth.AuthorizationServerMetadata{ Issuer: "https://self-issued.me/v2", AuthorizationEndpoint: "openid4vp:", - ResponseTypesSupported: []string{responseTypeVPToken}, + ResponseTypesSupported: []string{oauth.VPTokenResponseType}, VPFormatsSupported: map[string]map[string][]string{ "jwt_vp_json": {"alg_values_supported": []string{string(jwa.ES256)}}, "jwt_vc_json": {"alg_values_supported": []string{string(jwa.ES256)}}, @@ -91,6 +91,6 @@ func clientMetadata(identity url.URL) oauth.OAuthClientMetadata { SoftwareID: softwareID, // nuts-node-refimpl SoftwareVersion: softwareVersion, // version tag or "unknown" VPFormats: oauth.DefaultOpenIDSupportedFormats(), - ClientIdScheme: didScheme, + ClientIdScheme: didClientIDScheme, } } diff --git a/auth/api/iam/metadata_test.go b/auth/api/iam/metadata_test.go index 4d380d0679..6f346ddc5b 100644 --- a/auth/api/iam/metadata_test.go +++ b/auth/api/iam/metadata_test.go @@ -37,7 +37,7 @@ func Test_authorizationServerMetadata(t *testing.T) { AuthorizationEndpoint: "openid4vp:", ClientIdSchemesSupported: []string{"did"}, DPoPSigningAlgValuesSupported: jwx.SupportedAlgorithmsAsStrings(), - GrantTypesSupported: []string{"authorization_code", "vp_token", "urn:ietf:params:oauth:grant-type:pre-authorized_code"}, + GrantTypesSupported: []string{"authorization_code", "vp_token-bearer"}, Issuer: didExample.String(), PreAuthorizedGrantAnonymousAccessSupported: true, PresentationDefinitionUriSupported: &presentationDefinitionURISupported, @@ -75,7 +75,7 @@ func Test_clientMetadata(t *testing.T) { expected := OAuthClientMetadata{ RedirectURIs: nil, TokenEndpointAuthMethod: "none", - GrantTypes: []string{"authorization_code", "vp_token", "urn:ietf:params:oauth:grant-type:pre-authorized_code"}, + GrantTypes: []string{"authorization_code", "vp_token-bearer"}, ResponseTypes: []string{"code", "vp_token"}, Scope: "", Contacts: nil, diff --git a/auth/api/iam/openid4vp.go b/auth/api/iam/openid4vp.go index ed7f82f048..f2e47c2c5f 100644 --- a/auth/api/iam/openid4vp.go +++ b/auth/api/iam/openid4vp.go @@ -119,7 +119,7 @@ func (r Wrapper) handleAuthorizeRequestFromHolder(ctx context.Context, verifier return nil, withCallbackURI(oauthError(oauth.ServerError, "failed to get metadata from wallet", err), redirectURL) } // check metadata for supported client_id_schemes - if !slices.Contains(metadata.ClientIdSchemesSupported, didScheme) { + if !slices.Contains(metadata.ClientIdSchemesSupported, didClientIDScheme) { return nil, withCallbackURI(oauthError(oauth.InvalidRequest, "wallet metadata does not contain did in client_id_schemes_supported"), redirectURL) } @@ -200,12 +200,12 @@ func (r Wrapper) nextOpenID4VPFlow(ctx context.Context, state string, session OA metadataURL := ownURL.JoinPath(oauth.ClientMetadataPath) modifier := func(values map[string]string) { - values[oauth.ResponseTypeParam] = responseTypeVPToken - values[clientIDSchemeParam] = didScheme - values[responseURIParam] = callbackURL.String() - values[presentationDefUriParam] = presentationDefinitionURI.String() - values[clientMetadataURIParam] = metadataURL.String() - values[responseModeParam] = responseModeDirectPost + values[oauth.ResponseTypeParam] = oauth.VPTokenResponseType + values[oauth.ClientIDSchemeParam] = didClientIDScheme + values[oauth.ResponseURIParam] = callbackURL.String() + values[oauth.PresentationDefUriParam] = presentationDefinitionURI.String() + values[oauth.ClientMetadataURIParam] = metadataURL.String() + values[oauth.ResponseModeParam] = responseModeDirectPost values[oauth.NonceParam] = nonce values[oauth.StateParam] = state } @@ -243,7 +243,7 @@ func (r Wrapper) nextOpenID4VPFlow(ctx context.Context, state string, session OA // response_type, REQUIRED. Value MUST be set to "vp_token". // client_id, REQUIRED. This must be a did:web // client_id_scheme, REQUIRED. This must be did -// clientMetadataURIParam, REQUIRED. This must be the verifier metadata endpoint +// client_metadata_uri, REQUIRED. This must be the verifier metadata endpoint // nonce, REQUIRED. // response_uri, REQUIRED. This must be the verifier node url // response_mode, REQUIRED. Value MUST be "direct_post" @@ -253,13 +253,13 @@ func (r Wrapper) nextOpenID4VPFlow(ctx context.Context, state string, session OA // missing or invalid parameters are all mapped to invalid_request // any operation that fails is mapped to server_error, this includes unreachable or broken backends. func (r Wrapper) handleAuthorizeRequestFromVerifier(ctx context.Context, tenantDID did.DID, params oauthParameters, walletOwnerType WalletOwnerType) (HandleAuthorizeRequestResponseObject, error) { - responseMode := params.get(responseModeParam) + responseMode := params.get(oauth.ResponseModeParam) if responseMode != responseModeDirectPost { return nil, oauth.OAuth2Error{Code: oauth.InvalidRequest, Description: "invalid response_mode parameter"} } // check the response URL because later errors will redirect to this URL - responseURI := params.get(responseURIParam) + responseURI := params.get(oauth.ResponseURIParam) if responseURI == "" { return nil, oauth.OAuth2Error{Code: oauth.InvalidRequest, Description: "missing response_uri parameter"} } @@ -269,7 +269,7 @@ func (r Wrapper) handleAuthorizeRequestFromVerifier(ctx context.Context, tenantD return nil, oauth.OAuth2Error{Code: oauth.InvalidRequest, Description: "missing state parameter"} } - if params.get(clientIDSchemeParam) != didScheme { + if params.get(oauth.ClientIDSchemeParam) != didClientIDScheme { return r.sendAndHandleDirectPostError(ctx, oauth.OAuth2Error{Code: oauth.InvalidRequest, Description: "invalid client_id_scheme parameter"}, responseURI, state) } @@ -343,8 +343,8 @@ func (r Wrapper) handleAuthorizeRequestFromVerifier(ctx context.Context, tenantD func (r Wrapper) getClientMetadataFromRequest(ctx context.Context, params oauthParameters) (*oauth.OAuthClientMetadata, *oauth.OAuth2Error) { var metadata *oauth.OAuthClientMetadata var err error - if metadataString := params.get(clientMetadataParam); metadataString != "" { - if params.get(clientMetadataURIParam) != "" { + if metadataString := params.get(oauth.ClientMetadataParam); metadataString != "" { + if params.get(oauth.ClientMetadataURIParam) != "" { return nil, &oauth.OAuth2Error{Code: oauth.InvalidRequest, Description: "client_metadata and client_metadata_uri are mutually exclusive", InternalError: err} } err = json.Unmarshal([]byte(metadataString), &metadata) @@ -352,7 +352,7 @@ func (r Wrapper) getClientMetadataFromRequest(ctx context.Context, params oauthP return nil, &oauth.OAuth2Error{Code: oauth.InvalidRequest, Description: "invalid client_metadata", InternalError: err} } } else { - metadata, err = r.auth.IAMClient().ClientMetadata(ctx, params.get(clientMetadataURIParam)) + metadata, err = r.auth.IAMClient().ClientMetadata(ctx, params.get(oauth.ClientMetadataURIParam)) if err != nil { return nil, &oauth.OAuth2Error{Code: oauth.ServerError, Description: "failed to get client metadata (verifier)", InternalError: err} } @@ -363,8 +363,8 @@ func (r Wrapper) getClientMetadataFromRequest(ctx context.Context, params oauthP func (r Wrapper) getPresentationDefinitionFromRequest(ctx context.Context, params oauthParameters) (*pe.PresentationDefinition, *oauth.OAuth2Error) { var presentationDefinition *pe.PresentationDefinition var err error - if pdString := params.get(presentationDefParam); pdString != "" { - if params.get(presentationDefUriParam) != "" { + if pdString := params.get(oauth.PresentationDefParam); pdString != "" { + if params.get(oauth.PresentationDefUriParam) != "" { return nil, &oauth.OAuth2Error{Code: oauth.InvalidRequest, Description: "presentation_definition and presentation_definition_uri are mutually exclusive"} } err = json.Unmarshal([]byte(pdString), &presentationDefinition) @@ -372,7 +372,7 @@ func (r Wrapper) getPresentationDefinitionFromRequest(ctx context.Context, param return nil, &oauth.OAuth2Error{Code: oauth.InvalidRequest, Description: "invalid presentation_definition", InternalError: err} } } else { - presentationDefinitionURI := params.get(presentationDefUriParam) + presentationDefinitionURI := params.get(oauth.PresentationDefUriParam) presentationDefinition, err = r.auth.IAMClient().PresentationDefinition(ctx, presentationDefinitionURI) if err != nil { return nil, &oauth.OAuth2Error{Code: oauth.InvalidPresentationDefinitionURI, Description: fmt.Sprintf("failed to retrieve presentation definition on %s", presentationDefinitionURI), InternalError: err} @@ -605,8 +605,8 @@ func (r Wrapper) handleAuthorizeResponseSubmission(ctx context.Context, request // construct redirect URI according to RFC6749 redirectURI := httpNuts.AddQueryParams(*callbackURI, map[string]string{ - oauth.CodeParam: authorizationCode, - oauth.StateParam: session.ClientState, + oauth.CodeResponseType: authorizationCode, + oauth.StateParam: session.ClientState, }) return HandleAuthorizeResponse200JSONResponse{RedirectURI: redirectURI.String()}, nil } diff --git a/auth/api/iam/openid4vp_test.go b/auth/api/iam/openid4vp_test.go index 13a06079e9..607f950520 100644 --- a/auth/api/iam/openid4vp_test.go +++ b/auth/api/iam/openid4vp_test.go @@ -21,17 +21,16 @@ package iam import ( "context" "encoding/json" - "github.com/nuts-foundation/nuts-node/policy" "net/http" "net/url" "strings" "testing" "github.com/lestrrat-go/jwx/v2/jwt" - "github.com/nuts-foundation/go-did/did" "github.com/nuts-foundation/go-did/vc" "github.com/nuts-foundation/nuts-node/auth/oauth" + "github.com/nuts-foundation/nuts-node/policy" "github.com/nuts-foundation/nuts-node/storage" "github.com/nuts-foundation/nuts-node/test" "github.com/nuts-foundation/nuts-node/vcr/holder" @@ -154,7 +153,7 @@ func TestWrapper_handleAuthorizeRequestFromHolder(t *testing.T) { ctx.policy.EXPECT().PresentationDefinitions(gomock.Any(), verifierDID, "test").Return(pe.WalletOwnerMapping{pe.WalletOwnerOrganization: PresentationDefinition{}}, nil) params := defaultParams() ctx.iamClient.EXPECT().AuthorizationServerMetadata(context.Background(), holderDID).Return(&oauth.AuthorizationServerMetadata{ - ClientIdSchemesSupported: []string{didScheme}, + ClientIdSchemesSupported: []string{didClientIDScheme}, }, nil).Times(2) _, err := ctx.client.handleAuthorizeRequestFromHolder(context.Background(), verifierDID, params) @@ -171,16 +170,16 @@ func TestWrapper_handleAuthorizeRequestFromVerifier(t *testing.T) { pdEndpoint := "https://example.com/iam/verifier/presentation_definition?scope=test" defaultParams := func() map[string]interface{} { return map[string]interface{}{ - oauth.ClientIDParam: verifierDID.String(), - clientIDSchemeParam: didScheme, - clientMetadataURIParam: "https://example.com/.well-known/authorization-server/iam/verifier", - oauth.NonceParam: "nonce", - presentationDefUriParam: "https://example.com/iam/verifier/presentation_definition?scope=test", - responseModeParam: responseModeDirectPost, - responseURIParam: responseURI, - oauth.ResponseTypeParam: responseTypeVPToken, - oauth.ScopeParam: "test", - oauth.StateParam: "state", + oauth.ClientIDParam: verifierDID.String(), + oauth.ClientIDSchemeParam: didClientIDScheme, + oauth.ClientMetadataURIParam: "https://example.com/.well-known/authorization-server/iam/verifier", + oauth.NonceParam: "nonce", + oauth.PresentationDefUriParam: "https://example.com/iam/verifier/presentation_definition?scope=test", + oauth.ResponseModeParam: responseModeDirectPost, + oauth.ResponseURIParam: responseURI, + oauth.ResponseTypeParam: oauth.VPTokenResponseType, + oauth.ScopeParam: "test", + oauth.StateParam: "state", } } authzCodeSession := OAuthSession{ @@ -216,7 +215,7 @@ func TestWrapper_handleAuthorizeRequestFromVerifier(t *testing.T) { t.Run("invalid client_id_scheme", func(t *testing.T) { ctx := newTestClient(t) params := defaultParams() - params[clientIDSchemeParam] = "other" + params[oauth.ClientIDSchemeParam] = "other" expectPostError(t, ctx, oauth.InvalidRequest, "invalid client_id_scheme parameter", responseURI, "state") _, err := ctx.client.handleAuthorizeRequestFromVerifier(httpRequestCtx, holderDID, params, pe.WalletOwnerOrganization) @@ -247,7 +246,7 @@ func TestWrapper_handleAuthorizeRequestFromVerifier(t *testing.T) { t.Run("client_metadata and client_metadata_uri are mutually exclusive", func(t *testing.T) { ctx := newTestClient(t) params := defaultParams() - params[clientMetadataParam] = "not empty" + params[oauth.ClientMetadataParam] = "not empty" _ = ctx.client.userSessionStore().Put(userSessionID, userSession) expectPostError(t, ctx, oauth.InvalidRequest, "client_metadata and client_metadata_uri are mutually exclusive", responseURI, "state") @@ -258,8 +257,8 @@ func TestWrapper_handleAuthorizeRequestFromVerifier(t *testing.T) { t.Run("invalid client_metadata", func(t *testing.T) { ctx := newTestClient(t) params := defaultParams() - delete(params, clientMetadataURIParam) - params[clientMetadataParam] = "{invalid" + delete(params, oauth.ClientMetadataURIParam) + params[oauth.ClientMetadataParam] = "{invalid" _ = ctx.client.userSessionStore().Put(userSessionID, userSession) expectPostError(t, ctx, oauth.InvalidRequest, "invalid client_metadata", responseURI, "state") @@ -281,7 +280,7 @@ func TestWrapper_handleAuthorizeRequestFromVerifier(t *testing.T) { t.Run("missing response_mode", func(t *testing.T) { ctx := newTestClient(t) params := defaultParams() - delete(params, responseModeParam) + delete(params, oauth.ResponseModeParam) _, err := ctx.client.handleAuthorizeRequestFromVerifier(httpRequestCtx, holderDID, params, pe.WalletOwnerOrganization) @@ -290,7 +289,7 @@ func TestWrapper_handleAuthorizeRequestFromVerifier(t *testing.T) { t.Run("missing response_uri", func(t *testing.T) { ctx := newTestClient(t) params := defaultParams() - delete(params, responseURIParam) + delete(params, oauth.ResponseURIParam) _, err := ctx.client.handleAuthorizeRequestFromVerifier(httpRequestCtx, holderDID, params, pe.WalletOwnerOrganization) @@ -309,7 +308,7 @@ func TestWrapper_handleAuthorizeRequestFromVerifier(t *testing.T) { ctx := newTestClient(t) ctx.iamClient.EXPECT().ClientMetadata(gomock.Any(), "https://example.com/.well-known/authorization-server/iam/verifier").Return(&clientMetadata, nil) params := defaultParams() - params[presentationDefParam] = "not empty" + params[oauth.PresentationDefParam] = "not empty" _ = ctx.client.userSessionStore().Put(userSessionID, userSession) expectPostError(t, ctx, oauth.InvalidRequest, "presentation_definition and presentation_definition_uri are mutually exclusive", responseURI, "state") @@ -320,8 +319,8 @@ func TestWrapper_handleAuthorizeRequestFromVerifier(t *testing.T) { t.Run("invalid presentation_definition", func(t *testing.T) { ctx := newTestClient(t) params := defaultParams() - delete(params, presentationDefUriParam) - params[presentationDefParam] = "{invalid" + delete(params, oauth.PresentationDefUriParam) + params[oauth.PresentationDefParam] = "{invalid" ctx.iamClient.EXPECT().ClientMetadata(gomock.Any(), "https://example.com/.well-known/authorization-server/iam/verifier").Return(&clientMetadata, nil) _ = ctx.client.userSessionStore().Put(userSessionID, userSession) expectPostError(t, ctx, oauth.InvalidRequest, "invalid presentation_definition", responseURI, "state") diff --git a/auth/api/iam/session.go b/auth/api/iam/session.go index 5b994ed699..2489ad058e 100644 --- a/auth/api/iam/session.go +++ b/auth/api/iam/session.go @@ -21,12 +21,13 @@ package iam import ( "errors" "fmt" + "net/url" + "github.com/lestrrat-go/jwx/v2/jwk" "github.com/nuts-foundation/go-did/did" "github.com/nuts-foundation/go-did/vc" "github.com/nuts-foundation/nuts-node/http" "github.com/nuts-foundation/nuts-node/vcr/pe" - "net/url" ) // OAuthSession is the session object that is used to store information about the OAuth request. diff --git a/auth/api/iam/types.go b/auth/api/iam/types.go index 87845f0d63..37a6a9252e 100644 --- a/auth/api/iam/types.go +++ b/auth/api/iam/types.go @@ -74,84 +74,22 @@ type CookieReader interface { } const ( - // oauth.ResponseTypeParam is the name of the response_type parameter. - // Specified by https://datatracker.ietf.org/doc/html/rfc6749#section-3.1.1 - // - // TODO: reconsider the following - // Extension response types MAY contain a space-delimited (%x20) list of - // values, where the order of values does not matter (e.g., response - // type "a b" is the same as "b a"). The meaning of such composite - // response types is defined by their respective specifications. - // - // If an authorization request is missing the "response_type" parameter, - // or if the response type is not understood, the authorization server - // MUST return an error response as described in Section 4.1.2.1. - - // responseTypeCode is the default response_type in the OAuth2 authorized code flow - responseTypeCode = "code" - // responseTypeVPToken is defined in the OpenID4VP vp_token flow - // https://openid.bitbucket.io/connect/openid-4-verifiable-presentations-1_0.html#appendix-B - responseTypeVPToken = "vp_token" -) - -var responseTypesSupported = []string{responseTypeCode, responseTypeVPToken} - -const ( - // responseModeParam is the name of the OAuth2 response_mode parameter. - // Specified by https://openid.net/specs/oauth-v2-multiple-response-types-1_0.html - responseModeParam = "response_mode" // responseModeQuery returns the answer to the authorization request append as query parameters to the provided redirect_uri responseModeQuery = "query" // default if no response_mode is specified // responseModeDirectPost signals the Authorization Server to POST the requested presentation definition to the provided response_uri - // https://openid.bitbucket.io/connect/openid-4-verifiable-presentations-1_0.html#name-response-mode-direct_post responseModeDirectPost = "direct_post" ) var responseModesSupported = []string{responseModeQuery, responseModeDirectPost} -const ( - // grantTypeAuthorizationCode is the default OAuth2 grant type - grantTypeAuthorizationCode = "authorization_code" - // grantTypeVPToken is used to present a vp_token in exchange for an access_token in service-to-service flows - // TODO: EBSI inspired flow that is not standardized - // https://api-conformance.ebsi.eu/docs/ct/verifiable-presentation-exchange-guidelines-v3#service-to-service-token-flow - grantTypeVPToken = "vp_token" - // grantTypePreAuthorizedCode is defined in the pre-authorized_code flow of OpenID4VCI - // https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0.html#name-sub-namespace-registration - grantTypePreAuthorizedCode = "urn:ietf:params:oauth:grant-type:pre-authorized_code" -) - -var grantTypesSupported = []string{grantTypeAuthorizationCode, grantTypeVPToken, grantTypePreAuthorizedCode} - -// clientIdSchemesSupported lists the supported client_id_scheme -// https://openid.bitbucket.io/connect/openid-4-verifiable-presentations-1_0.html#name-verifier-metadata-managemen -var clientIdSchemesSupported = []string{didScheme} - -// clientMetadataParam is the name of the OpenID4VP client_metadata parameter. -// Specified by https://openid.bitbucket.io/connect/openid-4-verifiable-presentations-1_0.html#name-authorization-request -const clientMetadataParam = "client_metadata" - -// clientMetadataParam is the name of the OpenID4VP client_metadata_uri parameter. -// Specified by https://openid.bitbucket.io/connect/openid-4-verifiable-presentations-1_0.html#name-authorization-request -const clientMetadataURIParam = "client_metadata_uri" - -// clientIDSchemeParam is the name of the OpenID4VP client_id_scheme parameter. -// Specified by https://openid.bitbucket.io/connect/openid-4-verifiable-presentations-1_0.html#name-authorization-request -const clientIDSchemeParam = "client_id_scheme" - -// didScheme is the client_id_scheme value for DIDs -const didScheme = "did" +var responseTypesSupported = []string{oauth.CodeResponseType, oauth.VPTokenResponseType} -// responseURIParam is the name of the OpenID4VP response_uri parameter. -const responseURIParam = "response_uri" +var grantTypesSupported = []string{oauth.AuthorizationCodeGrantType, oauth.VpTokenGrantType} -// presentationDefParam is the name of the OpenID4VP presentation_definition parameter. -// Specified by https://openid.bitbucket.io/connect/openid-4-verifiable-presentations-1_0.html#name-presentation_definition-par -const presentationDefParam = "presentation_definition" +var clientIdSchemesSupported = []string{didClientIDScheme} -// presentationDefUriParam is the name of the OpenID4VP presentation_definition_uri parameter. -// Specified by https://openid.bitbucket.io/connect/openid-4-verifiable-presentations-1_0.html#name-presentation_definition_uri -const presentationDefUriParam = "presentation_definition_uri" +// didClientIDScheme is the client_id_scheme value for DIDs +const didClientIDScheme = "did" const ( AccessTokenTypeBearer = "Bearer" diff --git a/auth/api/iam/user.go b/auth/api/iam/user.go index d612e5cae0..71ee01c4b7 100644 --- a/auth/api/iam/user.go +++ b/auth/api/iam/user.go @@ -28,18 +28,17 @@ import ( "strings" "time" + "github.com/labstack/echo/v4" "github.com/lestrrat-go/jwx/v2/jwk" ssi "github.com/nuts-foundation/go-did" + "github.com/nuts-foundation/go-did/did" "github.com/nuts-foundation/go-did/vc" + "github.com/nuts-foundation/nuts-node/auth/log" "github.com/nuts-foundation/nuts-node/auth/oauth" + "github.com/nuts-foundation/nuts-node/crypto" "github.com/nuts-foundation/nuts-node/storage" "github.com/nuts-foundation/nuts-node/vcr/credential" - issuer "github.com/nuts-foundation/nuts-node/vcr/issuer" - - "github.com/labstack/echo/v4" - "github.com/nuts-foundation/go-did/did" - "github.com/nuts-foundation/nuts-node/auth/log" - "github.com/nuts-foundation/nuts-node/crypto" + "github.com/nuts-foundation/nuts-node/vcr/issuer" ) const ( @@ -148,7 +147,7 @@ func (r Wrapper) handleUserLanding(echoCtx echo.Context) error { values[oauth.CodeChallengeParam] = oauthSession.PKCEParams.Challenge values[oauth.CodeChallengeMethodParam] = oauthSession.PKCEParams.ChallengeMethod values[oauth.RedirectURIParam] = callbackURL.String() - values[oauth.ResponseTypeParam] = responseTypeCode + values[oauth.ResponseTypeParam] = oauth.CodeResponseType values[oauth.StateParam] = oauthSession.ClientState values[oauth.ScopeParam] = accessTokenRequest.Body.Scope } diff --git a/auth/api/iam/user_test.go b/auth/api/iam/user_test.go index ea6748cb66..24a9501da4 100644 --- a/auth/api/iam/user_test.go +++ b/auth/api/iam/user_test.go @@ -25,13 +25,12 @@ import ( "testing" "time" - "github.com/nuts-foundation/nuts-node/auth/oauth" - "github.com/lestrrat-go/jwx/v2/jwa" "github.com/lestrrat-go/jwx/v2/jwk" ssi "github.com/nuts-foundation/go-did" "github.com/nuts-foundation/go-did/did" "github.com/nuts-foundation/go-did/vc" + "github.com/nuts-foundation/nuts-node/auth/oauth" cryptoNuts "github.com/nuts-foundation/nuts-node/crypto" "github.com/nuts-foundation/nuts-node/mock" "github.com/nuts-foundation/nuts-node/vcr/issuer" @@ -83,7 +82,7 @@ func TestWrapper_handleUserLanding(t *testing.T) { serverMetadata := oauth.AuthorizationServerMetadata{ AuthorizationEndpoint: "https://example.com/authorize", - ClientIdSchemesSupported: []string{didScheme}, + ClientIdSchemesSupported: []string{didClientIDScheme}, VPFormats: oauth.DefaultOpenIDSupportedFormats(), RequireSignedRequestObject: true, } diff --git a/auth/client/iam/openid4vp.go b/auth/client/iam/openid4vp.go index 2b18338398..c6daff650a 100644 --- a/auth/client/iam/openid4vp.go +++ b/auth/client/iam/openid4vp.go @@ -176,7 +176,7 @@ func (c *OpenID4VPClient) AccessToken(ctx context.Context, code string, verifier data := url.Values{} data.Set(oauth.ClientIDParam, clientID.String()) data.Set(oauth.GrantTypeParam, oauth.AuthorizationCodeGrantType) - data.Set(oauth.CodeParam, code) + data.Set(oauth.CodeResponseType, code) data.Set(oauth.RedirectURIParam, callbackURI) data.Set(oauth.CodeVerifierParam, codeVerifier) diff --git a/auth/oauth/types.go b/auth/oauth/types.go index ab4adf2d7b..cb128cc108 100644 --- a/auth/oauth/types.go +++ b/auth/oauth/types.go @@ -109,6 +109,7 @@ const ( AccessTokenRequestStatusActive = "active" ) +// metadata endpoints const ( // AuthzServerWellKnown is the well-known base path for the oauth authorization server metadata as defined in RFC8414 AuthzServerWellKnown = "/.well-known/oauth-authorization-server" @@ -118,50 +119,80 @@ const ( // OpenID4VCI specification OpenIdCredIssuerWellKnown = "/.well-known/openid-credential-issuer" OpenIdConfigurationWellKnown = "/.well-known/openid-configuration" - // AssertionParam is the parameter name for the assertion parameter +) + +// oauth parameter keys +const ( + // AssertionParam is the parameter name for the assertion parameter. (RFC021) AssertionParam = "assertion" - // AuthorizationCodeGrantType is the grant_type for the authorization_code grant type - AuthorizationCodeGrantType = "authorization_code" - // ClientIDParam is the parameter name for the client_id parameter + // AuthorizationDetailsParam is the parameter name for the authorization_details parameter. (RFC9396) + AuthorizationDetailsParam = "authorization_details" + // ClientIDParam is the parameter name for the client_id parameter. (RFC6749) ClientIDParam = "client_id" - // CodeChallengeParam is the parameter name for the code_challenge parameter + // ClientIDSchemeParam is the parameter name for the client_id_scheme parameter. (OpenID4VP) + ClientIDSchemeParam = "client_id_scheme" + // ClientMetadataParam is the parameter name for the client_metadata parameter. (OpenID4VP) + ClientMetadataParam = "client_metadata" + // ClientMetadataURIParam is the parameter name for the client_metadata_uri parameter. (OpenID4VP) + ClientMetadataURIParam = "client_metadata_uri" + // CNonceParam is the parameter name for the c_nonce parameter. (OpenID4VCI) + CNonceParam = "c_nonce" + // CodeChallengeParam is the parameter name for the code_challenge parameter. (RFC7636) CodeChallengeParam = "code_challenge" - // CodeChallengeMethodParam is the parameter name for the code_challenge_method parameter + // CodeChallengeMethodParam is the parameter name for the code_challenge_method parameter. (RFC7636) CodeChallengeMethodParam = "code_challenge_method" - // CodeVerifierParam is the parameter name for the code_verifier parameter + // CodeVerifierParam is the parameter name for the code_verifier parameter. (RFC7636) CodeVerifierParam = "code_verifier" - // CodeParam is the parameter name for the code parameter - CodeParam = "code" - // GrantTypeParam is the parameter name for the grant_type parameter + // GrantTypeParam is the parameter name for the grant_type parameter. (RFC6749) GrantTypeParam = "grant_type" // NonceParam is the parameter name for the nonce parameter NonceParam = "nonce" - // RedirectURIParam is the parameter name for the redirect_uri parameter + // PresentationDefParam is the parameter name for the OpenID4VP presentation_definition parameter. (OpenID4VP) + PresentationDefParam = "presentation_definition" + // PresentationDefUriParam is the parameter name for the OpenID4VP presentation_definition_uri parameter. (OpenID4VP) + PresentationDefUriParam = "presentation_definition_uri" + // PresentationSubmissionParam is the parameter name for the presentation_submission parameter. (OpenID4VP) + PresentationSubmissionParam = "presentation_submission" + // RedirectURIParam is the parameter name for the redirect_uri parameter. (RFC6749) RedirectURIParam = "redirect_uri" - // RequestParam is the parameter name for the request parameter. Defined in RFC9101 + // RequestParam is the parameter name for the request parameter. (RFC9101) RequestParam = "request" - // RequestURIParam is the parameter name for the request parameter. Defined in RFC9101 + // RequestURIParam is the parameter name for the request parameter. (RFC9101) RequestURIParam = "request_uri" - // RequestURIMethodParam states what http method (get/post) should be used for RequestURIParam. Defined in OpenID4VP + // RequestURIMethodParam states what http method (get/post) should be used for RequestURIParam. (OpenID4VP) RequestURIMethodParam = "request_uri_method" - // ResponseTypeParam is the parameter name for the response_type parameter + // ResponseModeParam is the parameter name for the OAuth2 response_mode parameter. + ResponseModeParam = "response_mode" + // ResponseTypeParam is the parameter name for the response_type parameter. (RFC6749) ResponseTypeParam = "response_type" - // ScopeParam is the parameter name for the scope parameter + // ResponseURIParam is the parameter name for the OpenID4VP response_uri parameter. + ResponseURIParam = "response_uri" + // ScopeParam is the parameter name for the scope parameter. (RFC6749) ScopeParam = "scope" - // StateParam is the parameter name for the state parameter + // StateParam is the parameter name for the state parameter. (RFC6749) StateParam = "state" - // PresentationSubmissionParam is the parameter name for the presentation_submission parameter - PresentationSubmissionParam = "presentation_submission" - // VpTokenParam is the parameter name for the vp_token parameter + // VpTokenParam is the parameter name for the vp_token parameter. (OpenID4VP) VpTokenParam = "vp_token" // WalletMetadataParam is used by the wallet to provide its metadata in an authorization request when RequestURIMethodParam is 'post' WalletMetadataParam = "wallet_metadata" // WalletNonceParam is a wallet generated nonce to prevent authorization request replay when RequestURIMethodParam is 'post' WalletNonceParam = "wallet_nonce" - // VpTokenGrantType is the grant_type for the vp_token-bearer grant type +) + +// grant types +const ( + // AuthorizationCodeGrantType is the grant_type for the authorization_code grant type. (RFC6749) + AuthorizationCodeGrantType = "authorization_code" + // VpTokenGrantType is the grant_type for the vp_token-bearer grant type. (RFC021) VpTokenGrantType = "vp_token-bearer" - // CNonceParam is the parameter name for the c_nonce parameter. Defined in OpenID4VCI. - CNonceParam = "c_nonce" +) + +// response types +const ( + // CodeResponseType is the parameter name for the code parameter. (RFC6749) + CodeResponseType = "code" + // VPTokenResponseType is paramter name for the vp_token repsponse type. (OpenID4VP) + VPTokenResponseType = "vp_token" ) const (