Repository: goadesign/goa (go)

Issue:
Incorrect request header when retrieving JWT token
Using the latest V3 commit

[V3: Fix generated endpoints when using SkipRequestBodyEncodeDecode wi…](https://github.com/goadesign/goa/commit/8ecad6641506a90dcb4ccdc15c56e050f1cf6f3c), with the DSL

```go
var _ = Service("JwtTest", func() {
	Description("Test service with JWT")

	Method("Test", func() {
		Security(JWTAuth)
		Payload(func() {
			TokenField(1, "token", String, "JWT token", func() {
				Example("1abc3457f129810cd12e1297a0cb")
			})
		})
		HTTP(func() {
			GET("/")
			Response(StatusOK)
		})
	})
})
```
goa gen generated the following output in gen/http/jwt_test/server/encode_decode.go

```go
func DecodeTestRequest(mux goahttp.Muxer, decoder func(*http.Request) goahttp.Decoder) func(*http.Request) (interface{}, error) {
	return func(r *http.Request) (interface{}, error) {
		var (
			token *string
		)
		tokenRaw := r.Header.Get("token")
		if tokenRaw != "" {
			token = &tokenRaw
		}
		payload := NewTestPayload(token)
		if payload.Token != nil {
			if strings.Contains(*payload.Token, " ") {
				// Remove authorization scheme prefix (e.g. "Bearer")
				cred := strings.SplitN(*payload.Token, " ", 2)[1]
				payload.Token = &cred
			}
		}

		return payload, nil
	}
}
```
JWT specifies token in the "Authorization" request header, however the generated code get token from "token" request header

Below is the example request
```
GET /resource HTTP/1.1
Host: example.com
Authorization: Bearer eyJh...
```

Pull request:
Handle implicit Authorization header properly
Fix issue where the endpoint finalizer was setting the endpoint headers
prior to looking for non-mapped payload attributes corrsponding to
Authorization header. The code now tests and
initializes at the same time. Added a test to cover this case.

Fix #2537 

Patch:
diff --git a/expr/http_endpoint.go b/expr/http_endpoint.go
index 52834bd0..b5167cb0 100644
--- a/expr/http_endpoint.go
+++ b/expr/http_endpoint.go
@@ -575,22 +575,6 @@ func (e *HTTPEndpointExpr) Validate() error {
 // types so that the response encoding code can properly use the type to infer
 // the response that it needs to build.
 func (e *HTTPEndpointExpr) Finalize() {
-	// Initialize Authorization header implicitly defined via security DSL
-	// prior to computing headers and body.
-	for att, mapped := range defaultRequestHeaderAttributes(e) {
-		if !mapped {
-			continue
-		}
-		attr := e.MethodExpr.Payload.Find(att)
-		e.Headers.Type.(*Object).Set(att, attr)
-		if e.MethodExpr.Payload.IsRequired(att) {
-			if e.Headers.Validation == nil {
-				e.Headers.Validation = &ValidationExpr{}
-			}
-			e.Headers.Validation.AddRequired(att)
-		}
-	}
-
 	// Compute security scheme attribute name and corresponding HTTP location
 	if reqLen := len(e.MethodExpr.Requirements); reqLen > 0 {
 		e.Requirements = make([]*SecurityExpr, 0, reqLen)
@@ -614,8 +598,18 @@ func (e *HTTPEndpointExpr) Finalize() {
 				}
 				sch.Name, sch.In = findKey(e, field)
 				if sch.Name == "" {
+					// Initialize Authorization header implicitly defined via
+					// security DSL if mapping isn't explicit.
 					sch.Name = "Authorization"
-					e.Headers.Map("Authorization", field)
+					attr := e.MethodExpr.Payload.Find(field)
+					e.Headers.Type.(*Object).Set(field, attr)
+					e.Headers.Map(sch.Name, field)
+					if e.MethodExpr.Payload.IsRequired(field) {
+						if e.Headers.Validation == nil {
+							e.Headers.Validation = &ValidationExpr{}
+						}
+						e.Headers.Validation.AddRequired(field)
+					}
 				}
 			}
 			e.Requirements = append(e.Requirements, dupReq)


Test patch:
diff --git a/expr/http_endpoint_test.go b/expr/http_endpoint_test.go
index cbd2ddbe..21b32037 100644
--- a/expr/http_endpoint_test.go
+++ b/expr/http_endpoint_test.go
@@ -222,17 +222,17 @@ func TestHTTPEndpointFinalization(t *testing.T) {
 
 			if tc.ExpectedBody != nil {
 				if e.Body == nil {
-					t.Errorf("%s: got endpoint without body, expected endpoint with body", name)
+					t.Errorf("got endpoint without body, expected endpoint with body")
 					return
 				}
 				bodyObj := *expr.AsObject(e.Body.Type)
 				expectedBodyObj := *expr.AsObject(tc.ExpectedBody)
 				if len(bodyObj) != len(expectedBodyObj) {
-					t.Errorf("%s: got %d, expected %d attribute(s) in endpoint body", name, len(bodyObj), len(expectedBodyObj))
+					t.Errorf("got %d, expected %d attribute(s) in endpoint body", len(bodyObj), len(expectedBodyObj))
 				} else {
 					for i := 0; i < len(expectedBodyObj); i++ {
 						if bodyObj[i].Name != expectedBodyObj[i].Name {
-							t.Errorf("%s: got %q, expected %q attribute in endpoint body", name, bodyObj[i].Name, expectedBodyObj[i].Name)
+							t.Errorf("got %q, expected %q attribute in endpoint body", bodyObj[i].Name, expectedBodyObj[i].Name)
 						}
 					}
 				}
@@ -240,3 +240,38 @@ func TestHTTPEndpointFinalization(t *testing.T) {
 		})
 	}
 }
+
+func TestHTTPAuthorizationMapping(t *testing.T) {
+	cases := []struct {
+		Name           string
+		DSL            func()
+		ExpectedHeader string
+	}{{
+		Name:           "explicit",
+		DSL:            testdata.ExplicitAuthHeaderDSL,
+		ExpectedHeader: "token",
+	}, {
+		Name:           "implicit",
+		DSL:            testdata.ImplicitAuthHeaderDSL,
+		ExpectedHeader: "Authorization",
+	},
+	}
+	for _, tc := range cases {
+		t.Run(tc.Name, func(t *testing.T) {
+			root := expr.RunDSL(t, tc.DSL)
+			e := root.API.HTTP.Services[0].HTTPEndpoints[0]
+			if e.Headers == nil {
+				t.Errorf("got endpoint without header, expected endpoint with HTTP header")
+				return
+			}
+			if len(*expr.AsObject(e.Headers.Type)) != 1 {
+				t.Errorf("got %d, expected 1 attribute in endpoint headers", len(*expr.AsObject(e.Headers.Type)))
+				return
+			}
+			n := e.Headers.ElemName("token")
+			if n != tc.ExpectedHeader {
+				t.Errorf("got %q, expected %q attribute in endpoint headers", n, tc.ExpectedHeader)
+			}
+		})
+	}
+}
diff --git a/expr/project_test.go b/expr/project_test.go
index 2afbfa5d..549547c4 100644
--- a/expr/project_test.go
+++ b/expr/project_test.go
@@ -32,7 +32,7 @@ var (
 
 func init() {
 	vobj := (*collectionLinkView)[0]
-	vobj.Attribute.Meta = map[string][]string{"view": []string{"link"}}
+	vobj.Attribute.Meta = map[string][]string{"view": {"link"}}
 }
 
 func TestProject(t *testing.T) {
diff --git a/expr/testdata/endpoint_dsls.go b/expr/testdata/endpoint_dsls.go
index 5a149855..a9d55713 100644
--- a/expr/testdata/endpoint_dsls.go
+++ b/expr/testdata/endpoint_dsls.go
@@ -476,6 +476,39 @@ var FinalizeEndpointBodyAsPropWithExtendedTypeDSL = func() {
 	})
 }
 
+var ExplicitAuthHeaderDSL = func() {
+	var OAuth2 = OAuth2Security("authCode")
+	Service("Service", func() {
+		Method("Method", func() {
+			Security(OAuth2)
+			Payload(func() {
+				AccessToken("token", String)
+				Attribute("payload", String)
+			})
+			HTTP(func() {
+				POST("/")
+				Header("token")
+			})
+		})
+	})
+}
+
+var ImplicitAuthHeaderDSL = func() {
+	var OAuth2 = OAuth2Security("authCode")
+	Service("Service", func() {
+		Method("Method", func() {
+			Security(OAuth2)
+			Payload(func() {
+				AccessToken("token", String)
+				Attribute("payload", String)
+			})
+			HTTP(func() {
+				POST("/")
+			})
+		})
+	})
+}
+
 var GRPCEndpointWithAnyType = func() {
 	var Recursive = Type("Recursive", func() {
 		Field(1, "invalid_map_key", MapOf(Any, "Recursive"))