diff --git a/internal/auth/auth.go b/internal/auth/auth.go index 858172123e..6928a7a73b 100644 --- a/internal/auth/auth.go +++ b/internal/auth/auth.go @@ -25,12 +25,6 @@ import ( "google.golang.org/protobuf/proto" ) -const ( - HeaderAuthMethod = "Authorization" - HttpOnlyCookieName = "wt-http-token-cookie" - JsVisibleCookieName = "wt-js-token-cookie" -) - type TokenFormat int const ( @@ -554,10 +548,10 @@ func GetTokenFromRequest(logger hclog.Logger, kmsCache *kms.Kms, req *http.Reque if receivedTokenType != AuthTokenTypeBearer { var httpCookiePayload string var jsCookiePayload string - if hc, err := req.Cookie(HttpOnlyCookieName); err == nil { + if hc, err := req.Cookie(handlers.HttpOnlyCookieName); err == nil { httpCookiePayload = hc.Value } - if jc, err := req.Cookie(JsVisibleCookieName); err == nil { + if jc, err := req.Cookie(handlers.JsVisibleCookieName); err == nil { jsCookiePayload = jc.Value } if httpCookiePayload != "" && jsCookiePayload != "" { diff --git a/internal/auth/auth_test.go b/internal/auth/auth_test.go index aa5c1ccff9..cb6d942064 100644 --- a/internal/auth/auth_test.go +++ b/internal/auth/auth_test.go @@ -12,6 +12,7 @@ import ( "github.com/hashicorp/boundary/internal/iam" "github.com/hashicorp/boundary/internal/kms" "github.com/hashicorp/boundary/internal/servers" + "github.com/hashicorp/boundary/internal/servers/controller/handlers" "github.com/hashicorp/boundary/internal/types/action" "github.com/hashicorp/boundary/internal/types/resource" "github.com/hashicorp/go-hclog" @@ -317,8 +318,8 @@ func TestAuthTokenAuthenticator(t *testing.T) { { name: "Split cookie token", cookies: []http.Cookie{ - {Name: HttpOnlyCookieName, Value: httpCookieVal}, - {Name: JsVisibleCookieName, Value: jsCookieVal}, + {Name: handlers.HttpOnlyCookieName, Value: httpCookieVal}, + {Name: handlers.JsVisibleCookieName, Value: jsCookieVal}, }, userId: at.GetIamUserId(), tokenFormat: AuthTokenTypeSplitCookie, @@ -326,14 +327,14 @@ func TestAuthTokenAuthenticator(t *testing.T) { { name: "Split cookie token only http cookie", cookies: []http.Cookie{ - {Name: HttpOnlyCookieName, Value: httpCookieVal}, + {Name: handlers.HttpOnlyCookieName, Value: httpCookieVal}, }, tokenFormat: AuthTokenTypeUnknown, }, { name: "Split cookie token only js cookie", cookies: []http.Cookie{ - {Name: JsVisibleCookieName, Value: jsCookieVal}, + {Name: handlers.JsVisibleCookieName, Value: jsCookieVal}, }, tokenFormat: AuthTokenTypeUnknown, }, @@ -341,8 +342,8 @@ func TestAuthTokenAuthenticator(t *testing.T) { name: "Cookie and auth header", headers: map[string]string{"Authorization": fmt.Sprintf("Bearer %s", tokValue)}, cookies: []http.Cookie{ - {Name: HttpOnlyCookieName, Value: httpCookieVal}, - {Name: JsVisibleCookieName, Value: jsCookieVal}, + {Name: handlers.HttpOnlyCookieName, Value: httpCookieVal}, + {Name: handlers.JsVisibleCookieName, Value: jsCookieVal}, }, userId: at.GetIamUserId(), tokenFormat: AuthTokenTypeBearer, diff --git a/internal/gen/controller.swagger.json b/internal/gen/controller.swagger.json index 68f383b60d..88367d5b65 100644 --- a/internal/gen/controller.swagger.json +++ b/internal/gen/controller.swagger.json @@ -2814,7 +2814,7 @@ }, "token_type": { "type": "string", - "description": "This can be \"cookie\" or \"token\". If not provided, \"token\" will be used. For now only type \"token\" is returned." + "description": "This can be \"cookie\" or \"token\". If not provided, \"token\" will be used." }, "credentials": { "type": "object", @@ -2827,6 +2827,10 @@ "properties": { "item": { "$ref": "#/definitions/controller.api.resources.authtokens.v1.AuthToken" + }, + "token_type": { + "type": "string", + "description": "This can be \"cookie\" or \"token\". If not present, \"token\" should be assumed." } } }, diff --git a/internal/gen/controller/api/services/authenticate_service.pb.go b/internal/gen/controller/api/services/authenticate_service.pb.go index cb8ed2fbe8..af62e00b5f 100644 --- a/internal/gen/controller/api/services/authenticate_service.pb.go +++ b/internal/gen/controller/api/services/authenticate_service.pb.go @@ -98,7 +98,7 @@ type AuthenticateRequest struct { // The id to the authmethod in the system being used for authentication. The auth method must be in the scope // being logged in to. AuthMethodId string `protobuf:"bytes,1,opt,name=auth_method_id,json=authMethodId,proto3" json:"auth_method_id,omitempty"` - // This can be "cookie" or "token". If not provided, "token" will be used. For now only type "token" is returned. + // This can be "cookie" or "token". If not provided, "token" will be used. TokenType string `protobuf:"bytes,2,opt,name=token_type,json=tokenType,proto3" json:"token_type,omitempty"` // credentials are the different possible credential names depending on what type of auth method is used. // For password auth method: should include only "name" and "password". @@ -164,6 +164,8 @@ type AuthenticateResponse struct { unknownFields protoimpl.UnknownFields Item *authtokens.AuthToken `protobuf:"bytes,1,opt,name=item,proto3" json:"item,omitempty"` + // This can be "cookie" or "token". If not present, "token" should be assumed. + TokenType string `protobuf:"bytes,2,opt,name=token_type,json=tokenType,proto3" json:"token_type,omitempty"` } func (x *AuthenticateResponse) Reset() { @@ -205,6 +207,13 @@ func (x *AuthenticateResponse) GetItem() *authtokens.AuthToken { return nil } +func (x *AuthenticateResponse) GetTokenType() string { + if x != nil { + return x.TokenType + } + return "" +} + type DeauthenticateRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -314,39 +323,41 @@ var file_controller_api_services_v1_authenticate_service_proto_rawDesc = []byte{ 0x74, 0x69, 0x61, 0x6c, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, 0x0b, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, - 0x73, 0x22, 0x5d, 0x0a, 0x14, 0x41, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, + 0x73, 0x22, 0x7c, 0x0a, 0x14, 0x41, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x45, 0x0a, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x31, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x61, 0x75, 0x74, 0x68, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x75, 0x74, 0x68, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x52, 0x04, 0x69, 0x74, 0x65, 0x6d, - 0x22, 0x17, 0x0a, 0x15, 0x44, 0x65, 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, - 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x18, 0x0a, 0x16, 0x44, 0x65, 0x61, - 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x32, 0xa0, 0x02, 0x0a, 0x15, 0x41, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, - 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x86, 0x02, - 0x0a, 0x0c, 0x41, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x65, 0x12, 0x2f, + 0x12, 0x1d, 0x0a, 0x0a, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x22, + 0x17, 0x0a, 0x15, 0x44, 0x65, 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, + 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x18, 0x0a, 0x16, 0x44, 0x65, 0x61, 0x75, + 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x32, 0xa0, 0x02, 0x0a, 0x15, 0x41, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x86, 0x02, 0x0a, + 0x0c, 0x41, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x65, 0x12, 0x2f, 0x2e, + 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, + 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x75, 0x74, 0x68, 0x65, + 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x30, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x75, 0x74, 0x68, - 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x30, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, - 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x75, 0x74, - 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x22, 0x92, 0x01, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x42, 0x22, 0x37, 0x2f, 0x76, 0x31, 0x2f, - 0x73, 0x63, 0x6f, 0x70, 0x65, 0x73, 0x2f, 0x2a, 0x2f, 0x61, 0x75, 0x74, 0x68, 0x2d, 0x6d, 0x65, - 0x74, 0x68, 0x6f, 0x64, 0x73, 0x2f, 0x7b, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x6d, 0x65, 0x74, 0x68, - 0x6f, 0x64, 0x5f, 0x69, 0x64, 0x7d, 0x3a, 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, - 0x61, 0x74, 0x65, 0x3a, 0x01, 0x2a, 0x62, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x92, 0x41, 0x47, 0x12, - 0x45, 0x41, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x65, 0x20, 0x61, 0x20, - 0x75, 0x73, 0x65, 0x72, 0x20, 0x74, 0x6f, 0x20, 0x61, 0x6e, 0x20, 0x73, 0x63, 0x6f, 0x70, 0x65, - 0x20, 0x61, 0x6e, 0x64, 0x20, 0x72, 0x65, 0x74, 0x72, 0x69, 0x65, 0x76, 0x65, 0x20, 0x61, 0x6e, - 0x20, 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, - 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x2e, 0x42, 0x4d, 0x5a, 0x4b, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, - 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x62, - 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, - 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2f, - 0x61, 0x70, 0x69, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x3b, 0x73, 0x65, 0x72, - 0x76, 0x69, 0x63, 0x65, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x22, 0x92, 0x01, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x42, 0x22, 0x37, 0x2f, 0x76, 0x31, 0x2f, 0x73, + 0x63, 0x6f, 0x70, 0x65, 0x73, 0x2f, 0x2a, 0x2f, 0x61, 0x75, 0x74, 0x68, 0x2d, 0x6d, 0x65, 0x74, + 0x68, 0x6f, 0x64, 0x73, 0x2f, 0x7b, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x6d, 0x65, 0x74, 0x68, 0x6f, + 0x64, 0x5f, 0x69, 0x64, 0x7d, 0x3a, 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, + 0x74, 0x65, 0x3a, 0x01, 0x2a, 0x62, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x92, 0x41, 0x47, 0x12, 0x45, + 0x41, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x65, 0x20, 0x61, 0x20, 0x75, + 0x73, 0x65, 0x72, 0x20, 0x74, 0x6f, 0x20, 0x61, 0x6e, 0x20, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x20, + 0x61, 0x6e, 0x64, 0x20, 0x72, 0x65, 0x74, 0x72, 0x69, 0x65, 0x76, 0x65, 0x20, 0x61, 0x6e, 0x20, + 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x74, + 0x6f, 0x6b, 0x65, 0x6e, 0x2e, 0x42, 0x4d, 0x5a, 0x4b, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, + 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x62, 0x6f, + 0x75, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, + 0x67, 0x65, 0x6e, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2f, 0x61, + 0x70, 0x69, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x3b, 0x73, 0x65, 0x72, 0x76, + 0x69, 0x63, 0x65, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/internal/proto/local/controller/api/services/v1/authenticate_service.proto b/internal/proto/local/controller/api/services/v1/authenticate_service.proto index 4b8f293c16..46d54687ee 100644 --- a/internal/proto/local/controller/api/services/v1/authenticate_service.proto +++ b/internal/proto/local/controller/api/services/v1/authenticate_service.proto @@ -48,7 +48,7 @@ message AuthenticateRequest { // The id to the authmethod in the system being used for authentication. The auth method must be in the scope // being logged in to. string auth_method_id = 1; - // This can be "cookie" or "token". If not provided, "token" will be used. For now only type "token" is returned. + // This can be "cookie" or "token". If not provided, "token" will be used. string token_type = 2; // credentials are the different possible credential names depending on what type of auth method is used. @@ -58,6 +58,8 @@ message AuthenticateRequest { message AuthenticateResponse { resources.authtokens.v1.AuthToken item = 1; + // This can be "cookie" or "token". If not present, "token" should be assumed. + string token_type = 2; } message DeauthenticateRequest {} diff --git a/internal/servers/controller/handler.go b/internal/servers/controller/handler.go index 7708022c2b..95394c70dd 100644 --- a/internal/servers/controller/handler.go +++ b/internal/servers/controller/handler.go @@ -77,6 +77,7 @@ func handleGrpcGateway(c *Controller, props HandlerProperties) (http.Handler, er }, }), runtime.WithErrorHandler(handlers.ErrorHandler(c.logger)), + runtime.WithForwardResponseOption(handlers.OutgoingIntercepter), ) hcs, err := host_catalogs.NewService(c.StaticHostRepoFn) if err != nil { diff --git a/internal/servers/controller/handler_test.go b/internal/servers/controller/handler_test.go index 0da41caeed..5df88fcf59 100644 --- a/internal/servers/controller/handler_test.go +++ b/internal/servers/controller/handler_test.go @@ -1,6 +1,7 @@ package controller import ( + "bytes" "encoding/json" "fmt" "io/ioutil" @@ -8,6 +9,7 @@ import ( "strings" "testing" + "github.com/hashicorp/boundary/internal/servers/controller/handlers" "github.com/hashicorp/boundary/internal/types/scope" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -21,12 +23,23 @@ func TestAuthenticationHandler(t *testing.T) { }) defer c.Shutdown() - resp, err := http.Post(fmt.Sprintf("%s/v1/scopes/%s/auth-methods/ampw_1234567890:authenticate", c.ApiAddrs()[0], scope.Global.String()), "application/json", - strings.NewReader(`{"token_type": null, "credentials": {"login_name":"admin", "password": "password123"}}`)) + request := map[string]interface{}{ + "credentials": map[string]interface{}{ + "login_name": "admin", + "password": "password123", + }, + } + // No token_type defined means "token" type + b, err := json.Marshal(request) + require.NoError(t, err) + + resp, err := http.Post(fmt.Sprintf("%s/v1/scopes/%s/auth-methods/ampw_1234567890:authenticate", c.ApiAddrs()[0], + scope.Global.String()), "application/json", bytes.NewReader(b)) + require.NoError(t, err) assert.Equal(t, http.StatusOK, resp.StatusCode, "Got response: %v", resp) - b, err := ioutil.ReadAll(resp.Body) + b, err = ioutil.ReadAll(resp.Body) require.NoError(t, err) body := make(map[string]interface{}) require.NoError(t, json.Unmarshal(b, &body)) @@ -37,6 +50,41 @@ func TestAuthenticationHandler(t *testing.T) { assert.NotEmpty(t, pubId) assert.NotEmpty(t, tok) assert.Truef(t, strings.HasPrefix(tok, pubId), "Token: %q, Id: %q", tok, pubId) + + // Set the token type to cookie and make sure the body does not contain the token anymore. + request["token_type"] = "cookie" + b, err = json.Marshal(request) + resp, err = http.Post(fmt.Sprintf("%s/v1/scopes/%s/auth-methods/ampw_1234567890:authenticate", c.ApiAddrs()[0], + scope.Global.String()), "application/json", bytes.NewReader(b)) + + require.NoError(t, err) + assert.Equal(t, http.StatusOK, resp.StatusCode, "Got response: %v", resp) + + b, err = ioutil.ReadAll(resp.Body) + require.NoError(t, err) + body = make(map[string]interface{}) + require.NoError(t, json.Unmarshal(b, &body)) + + require.Contains(t, body, "id") + require.Contains(t, body, "auth_method_id") + require.Contains(t, body, "user_id") + require.NotContains(t, body, "token") + + cookies := make(map[string]*http.Cookie) + for _, c := range resp.Cookies() { + cookies[c.Name] = c + } + require.Contains(t, cookies, handlers.HttpOnlyCookieName) + require.Contains(t, cookies, handlers.JsVisibleCookieName) + assert.NotEmpty(t, cookies[handlers.HttpOnlyCookieName].Value) + assert.NotEmpty(t, cookies[handlers.JsVisibleCookieName].Value) + assert.True(t, cookies[handlers.HttpOnlyCookieName].HttpOnly) + assert.False(t, cookies[handlers.JsVisibleCookieName].HttpOnly) + tok = cookies[handlers.JsVisibleCookieName].Value + + pubId = body["id"].(string) + assert.NotEmpty(t, pubId) + assert.Truef(t, strings.HasPrefix(tok, pubId), "Token: %q, Id: %q", tok, pubId) } func TestHandleImplementedPaths(t *testing.T) { diff --git a/internal/servers/controller/handlers/authenticate/authenticate_service.go b/internal/servers/controller/handlers/authenticate/authenticate_service.go index 95a564115e..5ea8ac3169 100644 --- a/internal/servers/controller/handlers/authenticate/authenticate_service.go +++ b/internal/servers/controller/handlers/authenticate/authenticate_service.go @@ -70,7 +70,7 @@ func (s Service) Authenticate(ctx context.Context, req *pbs.AuthenticateRequest) if err != nil { return nil, err } - return &pbs.AuthenticateResponse{Item: tok}, nil + return &pbs.AuthenticateResponse{Item: tok, TokenType: req.GetTokenType()}, nil } // Deauthenticate implements the interface pbs.AuthenticationServiceServer. @@ -171,7 +171,7 @@ func validateAuthenticateRequest(req *pbs.AuthenticateRequest) error { } // TODO: Update this when we enable split cookie token types. tType := strings.ToLower(strings.TrimSpace(req.GetTokenType())) - if tType != "" && tType != "token" { + if tType != "" && tType != "token" && tType != "cookie" { badFields["token_type"] = "The only accepted type is 'token'." } if len(badFields) > 0 { diff --git a/internal/servers/controller/handlers/host_sets/host_set_service.go b/internal/servers/controller/handlers/host_sets/host_set_service.go index ffbaef2431..38be6dcf6d 100644 --- a/internal/servers/controller/handlers/host_sets/host_set_service.go +++ b/internal/servers/controller/handlers/host_sets/host_set_service.go @@ -23,7 +23,7 @@ var ( func init() { var err error - if maskManager, err = handlers.NewMaskManager(&pb.HostSet{}, &store.HostSet{}); err != nil { + if maskManager, err = handlers.NewMaskManager(&store.HostSet{}, &pb.HostSet{}); err != nil { panic(err) } } diff --git a/internal/servers/controller/handlers/outgoing_interceptor.go b/internal/servers/controller/handlers/outgoing_interceptor.go new file mode 100644 index 0000000000..50d3d9c034 --- /dev/null +++ b/internal/servers/controller/handlers/outgoing_interceptor.go @@ -0,0 +1,40 @@ +package handlers + +import ( + "context" + "net/http" + "strings" + + pbs "github.com/hashicorp/boundary/internal/gen/controller/api/services" + "google.golang.org/protobuf/proto" +) + +const ( + HttpOnlyCookieName = "wt-http-token-cookie" + JsVisibleCookieName = "wt-js-token-cookie" +) + +func OutgoingInterceptor(ctx context.Context, w http.ResponseWriter, m proto.Message) error { + m = m.ProtoReflect().Interface() + switch m := m.(type) { + case *pbs.AuthenticateResponse: + if strings.EqualFold(m.GetTokenType(), "cookie") { + tok := m.GetItem().GetToken() + m.GetItem().Token = "" + half := len(tok) / 2 + jsTok := http.Cookie{ + Name: JsVisibleCookieName, + Value: tok[:half], + } + httpTok := http.Cookie{ + Name: HttpOnlyCookieName, + Value: tok[half:], + HttpOnly: true, + } + http.SetCookie(w, &jsTok) + http.SetCookie(w, &httpTok) + } + } + + return nil +} diff --git a/internal/servers/controller/handlers/outgoing_interceptor_test.go b/internal/servers/controller/handlers/outgoing_interceptor_test.go new file mode 100644 index 0000000000..1bfd85b429 --- /dev/null +++ b/internal/servers/controller/handlers/outgoing_interceptor_test.go @@ -0,0 +1,24 @@ +package handlers + +import ( + "context" + "net/http" + "net/http/httptest" + "testing" + + pb "github.com/hashicorp/boundary/internal/gen/controller/api/resources/authtokens" + pbs "github.com/hashicorp/boundary/internal/gen/controller/api/services" + "github.com/stretchr/testify/assert" +) + +func TestOutgoingSplitCookie(t *testing.T) { + rec := httptest.NewRecorder() + OutgoingIntercepter(context.Background(), rec, &pbs.AuthenticateResponse{ + TokenType: "cookie", + Item: &pb.AuthToken{Token: "t_abc_1234567890"}, + }) + assert.ElementsMatch(t, rec.Result().Cookies(), []*http.Cookie{ + {Name: HttpOnlyCookieName, Value: "34567890", HttpOnly: true, Raw: "wt-http-token-cookie=34567890; HttpOnly"}, + {Name: JsVisibleCookieName, Value: "t_abc_12", Raw: "wt-js-token-cookie=t_abc_12"}, + }) +}