Repository: prebid/prebid-server
Pull request #1114 (2019-11-14 22:08:08)

TripleliftNative Adapter


Patch:
diff --git a/.travis.yml b/.travis.yml
index 2ad1f6c7..3cc55e5f 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,7 +1,6 @@
 language: go
 
 go:
-  - '1.11.4'
   - '1.12'
   - '1.13'
 
diff --git a/README.md b/README.md
index 9ba58d89..a59bf5f6 100644
--- a/README.md
+++ b/README.md
@@ -18,7 +18,7 @@ For more information, see:
 
 ## Installation
 
-First install [Go 1.11](https://golang.org/doc/install) latest version (1.11.4) or later.
+First install [Go 1.12](https://golang.org/doc/install) latest version.
 Note that prebid-server is using [Go modules](https://blog.golang.org/using-go-modules).
 If using Go version <1.13 and are inside GOPATH `GO111MODULE` needs to be set to `GO111MODULE=on`.
 
diff --git a/adapters/triplelift_native/triplelift_native.go b/adapters/triplelift_native/triplelift_native.go
new file mode 100644
index 00000000..a2576032
--- /dev/null
+++ b/adapters/triplelift_native/triplelift_native.go
@@ -0,0 +1,202 @@
+package triplelift_native
+
+import (
+	"encoding/json"
+	"fmt"
+	"github.com/golang/glog"
+	"github.com/mxmCherry/openrtb"
+	"github.com/prebid/prebid-server/adapters"
+	"github.com/prebid/prebid-server/errortypes"
+	"github.com/prebid/prebid-server/openrtb_ext"
+	"net/http"
+)
+
+type TripleliftNativeAdapter struct {
+	endpoint string
+	extInfo  TripleliftNativeExtInfo
+}
+
+type TripleliftInnerExt struct {
+	Format int `json:"format"`
+}
+
+type TripleliftRespExt struct {
+	Triplelift TripleliftInnerExt `json:"triplelift_pb"`
+}
+
+type TripleliftNativeExtInfo struct {
+	// Array is used for deserialization.
+	PublisherWhitelist []string `json:"publisher_whitelist"`
+
+	Endpoint string `json:"endpoint"`
+
+	// Map is used for optimized memory access and should be constructed after deserialization.
+	PublisherWhitelistMap map[string]bool
+}
+
+func getBidType(ext TripleliftRespExt) openrtb_ext.BidType {
+	return openrtb_ext.BidTypeNative
+}
+
+func processImp(imp *openrtb.Imp) error {
+	// get the triplelift extension
+	var ext adapters.ExtImpBidder
+	var tlext openrtb_ext.ExtImpTriplelift
+	if err := json.Unmarshal(imp.Ext, &ext); err != nil {
+		return err
+	}
+	if err := json.Unmarshal(ext.Bidder, &tlext); err != nil {
+		return err
+	}
+	if imp.Native == nil {
+		return fmt.Errorf("no native object specified")
+	}
+	if tlext.InvCode == "" {
+		return fmt.Errorf("no inv_code specified")
+	}
+	imp.TagID = tlext.InvCode
+	// floor is optional
+	if tlext.Floor == nil {
+		return nil
+	}
+	imp.BidFloor = *tlext.Floor
+	// no error
+	return nil
+}
+
+// Returns the effective publisher ID
+func effectivePubID(pub *openrtb.Publisher) string {
+	if pub != nil {
+		if pub.Ext != nil {
+			var pubExt openrtb_ext.ExtPublisher
+			err := json.Unmarshal(pub.Ext, &pubExt)
+			if err == nil && pubExt.Prebid != nil && pubExt.Prebid.ParentAccount != nil && *pubExt.Prebid.ParentAccount != "" {
+				return *pubExt.Prebid.ParentAccount
+			}
+		}
+		if pub.ID != "" {
+			return pub.ID
+		}
+	}
+	return "unknown"
+}
+
+func (a *TripleliftNativeAdapter) MakeRequests(request *openrtb.BidRequest, extra *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) {
+	errs := make([]error, 0, len(request.Imp)+1)
+	reqs := make([]*adapters.RequestData, 0, 1)
+	// copy the request, because we are going to mutate it
+	tlRequest := *request
+	// this will contain all the valid impressions
+	var validImps []openrtb.Imp
+	// pre-process the imps
+	for _, imp := range tlRequest.Imp {
+		if err := processImp(&imp); err == nil {
+			validImps = append(validImps, imp)
+		} else {
+			errs = append(errs, err)
+		}
+	}
+	publisher := getPublisher(request)
+	publisherID := effectivePubID(publisher)
+	if _, exists := a.extInfo.PublisherWhitelistMap[publisherID]; !exists {
+		err := fmt.Errorf("Unsupported publisher for triplelift_native")
+		return nil, []error{err}
+	}
+	if len(validImps) == 0 {
+		err := fmt.Errorf("No valid impressions for triplelift")
+		errs = append(errs, err)
+		return nil, errs
+	}
+	tlRequest.Imp = validImps
+	reqJSON, err := json.Marshal(tlRequest)
+	if err != nil {
+		errs = append(errs, err)
+		return nil, errs
+	}
+	headers := http.Header{}
+	headers.Add("Content-Type", "application/json;charset=utf-8")
+	headers.Add("Accept", "application/json")
+	ad := a.endpoint
+	reqs = append(reqs, &adapters.RequestData{
+		Method:  "POST",
+		Uri:     ad,
+		Body:    reqJSON,
+		Headers: headers})
+	return reqs, errs
+}
+
+func getPublisher(request *openrtb.BidRequest) *openrtb.Publisher {
+	if request.App != nil {
+		return request.App.Publisher
+	}
+	return request.Site.Publisher
+}
+
+func getBidCount(bidResponse openrtb.BidResponse) int {
+	c := 0
+	for _, sb := range bidResponse.SeatBid {
+		c = c + len(sb.Bid)
+	}
+	return c
+}
+
+func (a *TripleliftNativeAdapter) MakeBids(internalRequest *openrtb.BidRequest, externalRequest *adapters.RequestData, response *adapters.ResponseData) (*adapters.BidderResponse, []error) {
+	if response.StatusCode == http.StatusNoContent {
+		return nil, nil
+	}
+
+	if response.StatusCode == http.StatusBadRequest {
+		return nil, []error{&errortypes.BadInput{
+			Message: fmt.Sprintf("Unexpected status code: %d. Run with request.debug = 1 for more info", response.StatusCode),
+		}}
+	}
+
+	if response.StatusCode != http.StatusOK {
+		return nil, []error{&errortypes.BadServerResponse{Message: fmt.Sprintf("Unexpected status code: %d. Run with request.debug = 1 for more info", response.StatusCode)}}
+	}
+	var bidResp openrtb.BidResponse
+	if err := json.Unmarshal(response.Body, &bidResp); err != nil {
+		return nil, []error{err}
+	}
+	var errs []error
+	count := getBidCount(bidResp)
+	bidResponse := adapters.NewBidderResponseWithBidsCapacity(count)
+
+	for _, sb := range bidResp.SeatBid {
+		for i := 0; i < len(sb.Bid); i++ {
+			bid := sb.Bid[i]
+			var bidExt TripleliftRespExt
+			bidType := getBidType(bidExt)
+			bidResponse.Bids = append(bidResponse.Bids, &adapters.TypedBid{
+				Bid:     &bid,
+				BidType: bidType,
+			})
+		}
+	}
+	return bidResponse, errs
+}
+
+func NewTripleliftNativeBidder(client *http.Client, endpoint string, extraInfo string) adapters.Bidder {
+	var extInfo TripleliftNativeExtInfo
+
+	if len(extraInfo) == 0 {
+		extraInfo = "{\"publisher_whitelist\":[]}"
+	}
+	if err := json.Unmarshal([]byte(extraInfo), &extInfo); err != nil {
+		glog.Errorf("Invalid TripleLife Native extra adapter info: " + err.Error())
+		return &adapters.MisconfiguredBidder{
+			Name:  "TripleliftNativeAdapter",
+			Error: fmt.Errorf("TripleliftNativeAdapter could not unmarshal config json"),
+		}
+	}
+
+	// Populate map for faster memory access
+	extInfo.PublisherWhitelistMap = make(map[string]bool)
+	for _, v := range extInfo.PublisherWhitelist {
+		extInfo.PublisherWhitelistMap[v] = true
+	}
+
+	return &TripleliftNativeAdapter{
+		extInfo:  extInfo,
+		endpoint: extInfo.Endpoint}
+}
diff --git a/adapters/triplelift_native/usersync.go b/adapters/triplelift_native/usersync.go
new file mode 100644
index 00000000..b7587b77
--- /dev/null
+++ b/adapters/triplelift_native/usersync.go
@@ -0,0 +1,12 @@
+package triplelift_native
+
+import (
+	"text/template"
+
+	"github.com/prebid/prebid-server/adapters"
+	"github.com/prebid/prebid-server/usersync"
+)
+
+func NewTripleliftSyncer(temp *template.Template) usersync.Usersyncer {
+	return adapters.NewSyncer("triplelift", 28, temp, adapters.SyncTypeRedirect)
+}
diff --git a/config/config.go b/config/config.go
index 64e16cbe..0c3d36df 100644
--- a/config/config.go
+++ b/config/config.go
@@ -506,11 +506,12 @@ func (cfg *Configuration) setDerivedDefaults() {
 	setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderSomoaudience, "https://publisher-east.mobileadtrading.com/usersync?ru="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dsomoaudience%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%24%7BUID%7D")
 	setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderSovrn, "https://ap.lijit.com/pixel?redir="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dsovrn%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%24UID")
 	setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderSonobi, "https://sync.go.sonobi.com/us.gif?loc="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dsonobi%26consent_string%3D{{.GDPR}}%26gdpr%3D{{.GDPRConsent}}%26uid%3D%5BUID%5D")
-	setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderSynacormedia, "http://sync.technoratimedia.com/services?srv=cs&pid=70&cb="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dsynacormedia%26uid%3D%5BUSER_ID%5D")
+	setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderSynacormedia, "https://sync.technoratimedia.com/services?srv=cs&pid=70&cb="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dsynacormedia%26uid%3D%5BUSER_ID%5D")
 	// openrtb_ext.BidderVrtcal doesn't have a good default.
 	setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderYieldmo, "https://ads.yieldmo.com/pbsync?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&redirectUri="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dyieldmo%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%24UID")
 	setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderGamoshi, "https://rtb.gamoshi.io/pix/0000/scm?gdpr={{.GDPR}}&consent={{.GDPRConsent}}&rurl="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dgamoshi%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%5Bgusr%5D")
 	setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderMgid, "https://cm.mgid.com/m?cdsp=363893&adu="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dmgid%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%7Bmuidn%7D")
+	setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderTripleliftNative, "https://eb2.3lift.com/sync?gpdr={{.GDPR}}&cmp_cs={{.GDPRConsent}}")
 	setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderTriplelift, "https://eb2.3lift.com/getuid?gpdr={{.GDPR}}&cmp_cs={{.GDPRConsent}}&redir="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dtriplelift%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%24UID")
 	setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderUnruly, "https://usermatch.targeting.unrulymedia.com/pbsync?gdpr={{.GDPR}}&consent={{.GDPRConsent}}&rurl="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dunruly%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%24UID")
 	setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderVisx, "https://t.visx.net/s2s_sync?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&redir="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dvisx%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%24%7BUUID%7D")
@@ -674,6 +675,8 @@ func SetupViper(v *viper.Viper, filename string) {
 	v.SetDefault("adapters.rtbhouse.endpoint", "http://prebidserver-s2s-ams.creativecdn.com/bidder/prebidserver/bids")
 	v.SetDefault("adapters.somoaudience.endpoint", "http://publisher-east.mobileadtrading.com/rtb/bid")
 	v.SetDefault("adapters.sovrn.endpoint", "http://ap.lijit.com/rtb/bid?src=prebid_server")
+	v.SetDefault("adapters.triplelift_native.disabled", true)
+	v.SetDefault("adapters.triplelift_native.extra_info", "{\"publisher_whitelist\":[]}")
 	v.SetDefault("adapters.synacormedia.endpoint", "http://{{.Host}}.technoratimedia.com/openrtb/bids/{{.Host}}")
 	v.SetDefault("adapters.triplelift.endpoint", "https://tlx.3lift.com/s2s/auction?supplier_id=20")
 	v.SetDefault("adapters.adkernel.endpoint", "http://{{.Host}}/hb?zone={{.ZoneID}}")
diff --git a/exchange/adapter_map.go b/exchange/adapter_map.go
index e38c0edf..395b4a5d 100644
--- a/exchange/adapter_map.go
+++ b/exchange/adapter_map.go
@@ -46,6 +46,7 @@ import (
 	"github.com/prebid/prebid-server/adapters/synacormedia"
 	"github.com/prebid/prebid-server/adapters/tappx"
 	"github.com/prebid/prebid-server/adapters/triplelift"
+	"github.com/prebid/prebid-server/adapters/triplelift_native"
 	"github.com/prebid/prebid-server/adapters/unruly"
 	"github.com/prebid/prebid-server/adapters/verizonmedia"
 	"github.com/prebid/prebid-server/adapters/visx"
@@ -87,25 +88,26 @@ func newAdapterMap(client *http.Client, cfg *config.Configuration, infos adapter
 			cfg.Adapters[string(openrtb_ext.BidderRubicon)].XAPI.Username,
 			cfg.Adapters[string(openrtb_ext.BidderRubicon)].XAPI.Password,
 			cfg.Adapters[string(openrtb_ext.BidderRubicon)].XAPI.Tracker),
-		openrtb_ext.BidderSharethrough:   sharethrough.NewSharethroughBidder(cfg.Adapters[string(openrtb_ext.BidderSharethrough)].Endpoint),
-		openrtb_ext.BidderSomoaudience:   somoaudience.NewSomoaudienceBidder(cfg.Adapters[string(openrtb_ext.BidderSomoaudience)].Endpoint),
-		openrtb_ext.BidderSovrn:          sovrn.NewSovrnBidder(client, cfg.Adapters[string(openrtb_ext.BidderSovrn)].Endpoint),
-		openrtb_ext.Bidder33Across:       ttx.New33AcrossBidder(cfg.Adapters[string(openrtb_ext.Bidder33Across)].Endpoint),
-		openrtb_ext.BidderGrid:           grid.NewGridBidder(cfg.Adapters[string(openrtb_ext.BidderGrid)].Endpoint),
-		openrtb_ext.BidderSonobi:         sonobi.NewSonobiBidder(client, cfg.Adapters[string(openrtb_ext.BidderSonobi)].Endpoint),
-		openrtb_ext.BidderSynacormedia:   synacormedia.NewSynacorMediaBidder(cfg.Adapters[string(openrtb_ext.BidderSynacormedia)].Endpoint),
-		openrtb_ext.BidderTriplelift:     triplelift.NewTripleliftBidder(client, cfg.Adapters[string(openrtb_ext.BidderTriplelift)].Endpoint),
-		openrtb_ext.BidderUnruly:         unruly.NewUnrulyBidder(client, cfg.Adapters[string(openrtb_ext.BidderUnruly)].Endpoint),
-		openrtb_ext.BidderVrtcal:         vrtcal.NewVrtcalBidder(cfg.Adapters[string(openrtb_ext.BidderVrtcal)].Endpoint),
-		openrtb_ext.BidderYieldmo:        yieldmo.NewYieldmoBidder(cfg.Adapters[string(openrtb_ext.BidderYieldmo)].Endpoint),
-		openrtb_ext.BidderGamma:          gamma.NewGammaBidder(cfg.Adapters[string(openrtb_ext.BidderGamma)].Endpoint),
-		openrtb_ext.BidderVisx:           visx.NewVisxBidder(cfg.Adapters[string(openrtb_ext.BidderVisx)].Endpoint),
-		openrtb_ext.BidderGamoshi:        gamoshi.NewGamoshiBidder(cfg.Adapters[string(openrtb_ext.BidderGamoshi)].Endpoint),
-		openrtb_ext.BidderMgid:           mgid.NewMgidBidder(cfg.Adapters[string(openrtb_ext.BidderMgid)].Endpoint),
-		openrtb_ext.BidderImprovedigital: improvedigital.NewImprovedigitalBidder(cfg.Adapters[string(openrtb_ext.BidderImprovedigital)].Endpoint),
-		openrtb_ext.BidderTappx:          tappx.NewTappxBidder(client, cfg.Adapters[strings.ToLower(string(openrtb_ext.BidderTappx))].Endpoint),
-		openrtb_ext.BidderVerizonMedia:   verizonmedia.NewVerizonMediaBidder(client, cfg.Adapters[string(openrtb_ext.BidderVerizonMedia)].Endpoint),
-		openrtb_ext.BidderKubient:        kubient.NewKubientBidder(cfg.Adapters[string(openrtb_ext.BidderKubient)].Endpoint),
+		openrtb_ext.BidderSharethrough:     sharethrough.NewSharethroughBidder(cfg.Adapters[string(openrtb_ext.BidderSharethrough)].Endpoint),
+		openrtb_ext.BidderSomoaudience:     somoaudience.NewSomoaudienceBidder(cfg.Adapters[string(openrtb_ext.BidderSomoaudience)].Endpoint),
+		openrtb_ext.BidderSovrn:            sovrn.NewSovrnBidder(client, cfg.Adapters[string(openrtb_ext.BidderSovrn)].Endpoint),
+		openrtb_ext.Bidder33Across:         ttx.New33AcrossBidder(cfg.Adapters[string(openrtb_ext.Bidder33Across)].Endpoint),
+		openrtb_ext.BidderGrid:             grid.NewGridBidder(cfg.Adapters[string(openrtb_ext.BidderGrid)].Endpoint),
+		openrtb_ext.BidderSonobi:           sonobi.NewSonobiBidder(client, cfg.Adapters[string(openrtb_ext.BidderSonobi)].Endpoint),
+		openrtb_ext.BidderSynacormedia:     synacormedia.NewSynacorMediaBidder(cfg.Adapters[string(openrtb_ext.BidderSynacormedia)].Endpoint),
+		openrtb_ext.BidderTriplelift:       triplelift.NewTripleliftBidder(client, cfg.Adapters[string(openrtb_ext.BidderTriplelift)].Endpoint),
+		openrtb_ext.BidderTripleliftNative: triplelift_native.NewTripleliftNativeBidder(client, cfg.Adapters[string(openrtb_ext.BidderTripleliftNative)].Endpoint, cfg.Adapters[string(openrtb_ext.BidderTripleliftNative)].ExtraAdapterInfo),
+		openrtb_ext.BidderUnruly:           unruly.NewUnrulyBidder(client, cfg.Adapters[string(openrtb_ext.BidderUnruly)].Endpoint),
+		openrtb_ext.BidderVrtcal:           vrtcal.NewVrtcalBidder(cfg.Adapters[string(openrtb_ext.BidderVrtcal)].Endpoint),
+		openrtb_ext.BidderYieldmo:          yieldmo.NewYieldmoBidder(cfg.Adapters[string(openrtb_ext.BidderYieldmo)].Endpoint),
+		openrtb_ext.BidderGamma:            gamma.NewGammaBidder(cfg.Adapters[string(openrtb_ext.BidderGamma)].Endpoint),
+		openrtb_ext.BidderVisx:             visx.NewVisxBidder(cfg.Adapters[string(openrtb_ext.BidderVisx)].Endpoint),
+		openrtb_ext.BidderGamoshi:          gamoshi.NewGamoshiBidder(cfg.Adapters[string(openrtb_ext.BidderGamoshi)].Endpoint),
+		openrtb_ext.BidderMgid:             mgid.NewMgidBidder(cfg.Adapters[string(openrtb_ext.BidderMgid)].Endpoint),
+		openrtb_ext.BidderImprovedigital:   improvedigital.NewImprovedigitalBidder(cfg.Adapters[string(openrtb_ext.BidderImprovedigital)].Endpoint),
+		openrtb_ext.BidderTappx:            tappx.NewTappxBidder(client, cfg.Adapters[strings.ToLower(string(openrtb_ext.BidderTappx))].Endpoint),
+		openrtb_ext.BidderVerizonMedia:     verizonmedia.NewVerizonMediaBidder(client, cfg.Adapters[string(openrtb_ext.BidderVerizonMedia)].Endpoint),
+		openrtb_ext.BidderKubient:          kubient.NewKubientBidder(cfg.Adapters[string(openrtb_ext.BidderKubient)].Endpoint),
 		openrtb_ext.BidderFacebook: audienceNetwork.NewFacebookBidder(
 			client,
 			cfg.Adapters[strings.ToLower(string(openrtb_ext.BidderFacebook))].PlatformID,
diff --git a/go.mod b/go.mod
index b0c0ee67..5c837c2e 100644
--- a/go.mod
+++ b/go.mod
@@ -32,6 +32,7 @@ require (
 	github.com/onsi/ginkgo v1.10.1 // indirect
 	github.com/onsi/gomega v1.7.0 // indirect
 	github.com/pelletier/go-toml v1.2.0 // indirect
+	github.com/prebid/go-gdpr v0.6.0
 	github.com/prometheus/client_golang v0.0.0-20180623155954-77e8f2ddcfed
 	github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910
 	github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e // indirect
diff --git a/go.sum b/go.sum
index 02d21a8d..6d9e21f0 100644
--- a/go.sum
+++ b/go.sum
@@ -70,6 +70,8 @@ github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181
 github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
 github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/prebid/go-gdpr v0.6.0 h1:/GKrygGkUbsgd96HIkjAu7/6CHtRedvcojRtfAd4Igc=
+github.com/prebid/go-gdpr v0.6.0/go.mod h1:FPY0uxSrl9/Mz237LnPo3ge4aCG0wQ9FWf2b4WhwNn0=
 github.com/prometheus/client_golang v0.0.0-20180623155954-77e8f2ddcfed h1:0dloFFFNNDG7c+8qtkYw2FdADrWy9s5cI8wHp6tK3Mg=
 github.com/prometheus/client_golang v0.0.0-20180623155954-77e8f2ddcfed/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
 github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910 h1:idejC8f05m9MGOsuEi1ATq9shN03HrxNkD/luQvxCv8=
diff --git a/openrtb_ext/bidders.go b/openrtb_ext/bidders.go
index 099cdc96..f7f54976 100644
--- a/openrtb_ext/bidders.go
+++ b/openrtb_ext/bidders.go
@@ -20,100 +20,102 @@ type BidderName string
 // These names _must_ coincide with the bidder code in Prebid.js, if an adapter also exists in that project.
 // Please keep these (and the BidderMap) alphabetized to minimize merge conflicts among adapter submissions.
 const (
-	Bidder33Across       BidderName = "33across"
-	BidderAdkernel       BidderName = "adkernel"
-	BidderAdkernelAdn    BidderName = "adkernelAdn"
-	BidderAdpone         BidderName = "adpone"
-	BidderAdtelligent    BidderName = "adtelligent"
-	BidderAdform         BidderName = "adform"
-	BidderAdvangelists   BidderName = "advangelists"
-	BidderAppnexus       BidderName = "appnexus"
-	BidderBeachfront     BidderName = "beachfront"
-	BidderBrightroll     BidderName = "brightroll"
-	BidderConsumable     BidderName = "consumable"
-	BidderConversant     BidderName = "conversant"
-	BidderDatablocks     BidderName = "datablocks"
-	BidderEmxDigital     BidderName = "emx_digital"
-	BidderEPlanning      BidderName = "eplanning"
-	BidderFacebook       BidderName = "audienceNetwork"
-	BidderGamma          BidderName = "gamma"
-	BidderGamoshi        BidderName = "gamoshi"
-	BidderGrid           BidderName = "grid"
-	BidderGumGum         BidderName = "gumgum"
-	BidderImprovedigital BidderName = "improvedigital"
-	BidderIx             BidderName = "ix"
-	BidderKubient        BidderName = "kubient"
-	BidderLifestreet     BidderName = "lifestreet"
-	BidderLockerDome     BidderName = "lockerdome"
-	BidderMgid           BidderName = "mgid"
-	BidderOpenx          BidderName = "openx"
-	BidderPubmatic       BidderName = "pubmatic"
-	BidderPulsepoint     BidderName = "pulsepoint"
-	BidderRhythmone      BidderName = "rhythmone"
-	BidderRTBHouse       BidderName = "rtbhouse"
-	BidderRubicon        BidderName = "rubicon"
-	BidderSharethrough   BidderName = "sharethrough"
-	BidderSomoaudience   BidderName = "somoaudience"
-	BidderSovrn          BidderName = "sovrn"
-	BidderSonobi         BidderName = "sonobi"
-	BidderSynacormedia   BidderName = "synacormedia"
-	BidderTriplelift     BidderName = "triplelift"
-	BidderUnruly         BidderName = "unruly"
-	BidderVerizonMedia   BidderName = "verizonmedia"
-	BidderVrtcal         BidderName = "vrtcal"
-	BidderYieldmo        BidderName = "yieldmo"
-	BidderVisx           BidderName = "visx"
-	BidderTappx          BidderName = "tappx"
-	BidderEngageBDR      BidderName = "engagebdr"
+	Bidder33Across         BidderName = "33across"
+	BidderAdkernel         BidderName = "adkernel"
+	BidderAdkernelAdn      BidderName = "adkernelAdn"
+	BidderAdpone           BidderName = "adpone"
+	BidderAdtelligent      BidderName = "adtelligent"
+	BidderAdform           BidderName = "adform"
+	BidderAdvangelists     BidderName = "advangelists"
+	BidderAppnexus         BidderName = "appnexus"
+	BidderBeachfront       BidderName = "beachfront"
+	BidderBrightroll       BidderName = "brightroll"
+	BidderConsumable       BidderName = "consumable"
+	BidderConversant       BidderName = "conversant"
+	BidderDatablocks       BidderName = "datablocks"
+	BidderEmxDigital       BidderName = "emx_digital"
+	BidderEPlanning        BidderName = "eplanning"
+	BidderFacebook         BidderName = "audienceNetwork"
+	BidderGamma            BidderName = "gamma"
+	BidderGamoshi          BidderName = "gamoshi"
+	BidderGrid             BidderName = "grid"
+	BidderGumGum           BidderName = "gumgum"
+	BidderImprovedigital   BidderName = "improvedigital"
+	BidderIx               BidderName = "ix"
+	BidderKubient          BidderName = "kubient"
+	BidderLifestreet       BidderName = "lifestreet"
+	BidderLockerDome       BidderName = "lockerdome"
+	BidderMgid             BidderName = "mgid"
+	BidderOpenx            BidderName = "openx"
+	BidderPubmatic         BidderName = "pubmatic"
+	BidderPulsepoint       BidderName = "pulsepoint"
+	BidderRhythmone        BidderName = "rhythmone"
+	BidderRTBHouse         BidderName = "rtbhouse"
+	BidderRubicon          BidderName = "rubicon"
+	BidderSharethrough     BidderName = "sharethrough"
+	BidderSomoaudience     BidderName = "somoaudience"
+	BidderSovrn            BidderName = "sovrn"
+	BidderSonobi           BidderName = "sonobi"
+	BidderSynacormedia     BidderName = "synacormedia"
+	BidderTriplelift       BidderName = "triplelift"
+	BidderTripleliftNative BidderName = "triplelift_native"
+	BidderUnruly           BidderName = "unruly"
+	BidderVerizonMedia     BidderName = "verizonmedia"
+	BidderVrtcal           BidderName = "vrtcal"
+	BidderYieldmo          BidderName = "yieldmo"
+	BidderVisx             BidderName = "visx"
+	BidderTappx            BidderName = "tappx"
+	BidderEngageBDR        BidderName = "engagebdr"
 )
 
 // BidderMap stores all the valid OpenRTB 2.x Bidders in the project. This map *must not* be mutated.
 var BidderMap = map[string]BidderName{
-	"33across":        Bidder33Across,
-	"adkernel":        BidderAdkernel,
-	"adkernelAdn":     BidderAdkernelAdn,
-	"adpone":          BidderAdpone,
-	"adtelligent":     BidderAdtelligent,
-	"adform":          BidderAdform,
-	"advangelists":    BidderAdvangelists,
-	"appnexus":        BidderAppnexus,
-	"beachfront":      BidderBeachfront,
-	"audienceNetwork": BidderFacebook,
-	"brightroll":      BidderBrightroll,
-	"consumable":      BidderConsumable,
-	"conversant":      BidderConversant,
-	"datablocks":      BidderDatablocks,
-	"emx_digital":     BidderEmxDigital,
-	"eplanning":       BidderEPlanning,
-	"gamma":           BidderGamma,
-	"gamoshi":         BidderGamoshi,
-	"grid":            BidderGrid,
-	"gumgum":          BidderGumGum,
-	"improvedigital":  BidderImprovedigital,
-	"ix":              BidderIx,
-	"lifestreet":      BidderLifestreet,
-	"lockerdome":      BidderLockerDome,
-	"openx":           BidderOpenx,
-	"mgid":            BidderMgid,
-	"pubmatic":        BidderPubmatic,
-	"pulsepoint":      BidderPulsepoint,
-	"rhythmone":       BidderRhythmone,
-	"rtbhouse":        BidderRTBHouse,
-	"rubicon":         BidderRubicon,
-	"sharethrough":    BidderSharethrough,
-	"somoaudience":    BidderSomoaudience,
-	"sovrn":           BidderSovrn,
-	"sonobi":          BidderSonobi,
-	"synacormedia":    BidderSynacormedia,
-	"triplelift":      BidderTriplelift,
-	"unruly":          BidderUnruly,
-	"verizonmedia":    BidderVerizonMedia,
-	"vrtcal":          BidderVrtcal,
-	"yieldmo":         BidderYieldmo,
-	"visx":            BidderVisx,
-	"tappx":           BidderTappx,
-	"engagebdr":       BidderEngageBDR,
-	"kubient":         BidderKubient,
+	"33across":          Bidder33Across,
+	"adkernel":          BidderAdkernel,
+	"adkernelAdn":       BidderAdkernelAdn,
+	"adpone":            BidderAdpone,
+	"adtelligent":       BidderAdtelligent,
+	"adform":            BidderAdform,
+	"advangelists":      BidderAdvangelists,
+	"appnexus":          BidderAppnexus,
+	"beachfront":        BidderBeachfront,
+	"audienceNetwork":   BidderFacebook,
+	"brightroll":        BidderBrightroll,
+	"consumable":        BidderConsumable,
+	"conversant":        BidderConversant,
+	"datablocks":        BidderDatablocks,
+	"emx_digital":       BidderEmxDigital,
+	"eplanning":         BidderEPlanning,
+	"gamma":             BidderGamma,
+	"gamoshi":           BidderGamoshi,
+	"grid":              BidderGrid,
+	"gumgum":            BidderGumGum,
+	"improvedigital":    BidderImprovedigital,
+	"ix":                BidderIx,
+	"lifestreet":        BidderLifestreet,
+	"lockerdome":        BidderLockerDome,
+	"openx":             BidderOpenx,
+	"mgid":              BidderMgid,
+	"pubmatic":          BidderPubmatic,
+	"pulsepoint":        BidderPulsepoint,
+	"rhythmone":         BidderRhythmone,
+	"rtbhouse":          BidderRTBHouse,
+	"rubicon":           BidderRubicon,
+	"sharethrough":      BidderSharethrough,
+	"somoaudience":      BidderSomoaudience,
+	"sovrn":             BidderSovrn,
+	"sonobi":            BidderSonobi,
+	"synacormedia":      BidderSynacormedia,
+	"triplelift":        BidderTriplelift,
+	"triplelift_native": BidderTripleliftNative,
+	"unruly":            BidderUnruly,
+	"verizonmedia":      BidderVerizonMedia,
+	"vrtcal":            BidderVrtcal,
+	"yieldmo":           BidderYieldmo,
+	"visx":              BidderVisx,
+	"tappx":             BidderTappx,
+	"engagebdr":         BidderEngageBDR,
+	"kubient":           BidderKubient,
 }
 
 // BidderList returns the values of the BidderMap
diff --git a/static/bidder-info/triplelift_native.yaml b/static/bidder-info/triplelift_native.yaml
new file mode 100644
index 00000000..abe91659
--- /dev/null
+++ b/static/bidder-info/triplelift_native.yaml
@@ -0,0 +1,9 @@
+maintainer:
+  email: "prebid@triplelift.com"
+capabilities:
+  app:
+    mediaTypes:
+      - native
+  site:
+    mediaTypes:
+      - native
diff --git a/static/bidder-params/triplelift_native.json b/static/bidder-params/triplelift_native.json
new file mode 100644
index 00000000..c81230aa
--- /dev/null
+++ b/static/bidder-params/triplelift_native.json
@@ -0,0 +1,21 @@
+{
+  "$schema": "http://json-schema.org/draft-04/schema#",
+  "title": "Triplelift Adapter Params",
+  "description": "A schema which validates params accepted by the Triplelift adapter",
+
+  "type": "object",
+  "properties": {
+    "inventoryCode": {
+      "type": "string",
+      "description": "TripleLift inventory code for this ad unit (provided to you by your partner manager)"
+    },
+    "floor" :  {"description" : "the bid floor, in usd", "type": "number" }
+  },
+  "oneOf": [{
+    "oneOf": [{
+      "required": ["inventoryCode"]
+    }] }],
+  "not": {
+    "required": ["floor"]
+  }
+}
diff --git a/usersync/cookie.go b/usersync/cookie.go
index 6ced4b62..13aa7f38 100644
--- a/usersync/cookie.go
+++ b/usersync/cookie.go
@@ -193,19 +193,24 @@ func (cookie *PBSCookie) SetCookieOnResponse(w http.ResponseWriter, setSiteCooki
 		currSize = len([]byte(httpCookie.String()))
 	}
 
-	uidsCookieStr := httpCookie.String()
+	var uidsCookieStr string
 	var sameSiteCookie *http.Cookie
 	if setSiteCookie {
+		httpCookie.Secure = true
+		uidsCookieStr = httpCookie.String()
 		uidsCookieStr += SameSiteAttribute
 		sameSiteCookie = &http.Cookie{
 			Name:    SameSiteCookieName,
 			Value:   SameSiteCookieValue,
 			Expires: time.Now().Add(ttl),
 			Path:    "/",
+			Secure:  true,
 		}
 		sameSiteCookieStr := sameSiteCookie.String()
 		sameSiteCookieStr += SameSiteAttribute
 		w.Header().Add("Set-Cookie", sameSiteCookieStr)
+	} else {
+		uidsCookieStr = httpCookie.String()
 	}
 	w.Header().Add("Set-Cookie", uidsCookieStr)
 }
diff --git a/usersync/usersyncers/syncer.go b/usersync/usersyncers/syncer.go
index 250f5a62..7a43fe83 100644
--- a/usersync/usersyncers/syncer.go
+++ b/usersync/usersyncers/syncer.go
@@ -43,6 +43,7 @@ import (
 	"github.com/prebid/prebid-server/adapters/sovrn"
 	"github.com/prebid/prebid-server/adapters/synacormedia"
 	"github.com/prebid/prebid-server/adapters/triplelift"
+	"github.com/prebid/prebid-server/adapters/triplelift_native"
 	"github.com/prebid/prebid-server/adapters/unruly"
 	"github.com/prebid/prebid-server/adapters/verizonmedia"
 	"github.com/prebid/prebid-server/adapters/visx"
@@ -95,6 +96,7 @@ func NewSyncerMap(cfg *config.Configuration) map[openrtb_ext.BidderName]usersync
 	insertIntoMap(cfg, syncers, openrtb_ext.BidderSonobi, sonobi.NewSonobiSyncer)
 	insertIntoMap(cfg, syncers, openrtb_ext.BidderSynacormedia, synacormedia.NewSynacorMediaSyncer)
 	insertIntoMap(cfg, syncers, openrtb_ext.BidderTriplelift, triplelift.NewTripleliftSyncer)
+	insertIntoMap(cfg, syncers, openrtb_ext.BidderTripleliftNative, triplelift_native.NewTripleliftSyncer)
 	insertIntoMap(cfg, syncers, openrtb_ext.BidderVrtcal, vrtcal.NewVrtcalSyncer)
 	insertIntoMap(cfg, syncers, openrtb_ext.BidderYieldmo, yieldmo.NewYieldmoSyncer)
 	insertIntoMap(cfg, syncers, openrtb_ext.BidderVisx, visx.NewVisxSyncer)


Test patch:
diff --git a/adapters/triplelift_native/triplelift_native_test.go b/adapters/triplelift_native/triplelift_native_test.go
new file mode 100644
index 00000000..0beb18ed
--- /dev/null
+++ b/adapters/triplelift_native/triplelift_native_test.go
@@ -0,0 +1,23 @@
+package triplelift_native
+
+import (
+	"fmt"
+	"github.com/prebid/prebid-server/adapters"
+	"github.com/prebid/prebid-server/adapters/adapterstest"
+	"github.com/stretchr/testify/assert"
+	"testing"
+)
+
+func TestBadConfig(t *testing.T) {
+	badServer := NewTripleliftNativeBidder(nil, "", "{foo:2}")
+	assert.NotEmpty(t, badServer, "NewTripleliftBidder should not return nil")
+	expected := &adapters.MisconfiguredBidder{
+		Name:  "TripleliftNativeAdapter",
+		Error: fmt.Errorf("TripleliftNativeAdapter could not unmarshal config json"),
+	}
+	assert.IsType(t, expected, badServer, "expected misconfigured server")
+}
+
+func TestJsonSamples(t *testing.T) {
+	adapterstest.RunJSONBidderTest(t, "triplelift_nativetest", NewTripleliftNativeBidder(nil, "http://tlx.3lift.net/s2s/auction?supplier_id=19", "{\"publisher_whitelist\":[\"foo\",\"bar\",\"baz\"], \"endpoint\":\"http://tlx.3lift.net/s2sn/auction?supplier_id=20\"}"))
+}
diff --git a/adapters/triplelift_native/triplelift_nativetest/exemplary/optional-params.json b/adapters/triplelift_native/triplelift_nativetest/exemplary/optional-params.json
new file mode 100644
index 00000000..d25f72e2
--- /dev/null
+++ b/adapters/triplelift_native/triplelift_nativetest/exemplary/optional-params.json
@@ -0,0 +1,55 @@
+{
+  "mockBidRequest": {
+    "id": "test-request-id",
+    "site": { "publisher": {"id":"foo","name":"foo"}},
+    "imp": [
+      {
+   "native":{
+    "request" : "{\"plcmtcnt\":1,\"plcmttype\":2,\"privacy\":1,\"context\":1,\"contextsubtype\":12,\"eventtrackers\":[{\"event\":1,\"methods\":[1,2]},{\"event\":2,\"methods\":[1]}],\"assets\":[{\"data\":{\"type\":12},\"required\":1},{\"title\":{\"len\":50},\"required\":1},{\"img\":{\"w\":80,\"h\":80,\"type\":1},\"required\":1},{\"img\":{\"w\":1200,\"h\":627,\"type\":3},\"required\":1},{\"data\":{\"type\":3},\"required\":0},{\"data\":{\"len\":100,\"type\":2},\"required\":1},{\"video\":{\"mimes\":[\"video/mpeg\",\"video/mp4\"],\"minduration\":2,\"protocols\":[2,5],\"maxduration\":2,\"ext\":{\"playbackmethod\":[1,2]}},\"required\":1}],\"ver\":\"1.2\"}"
+   },
+        "id": "test-imp-id",
+        "ext": {
+          "bidder": {
+            "inventoryCode": "foo",
+            "floor" : 20
+          }
+        }
+      }
+    ]
+  },
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "http://tlx.3lift.net/s2sn/auction?supplier_id=20",
+        "body": {
+          "id": "test-request-id",
+          "imp": [
+            {
+              "id": "test-imp-id",
+              "native": {
+    "request" : "{\"plcmtcnt\":1,\"plcmttype\":2,\"privacy\":1,\"context\":1,\"contextsubtype\":12,\"eventtrackers\":[{\"event\":1,\"methods\":[1,2]},{\"event\":2,\"methods\":[1]}],\"assets\":[{\"data\":{\"type\":12},\"required\":1},{\"title\":{\"len\":50},\"required\":1},{\"img\":{\"w\":80,\"h\":80,\"type\":1},\"required\":1},{\"img\":{\"w\":1200,\"h\":627,\"type\":3},\"required\":1},{\"data\":{\"type\":3},\"required\":0},{\"data\":{\"len\":100,\"type\":2},\"required\":1},{\"video\":{\"mimes\":[\"video/mpeg\",\"video/mp4\"],\"minduration\":2,\"protocols\":[2,5],\"maxduration\":2,\"ext\":{\"playbackmethod\":[1,2]}},\"required\":1}],\"ver\":\"1.2\"}"
+              },
+              "tagid": "foo",
+              "bidfloor": 20,
+              "ext": {
+                "bidder": {
+                  "inventoryCode": "foo",
+                   "floor" : 20
+                }
+              }
+            }
+          ],
+        "site": {
+            "publisher": {
+              "id": "foo",
+              "name": "foo"
+            }
+          }
+        }
+      },
+      "mockResponse": {
+        "status": 204
+      }
+    }
+  ]
+}
diff --git a/adapters/triplelift_native/triplelift_nativetest/supplemental/badext.json b/adapters/triplelift_native/triplelift_nativetest/supplemental/badext.json
new file mode 100644
index 00000000..7df178bd
--- /dev/null
+++ b/adapters/triplelift_native/triplelift_nativetest/supplemental/badext.json
@@ -0,0 +1,36 @@
+{
+  "expectedMakeRequestsErrors": [
+    {
+      "value": "json: cannot unmarshal string into Go value of type adapters.ExtImpBidder",
+      "comparison": "literal"
+    },
+    {
+      "value": "No valid impressions for triplelift",
+      "comparison": "literal"
+    }
+  ],
+  "mockBidRequest": {
+    "id": "test-request-id",
+    "app": { "publisher": {"ext":{"prebid":{"parentAccount":"foo"}}, "id":"far","name":"bar"}},
+    "imp": [
+      {
+        "id": "test-imp-id",
+        "banner": {
+          "format": [
+            {
+              "w": 300,
+              "h": 250
+            },
+            {
+              "w": 300,
+              "h": 600
+            }
+          ]
+        },
+        "ext":  "aaa"      
+    }
+    ]
+  },
+  "httpCalls": [
+  ]
+}
diff --git a/adapters/triplelift_native/triplelift_nativetest/supplemental/badextbidder.json b/adapters/triplelift_native/triplelift_nativetest/supplemental/badextbidder.json
new file mode 100644
index 00000000..59ebe048
--- /dev/null
+++ b/adapters/triplelift_native/triplelift_nativetest/supplemental/badextbidder.json
@@ -0,0 +1,38 @@
+{
+  "expectedMakeRequestsErrors": [
+    {
+      "value": "json: cannot unmarshal string into Go value of type openrtb_ext.ExtImpTriplelift",
+      "comparison": "literal"
+    },
+    {
+      "value": "No valid impressions for triplelift",
+      "comparison": "literal"
+    }
+  ],
+  "mockBidRequest": {
+    "id": "test-request-id",
+    "site": { "publisher": {"id":"foo","name":"foo"}},
+    "imp": [
+      {
+        "id": "test-imp-id",
+        "banner": {
+          "format": [
+            {
+              "w": 300,
+              "h": 250
+            },
+            {
+              "w": 300,
+              "h": 600
+            }
+          ]
+        },
+        "ext": {
+          "bidder": "aaa"
+        }
+      }
+    ]
+  },
+  "httpCalls":[],
+  "expectedBidResponses": []
+}
diff --git a/adapters/triplelift_native/triplelift_nativetest/supplemental/badlyformed.json b/adapters/triplelift_native/triplelift_nativetest/supplemental/badlyformed.json
new file mode 100644
index 00000000..c38e9c91
--- /dev/null
+++ b/adapters/triplelift_native/triplelift_nativetest/supplemental/badlyformed.json
@@ -0,0 +1,19 @@
+{
+  "expectedMakeRequestsErrors" : [{"comparison":"literal","value": "no native object specified"},{"comparison":"literal","value":"No valid impressions for triplelift"}],
+  "mockBidRequest": {
+    "site": { "publisher": {"id":"foo","name":"foo"}},
+    "id": "test-request-id",
+    "imp": [
+      {
+        "id": "test-imp-id",
+        "ext": {
+          "bidder": {
+            "inventoryCode": "aa" 
+          }
+        }
+      }
+    ]
+  },
+  "httpCalls": [
+  ]
+}
diff --git a/adapters/triplelift_native/triplelift_nativetest/supplemental/badresponseext.json b/adapters/triplelift_native/triplelift_nativetest/supplemental/badresponseext.json
new file mode 100644
index 00000000..c9bb90f8
--- /dev/null
+++ b/adapters/triplelift_native/triplelift_nativetest/supplemental/badresponseext.json
@@ -0,0 +1,103 @@
+{
+  "mockBidRequest": {
+    "id": "test-request-id",
+    "site": { "publisher": {"id":"foo","name":"foo"}},
+    "imp": [
+      {
+        "id": "test-imp-id",
+   "native":{
+    "request" : "{\"plcmtcnt\":1,\"plcmttype\":2,\"privacy\":1,\"context\":1,\"contextsubtype\":12,\"eventtrackers\":[{\"event\":1,\"methods\":[1,2]},{\"event\":2,\"methods\":[1]}],\"assets\":[{\"data\":{\"type\":12},\"required\":1},{\"title\":{\"len\":50},\"required\":1},{\"img\":{\"w\":80,\"h\":80,\"type\":1},\"required\":1},{\"img\":{\"w\":1200,\"h\":627,\"type\":3},\"required\":1},{\"data\":{\"type\":3},\"required\":0},{\"data\":{\"len\":100,\"type\":2},\"required\":1},{\"video\":{\"mimes\":[\"video/mpeg\",\"video/mp4\"],\"minduration\":2,\"protocols\":[2,5],\"maxduration\":2,\"ext\":{\"playbackmethod\":[1,2]}},\"required\":1}],\"ver\":\"1.2\"}"
+   },
+        "ext": {
+          "bidder": {
+            "inventoryCode": "aa" 
+          }
+        }
+      }
+    ]
+  },
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "http://tlx.3lift.net/s2sn/auction?supplier_id=20",
+        "body": {
+          "site": { "publisher": {"id":"foo","name":"foo"}},
+          "id": "test-request-id",
+          "imp": [
+            {
+              "id": "test-imp-id",
+              "native" : {
+    "request" : "{\"plcmtcnt\":1,\"plcmttype\":2,\"privacy\":1,\"context\":1,\"contextsubtype\":12,\"eventtrackers\":[{\"event\":1,\"methods\":[1,2]},{\"event\":2,\"methods\":[1]}],\"assets\":[{\"data\":{\"type\":12},\"required\":1},{\"title\":{\"len\":50},\"required\":1},{\"img\":{\"w\":80,\"h\":80,\"type\":1},\"required\":1},{\"img\":{\"w\":1200,\"h\":627,\"type\":3},\"required\":1},{\"data\":{\"type\":3},\"required\":0},{\"data\":{\"len\":100,\"type\":2},\"required\":1},{\"video\":{\"mimes\":[\"video/mpeg\",\"video/mp4\"],\"minduration\":2,\"protocols\":[2,5],\"maxduration\":2,\"ext\":{\"playbackmethod\":[1,2]}},\"required\":1}],\"ver\":\"1.2\"}"
+              },
+              "tagid" : "aa",
+              "ext": {
+                "bidder": {
+                  "inventoryCode": "aa"
+                }
+              }
+            }
+          ]
+        }
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": {
+          "id": "test-request-id",
+          "seatbid": [
+            {
+              "seat": "958",
+              "bid": [
+                {
+                  "id": "7706636740145184841",
+                  "impid": "test-imp-id",
+                  "price": 0.5,
+                  "adid": "29681110",
+                  "adm": "some-test-ad",
+                  "adomain": [
+                    "triplelift.com"
+                  ],
+                  "iurl": "http://nym1-ib.adnxs.com/cr?id=29681110",
+                  "cid": "958",
+                  "crid": "29681110",
+                  "h": 250,
+                  "w": 300,
+                  "ext": "aaa"                 
+                }
+              ]
+            }
+          ],
+          "bidid": "5778926625248726496",
+          "cur": "USD"
+        }
+      }
+    }
+  ],
+  "expectedBidResponses": [
+    {
+      "currency": "USD",
+      "bids": [ {"id": "test-request-id",
+            "type":"native",
+              "bid": 
+                {
+                  "id": "7706636740145184841",
+                  "impid": "test-imp-id",
+                  "price": 0.5,
+                  "adid": "29681110",
+                  "adm": "some-test-ad",
+                  "adomain": [
+                    "triplelift.com"
+                  ],
+                  "iurl": "http://nym1-ib.adnxs.com/cr?id=29681110",
+                  "cid": "958",
+                  "crid": "29681110",
+                  "h": 250,
+                  "w": 300,
+                  "ext": "aaa"
+                }
+              ,
+          "bidid": "5778926625248726496",
+          "cur": "USD" }
+      ]
+    }
+  ]
+}
diff --git a/adapters/triplelift_native/triplelift_nativetest/supplemental/badstatuscode.json b/adapters/triplelift_native/triplelift_nativetest/supplemental/badstatuscode.json
new file mode 100644
index 00000000..175ec038
--- /dev/null
+++ b/adapters/triplelift_native/triplelift_nativetest/supplemental/badstatuscode.json
@@ -0,0 +1,50 @@
+{
+  "mockBidRequest": {
+    "site": { "publisher": {"id":"foo","name":"foo"}},
+    "id": "test-request-id",
+    "imp": [
+      {
+   "native":{
+    "request" : "{\"plcmtcnt\":1,\"plcmttype\":2,\"privacy\":1,\"context\":1,\"contextsubtype\":12,\"eventtrackers\":[{\"event\":1,\"methods\":[1,2]},{\"event\":2,\"methods\":[1]}],\"assets\":[{\"data\":{\"type\":12},\"required\":1},{\"title\":{\"len\":50},\"required\":1},{\"img\":{\"w\":80,\"h\":80,\"type\":1},\"required\":1},{\"img\":{\"w\":1200,\"h\":627,\"type\":3},\"required\":1},{\"data\":{\"type\":3},\"required\":0},{\"data\":{\"len\":100,\"type\":2},\"required\":1},{\"video\":{\"mimes\":[\"video/mpeg\",\"video/mp4\"],\"minduration\":2,\"protocols\":[2,5],\"maxduration\":2,\"ext\":{\"playbackmethod\":[1,2]}},\"required\":1}],\"ver\":\"1.2\"}"
+   },
+        "id": "test-imp-id",
+        "ext": {
+          "bidder": {
+            "inventoryCode": "aa" 
+          }
+        }
+      }
+    ]
+  },
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "http://tlx.3lift.net/s2sn/auction?supplier_id=20",
+        "body": {
+          "site": { "publisher": {"id":"foo","name":"foo"}},
+          "id": "test-request-id",
+          "imp": [
+            {
+   "native":{
+    "request" : "{\"plcmtcnt\":1,\"plcmttype\":2,\"privacy\":1,\"context\":1,\"contextsubtype\":12,\"eventtrackers\":[{\"event\":1,\"methods\":[1,2]},{\"event\":2,\"methods\":[1]}],\"assets\":[{\"data\":{\"type\":12},\"required\":1},{\"title\":{\"len\":50},\"required\":1},{\"img\":{\"w\":80,\"h\":80,\"type\":1},\"required\":1},{\"img\":{\"w\":1200,\"h\":627,\"type\":3},\"required\":1},{\"data\":{\"type\":3},\"required\":0},{\"data\":{\"len\":100,\"type\":2},\"required\":1},{\"video\":{\"mimes\":[\"video/mpeg\",\"video/mp4\"],\"minduration\":2,\"protocols\":[2,5],\"maxduration\":2,\"ext\":{\"playbackmethod\":[1,2]}},\"required\":1}],\"ver\":\"1.2\"}"
+   },
+              "id": "test-imp-id",
+              "tagid" : "aa",
+              "ext": {
+                "bidder": {
+                  "inventoryCode": "aa"
+                }
+              }
+            }
+          ]
+        }
+      },
+      "mockResponse": {
+        "status": 400
+      }
+    }
+  ],
+  "expectedBidResponses": [
+  ],
+  "expectedMakeBidsErrors": [{"comparison":"literal", "value" : "Unexpected status code: 400. Run with request.debug = 1 for more info"}]
+}
diff --git a/adapters/triplelift_native/triplelift_nativetest/supplemental/nopub.json b/adapters/triplelift_native/triplelift_nativetest/supplemental/nopub.json
new file mode 100644
index 00000000..cdeaff39
--- /dev/null
+++ b/adapters/triplelift_native/triplelift_nativetest/supplemental/nopub.json
@@ -0,0 +1,37 @@
+{
+  "expectedMakeRequestsErrors": [
+    {
+      "value": "Unsupported publisher for triplelift_native",
+      "comparison": "literal"
+    }
+  ],
+  "mockBidRequest": {
+    "id": "test-request-id",
+    "app": { "publisher": {"ext":{"prebid":{"parentAccount":"faz"}}, "id":"far","name":"bar"}},
+    "imp": [
+      {
+        "id": "test-imp-id",
+        "banner": {
+          "format": [
+            {
+              "w": 300,
+              "h": 250
+            },
+            {
+              "w": 300,
+              "h": 600
+            }
+          ]
+        },
+        "ext": {
+          "bidder": {
+            "inventoryCode": "foo",
+            "floor": 20
+          }
+        }
+    }
+    ]
+  },
+  "httpCalls": [
+  ]
+}
diff --git a/adapters/triplelift_native/triplelift_nativetest/supplemental/notgoodstatuscode.json b/adapters/triplelift_native/triplelift_nativetest/supplemental/notgoodstatuscode.json
new file mode 100644
index 00000000..ce40d36f
--- /dev/null
+++ b/adapters/triplelift_native/triplelift_nativetest/supplemental/notgoodstatuscode.json
@@ -0,0 +1,86 @@
+{
+  "mockBidRequest": {
+    "site": { "publisher": {"id":"foo","name":"foo"}},
+    "id": "test-request-id",
+    "imp": [
+      {
+   "native":{
+    "request" : "{\"plcmtcnt\":1,\"plcmttype\":2,\"privacy\":1,\"context\":1,\"contextsubtype\":12,\"eventtrackers\":[{\"event\":1,\"methods\":[1,2]},{\"event\":2,\"methods\":[1]}],\"assets\":[{\"data\":{\"type\":12},\"required\":1},{\"title\":{\"len\":50},\"required\":1},{\"img\":{\"w\":80,\"h\":80,\"type\":1},\"required\":1},{\"img\":{\"w\":1200,\"h\":627,\"type\":3},\"required\":1},{\"data\":{\"type\":3},\"required\":0},{\"data\":{\"len\":100,\"type\":2},\"required\":1},{\"video\":{\"mimes\":[\"video/mpeg\",\"video/mp4\"],\"minduration\":2,\"protocols\":[2,5],\"maxduration\":2,\"ext\":{\"playbackmethod\":[1,2]}},\"required\":1}],\"ver\":\"1.2\"}"
+   },
+        "id": "test-imp-id",
+        "ext": {
+          "bidder": {
+            "inventoryCode": "aa"
+          }
+        }
+      }
+    ]
+  },
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "http://tlx.3lift.net/s2sn/auction?supplier_id=20",
+        "body": {
+          "site": { "publisher": {"id":"foo","name":"foo"}},
+          "id": "test-request-id",
+          "imp": [
+            {
+   "native":{
+    "request" : "{\"plcmtcnt\":1,\"plcmttype\":2,\"privacy\":1,\"context\":1,\"contextsubtype\":12,\"eventtrackers\":[{\"event\":1,\"methods\":[1,2]},{\"event\":2,\"methods\":[1]}],\"assets\":[{\"data\":{\"type\":12},\"required\":1},{\"title\":{\"len\":50},\"required\":1},{\"img\":{\"w\":80,\"h\":80,\"type\":1},\"required\":1},{\"img\":{\"w\":1200,\"h\":627,\"type\":3},\"required\":1},{\"data\":{\"type\":3},\"required\":0},{\"data\":{\"len\":100,\"type\":2},\"required\":1},{\"video\":{\"mimes\":[\"video/mpeg\",\"video/mp4\"],\"minduration\":2,\"protocols\":[2,5],\"maxduration\":2,\"ext\":{\"playbackmethod\":[1,2]}},\"required\":1}],\"ver\":\"1.2\"}"
+   },
+              "id": "test-imp-id",
+              "tagid" : "aa",
+              "ext": {
+                "bidder": {
+                  "inventoryCode": "aa"
+                }
+              }
+            }
+          ]
+        }
+      },
+      "mockResponse": {
+        "status": 302,
+        "body": {
+          "id": "test-request-id",
+          "seatbid": [
+            {
+              "seat": "958",
+              "bid": [
+                {
+                  "id": "7706636740145184841",
+                  "impid": "test-imp-id",
+                  "price": 0.5,
+                  "adid": "29681110",
+                  "adm": "some-test-ad",
+                  "adomain": [
+                    "triplelift.com"
+                  ],
+                  "iurl": "http://nym1-ib.adnxs.com/cr?id=29681110",
+                  "cid": "958",
+                  "crid": "29681110",
+                  "h": 250,
+                  "w": 300,
+                  "ext": {
+                    "triplelift_pb": {
+                      "format": 2
+                    }
+                  }
+                }
+              ]
+            }
+          ],
+          "bidid": "5778926625248726496",
+          "cur": "USD"
+        }
+      }
+    }
+  ],
+  "expectedBidResponses": [],
+  "expectedMakeBidsErrors": [
+    {
+      "value": "Unexpected status code: 302. Run with request.debug = 1 for more info",
+      "comparison": "literal"
+    }
+  ]
+}
diff --git a/adapters/triplelift_native/usersync_test.go b/adapters/triplelift_native/usersync_test.go
new file mode 100644
index 00000000..85610f61
--- /dev/null
+++ b/adapters/triplelift_native/usersync_test.go
@@ -0,0 +1,19 @@
+package triplelift_native
+
+import (
+	"testing"
+	"text/template"
+
+	"github.com/stretchr/testify/assert"
+)
+
+func TestTripleliftSyncer(t *testing.T) {
+	temp := template.Must(template.New("sync-template").Parse("//eb2.3lift.com/getuid?gdpr={{.GDPR}}&cmp_cs={{.GDPRConsent}}&redir=https%3A%2F%2Feb2.3lift.com%2Fsetuid%3Fbidder%3Dtriplelift%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%24UID"))
+	syncer := NewTripleliftSyncer(temp)
+	syncInfo, err := syncer.GetUsersyncInfo("", "")
+	assert.NoError(t, err)
+	assert.Equal(t, "//eb2.3lift.com/getuid?gdpr=&cmp_cs=&redir=https%3A%2F%2Feb2.3lift.com%2Fsetuid%3Fbidder%3Dtriplelift%26gdpr%3D%26gdpr_consent%3D%26uid%3D%24UID", syncInfo.URL)
+	assert.Equal(t, "redirect", syncInfo.Type)
+	assert.EqualValues(t, 28, syncer.GDPRVendorID())
+	assert.Equal(t, false, syncInfo.SupportCORS)
+}
diff --git a/docs/developers/automated-tests.md b/docs/developers/automated-tests.md
index c8c19d3d..12532237 100644
--- a/docs/developers/automated-tests.md
+++ b/docs/developers/automated-tests.md
@@ -1,8 +1,5 @@
 # Automated Tests
 
-**NOTE**: The automated tests will not pass on Go 1.11 due to [this bug](https://github.com/golang/go/issues/27275) in Go.
-This issue has been fixed in Go 1.11.1.
-
 This project uses [TravisCI](https://travis-ci.org/) to make sure that every PR passes automated tests.
 To reproduce these tests locally, use:
 
diff --git a/usersync/cookie_test.go b/usersync/cookie_test.go
index a59f7def..ef2e9911 100644
--- a/usersync/cookie_test.go
+++ b/usersync/cookie_test.go
@@ -425,6 +425,9 @@ func TestSetCookieOnResponseForSameSiteNone(t *testing.T) {
 	if !strings.Contains(writtenCookie, "SSCookie=1") {
 		t.Error("Set-Cookie should contain SSCookie=1")
 	}
+	if !strings.Contains(writtenCookie, "; Secure;") {
+		t.Error("Set-Cookie should contain Secure")
+	}
 }
 
 func TestSetCookieOnResponseForOlderChromeVersion(t *testing.T) {
diff --git a/usersync/usersyncers/syncer_test.go b/usersync/usersyncers/syncer_test.go
index 8d9cc644..1c4b3b07 100644
--- a/usersync/usersyncers/syncer_test.go
+++ b/usersync/usersyncers/syncer_test.go
@@ -14,49 +14,50 @@ func TestNewSyncerMap(t *testing.T) {
 	}
 	cfg := &config.Configuration{
 		Adapters: map[string]config.Adapter{
-			string(openrtb_ext.BidderAdform):         syncConfig,
-			string(openrtb_ext.BidderAdkernel):       syncConfig,
-			string(openrtb_ext.BidderAdkernelAdn):    syncConfig,
-			string(openrtb_ext.BidderAdpone):         syncConfig,
-			string(openrtb_ext.BidderAdtelligent):    syncConfig,
-			string(openrtb_ext.BidderAdvangelists):   syncConfig,
-			string(openrtb_ext.BidderAppnexus):       syncConfig,
-			string(openrtb_ext.BidderBeachfront):     syncConfig,
-			string(openrtb_ext.BidderFacebook):       syncConfig,
-			string(openrtb_ext.BidderBrightroll):     syncConfig,
-			string(openrtb_ext.BidderConsumable):     syncConfig,
-			string(openrtb_ext.BidderConversant):     syncConfig,
-			string(openrtb_ext.BidderDatablocks):     syncConfig,
-			string(openrtb_ext.BidderEmxDigital):     syncConfig,
-			string(openrtb_ext.BidderEPlanning):      syncConfig,
-			string(openrtb_ext.BidderGrid):           syncConfig,
-			string(openrtb_ext.BidderGumGum):         syncConfig,
-			string(openrtb_ext.BidderImprovedigital): syncConfig,
-			string(openrtb_ext.BidderIx):             syncConfig,
-			string(openrtb_ext.BidderLifestreet):     syncConfig,
-			string(openrtb_ext.BidderLockerDome):     syncConfig,
-			string(openrtb_ext.BidderMgid):           syncConfig,
-			string(openrtb_ext.BidderOpenx):          syncConfig,
-			string(openrtb_ext.BidderPubmatic):       syncConfig,
-			string(openrtb_ext.BidderPulsepoint):     syncConfig,
-			string(openrtb_ext.BidderRhythmone):      syncConfig,
-			string(openrtb_ext.BidderRTBHouse):       syncConfig,
-			string(openrtb_ext.BidderRubicon):        syncConfig,
-			string(openrtb_ext.BidderSharethrough):   syncConfig,
-			string(openrtb_ext.BidderSomoaudience):   syncConfig,
-			string(openrtb_ext.BidderSovrn):          syncConfig,
-			string(openrtb_ext.BidderSynacormedia):   syncConfig,
-			string(openrtb_ext.BidderTriplelift):     syncConfig,
-			string(openrtb_ext.Bidder33Across):       syncConfig,
-			string(openrtb_ext.BidderSonobi):         syncConfig,
-			string(openrtb_ext.BidderVrtcal):         syncConfig,
-			string(openrtb_ext.BidderYieldmo):        syncConfig,
-			string(openrtb_ext.BidderGamma):          syncConfig,
-			string(openrtb_ext.BidderVisx):           syncConfig,
-			string(openrtb_ext.BidderGamoshi):        syncConfig,
-			string(openrtb_ext.BidderUnruly):         syncConfig,
-			string(openrtb_ext.BidderVerizonMedia):   syncConfig,
-			string(openrtb_ext.BidderEngageBDR):      syncConfig,
+			string(openrtb_ext.BidderAdform):           syncConfig,
+			string(openrtb_ext.BidderAdkernel):         syncConfig,
+			string(openrtb_ext.BidderAdkernelAdn):      syncConfig,
+			string(openrtb_ext.BidderAdpone):           syncConfig,
+			string(openrtb_ext.BidderAdtelligent):      syncConfig,
+			string(openrtb_ext.BidderAdvangelists):     syncConfig,
+			string(openrtb_ext.BidderAppnexus):         syncConfig,
+			string(openrtb_ext.BidderBeachfront):       syncConfig,
+			string(openrtb_ext.BidderFacebook):         syncConfig,
+			string(openrtb_ext.BidderBrightroll):       syncConfig,
+			string(openrtb_ext.BidderConsumable):       syncConfig,
+			string(openrtb_ext.BidderConversant):       syncConfig,
+			string(openrtb_ext.BidderDatablocks):       syncConfig,
+			string(openrtb_ext.BidderEmxDigital):       syncConfig,
+			string(openrtb_ext.BidderEPlanning):        syncConfig,
+			string(openrtb_ext.BidderGrid):             syncConfig,
+			string(openrtb_ext.BidderGumGum):           syncConfig,
+			string(openrtb_ext.BidderImprovedigital):   syncConfig,
+			string(openrtb_ext.BidderIx):               syncConfig,
+			string(openrtb_ext.BidderLifestreet):       syncConfig,
+			string(openrtb_ext.BidderLockerDome):       syncConfig,
+			string(openrtb_ext.BidderMgid):             syncConfig,
+			string(openrtb_ext.BidderOpenx):            syncConfig,
+			string(openrtb_ext.BidderPubmatic):         syncConfig,
+			string(openrtb_ext.BidderPulsepoint):       syncConfig,
+			string(openrtb_ext.BidderRhythmone):        syncConfig,
+			string(openrtb_ext.BidderRTBHouse):         syncConfig,
+			string(openrtb_ext.BidderRubicon):          syncConfig,
+			string(openrtb_ext.BidderSharethrough):     syncConfig,
+			string(openrtb_ext.BidderSomoaudience):     syncConfig,
+			string(openrtb_ext.BidderSovrn):            syncConfig,
+			string(openrtb_ext.BidderSynacormedia):     syncConfig,
+			string(openrtb_ext.BidderTriplelift):       syncConfig,
+			string(openrtb_ext.BidderTripleliftNative): syncConfig,
+			string(openrtb_ext.Bidder33Across):         syncConfig,
+			string(openrtb_ext.BidderSonobi):           syncConfig,
+			string(openrtb_ext.BidderVrtcal):           syncConfig,
+			string(openrtb_ext.BidderYieldmo):          syncConfig,
+			string(openrtb_ext.BidderGamma):            syncConfig,
+			string(openrtb_ext.BidderVisx):             syncConfig,
+			string(openrtb_ext.BidderGamoshi):          syncConfig,
+			string(openrtb_ext.BidderUnruly):           syncConfig,
+			string(openrtb_ext.BidderVerizonMedia):     syncConfig,
+			string(openrtb_ext.BidderEngageBDR):        syncConfig,
 		},
 	}