Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added support for ECIDs passed via the adobe_mc query string parameter. Added appendIdentityToUrl command to enable cross-domain identity sharing. #862

Merged
merged 19 commits into from
May 12, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
07d5b7d
parse the query string for adobe_mc and add unit tests
jonsnyder Mar 18, 2022
797cc4b
Fixes after testing in the sandbox
jonsnyder Mar 18, 2022
7add720
Create functional tests and fix hasIdentity error in getIdentity payload
jonsnyder Mar 22, 2022
09a80bc
Merge remote-tracking branch 'origin/main' into queryStringIdentity
jonsnyder Mar 22, 2022
1277323
Add a few more functional tests, extract common functionality from tests
jonsnyder Mar 22, 2022
803f5c0
Add logging to AddQueryStringIdentityToPayload, add additional unit t…
jonsnyder Mar 22, 2022
1684ecf
Add better comments. Use constant for TTL
jonsnyder Apr 1, 2022
c1d7318
Write a different AMCV cookie if the ECID has changed. Add failing te…
jonsnyder Apr 1, 2022
0af3b6f
Add comment and update logging message from code review
jonsnyder Apr 1, 2022
acb4eb2
Add appendIdentityToUrl command with updated unit tests and sandbox.
jonsnyder Apr 1, 2022
cf9dbb5
Add functional tests for appendIdentityToUrl, round the timestamp
jonsnyder Apr 4, 2022
63838f5
Use alloyio2.com as the cross-domain link
jonsnyder Apr 4, 2022
e6b85f5
revert sandbox index.html back to original config
jonsnyder Apr 4, 2022
08c7d13
Remove unresolved promise error in test for Firefox and Safari
jonsnyder Apr 4, 2022
bdf2051
Add test from mobile team url
jonsnyder Apr 20, 2022
5638ee3
Add idOverwriteEnabled config
jonsnyder May 10, 2022
69a1340
Merge remote-tracking branch 'origin/main' into queryStringIdentity2
jonsnyder May 10, 2022
d394a21
Skip test that is waiting on Konductor changes
jonsnyder May 10, 2022
136f6e6
set idOverwriteEnabled default to false, Updated sandbox to help with…
jonsnyder May 12, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions sandbox/public/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -79,12 +79,14 @@
location.host.indexOf("alloyio.com") !== -1
? "firstparty.alloyio.com"
: undefined,
edgeBasePath: getUrlParameter("edgeBasePath") || "ee",
edgeConfigId: "bc1a10e0-aee4-4e0e-ac5b-cdbb9abbec83",
orgId: "5BFE274A5F6980A50A495C08@AdobeOrg",
debugEnabled: true,
idMigrationEnabled: !(
getUrlParameter("idMigrationEnabled") === "false"
),
idOverwriteEnabled: getUrlParameter("idOverwriteEnabled") === "true",
prehidingStyle: ".personalization-container { opacity: 0 !important }",
onBeforeEventSend: function(options) {
const xdm = options.xdm;
Expand Down
5 changes: 5 additions & 0 deletions sandbox/src/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import DualTag from "./DualTag";
import RedirectOffers from "./RedirectOffers";
import RedirectedNewPage from "./RedirectedNewPage";
import PersonalizationAnalyticsClientSide from "./PersonalizationAnalyticsClientSide";
import Identity from "./Identity";

function BasicExample() {
return (
Expand Down Expand Up @@ -74,6 +75,9 @@ function BasicExample() {
<li>
<a href="/redirectOffers">Redirect Offers</a>
</li>
<li>
<a href="/identity">Identity</a>
</li>
</ul>

<hr />
Expand All @@ -97,6 +101,7 @@ function BasicExample() {
<Route path="/dualTag" component={DualTag} />
<Route path="/redirectOffers" component={RedirectOffers} />
<Route path="/redirectedNewPage" component={RedirectedNewPage} />
<Route path="/identity" component={Identity} />
</div>
</Router>
);
Expand Down
203 changes: 203 additions & 0 deletions sandbox/src/Identity.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,203 @@
/*
Copyright 2022 Adobe. All rights reserved.
This file is licensed to you under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. You may obtain a copy
of the License at http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software distributed under
the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
OF ANY KIND, either express or implied. See the License for the specific language
governing permissions and limitations under the License.
*/

import React, { useEffect, useState } from "react";

const getQueryStringParameter = key => {
var searchParams = new URLSearchParams(window.location.search);
return searchParams.get(key);
};

const urlWithUpdatedQueryStringParameter = (key, value, defaultValue) => {
var searchParams = new URLSearchParams(window.location.search);
if (value !== defaultValue) {
searchParams.set(key, value);
} else {
searchParams.delete(key);
}
return window.location.pathname + "?" + searchParams;
};

const readCookies = () => {
const cookies = {};
document.cookie.split(";").forEach(function(c) {
const ct = c.trim();
const index = ct.indexOf("=");
const key = ct.slice(0, index);
const value = ct.slice(index + 1);
cookies[key] = value;
});
return cookies;
};
const readIdentityCookie = () => {
const cookies = readCookies();
const value = cookies["kndctr_5BFE274A5F6980A50A495C08_AdobeOrg_identity"];
if (!value) {
return "None";
}
const decoded = Buffer.from(value, "base64").toString();
return decoded.substring(2, 40);
};

const getIdentity = setIdentity => () => {
window.alloy("getIdentity", { namespaces: ["ECID"] }).then(function(result) {
if (result.identity) {
console.log(
"Sandbox: Get Identity command has completed.",
result.identity.ECID
);
setIdentity(result.identity.ECID);
} else {
console.log(
"Sandbox: Get Identity command has completed but no identity was provided in the result (possibly due to lack of consent)."
);
setIdentity("No Identity");
}
});
};

const sendEvent = setIdentity => () => {
window.alloy("sendEvent", {}).then(getIdentity(setIdentity));
};

const setConsent = setIdentity => () => {
window
.alloy("setConsent", {
consent: [
{
standard: "Adobe",
version: "2.0",
value: {
collect: {
val: "y"
}
}
}
]
})
.then(getIdentity(setIdentity));
};

const appendIdentityToUrl = event => {
const url = event.target.href;
event.preventDefault();
window.alloy("appendIdentityToUrl", { url }).then(({ url }) => {
document.location = url;
});
};

const linkedUrl = new URL(window.location.href);
linkedUrl.protocol = "https";
linkedUrl.port = "";
linkedUrl.host =
linkedUrl.host === "alloyio2.com" ? "alloyio.com" : "alloyio2.com";

const idOverwriteEnabled =
getQueryStringParameter("idOverwriteEnabled") === "true";

export default function Identity() {
const [originalIdentityCookie, setOriginalIdentityCookie] = useState("");
const [currentIdentityCookie, setCurrentIdentityCookie] = useState("");
const [identity, setIdentity] = useState("");

useEffect(() => {
const ecid = readIdentityCookie();
setOriginalIdentityCookie(ecid);
setCurrentIdentityCookie(ecid);
}, []);

const wrappedSetIdentity = ecid => {
setIdentity(ecid);
setCurrentIdentityCookie(readIdentityCookie());
};

return (
<div>
<h1>Identity</h1>
<section>
This page demonstrates recieving or sending identity within the URL. The
current value of the `idOverwriteEnabled` configuration parameter is
shown in the first table. No calls to experience edge are made until you
press one of the buttons below. The second table shows the current and
original identities. If you click on the link on the bottom, it will
generate a link to another domain with the ID included in the URL.
</section>
<section>
<table>
<tbody>
<tr>
<td>idOverwriteEnabled</td>
<td>{idOverwriteEnabled ? "true" : "false"}</td>
<td>
{idOverwriteEnabled ? (
<a
href={urlWithUpdatedQueryStringParameter(
"idOverwriteEnabled",
"false",
"false"
)}
>
Disable
</a>
) : (
<a
href={urlWithUpdatedQueryStringParameter(
"idOverwriteEnabled",
"true",
"false"
)}
>
Enable
</a>
)}
</td>
</tr>
</tbody>
</table>
</section>
<section>
<button onClick={getIdentity(wrappedSetIdentity)}>Get Identity</button>
<button onClick={sendEvent(wrappedSetIdentity)}>Send Event</button>
<button onClick={setConsent(wrappedSetIdentity)}>Set Consent</button>
</section>
<section>
<table>
<tbody>
<tr>
<td>Original Identity Cookie</td>
<td>
<pre>{originalIdentityCookie}</pre>
</td>
</tr>
<tr>
<td>Current Identity Cookie</td>
<td>
<pre>{currentIdentityCookie}</pre>
</td>
</tr>
<tr>
<td>Identity from Web SDK</td>
<td>
<pre>{identity}</pre>
</td>
</tr>
</tbody>
</table>
</section>
<section>
<a href={linkedUrl.toString()} onClick={appendIdentityToUrl}>
Cross domain linked identity.
</a>
</section>
</div>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/*
Copyright 2022 Adobe. All rights reserved.
This file is licensed to you under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. You may obtain a copy
of the License at http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software distributed under
the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
OF ANY KIND, either express or implied. See the License for the specific language
governing permissions and limitations under the License.
*/

import { objectOf, string } from "../../../utils/validation";
/**
* Verifies user provided event options.
* @param {*} options The user event options to validate
* @returns {*} Validated options
*/
export default objectOf({
url: string()
.required()
.nonEmpty()
})
.required()
.noUnknownFields();
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/*
Copyright 2022 Adobe. All rights reserved.
This file is licensed to you under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. You may obtain a copy
of the License at http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software distributed under
the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
OF ANY KIND, either express or implied. See the License for the specific language
governing permissions and limitations under the License.
*/
const URL_REGEX = /^([^?#]*)(\??[^#]*)(#?.*)$/;

const getSeparator = queryString => {
if (queryString === "") {
return "?";
}
if (queryString === "?") {
return "";
}
return "&";
};

export default ({ dateProvider, orgId }) => (ecid, url) => {
const ts = Math.round(dateProvider().getTime() / 1000);
const adobemc = encodeURIComponent(`TS=${ts}|MCMID=${ecid}|MCORGID=${orgId}`);
const [, location, queryString, fragment] = url.match(URL_REGEX);
const separator = getSeparator(queryString);
return `${location}${queryString}${separator}adobe_mc=${adobemc}${fragment}`;
};
3 changes: 2 additions & 1 deletion src/components/Identity/configValidators.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ import { boolean } from "../../utils/validation";

const configValidators = {
thirdPartyCookiesEnabled: boolean().default(true),
idMigrationEnabled: boolean().default(true)
idMigrationEnabled: boolean().default(true),
idOverwriteEnabled: boolean().default(false)
};

export default configValidators;
24 changes: 23 additions & 1 deletion src/components/Identity/createComponent.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
import { assign } from "../../utils";
import getIdentityOptionsValidator from "./getIdentity/getIdentityOptionsValidator";
import appendIdentityToUrlOptionsValidator from "./appendIdentityToUrl/appendIdentityToUrlOptionsValidator";

export default ({
addEcidQueryToPayload,
addQueryStringIdentityToPayload,
ensureSingleIdentity,
setLegacyEcid,
handleResponseForIdSyncs,
getEcidFromResponse,
getIdentity,
consent
consent,
appendIdentityToUrl,
logger
}) => {
let ecid;
let edge = {};
Expand All @@ -18,6 +22,7 @@ export default ({
// Querying the ECID on every request to be able to set the legacy cookie, and make it
// available for the `getIdentity` command.
addEcidQueryToPayload(request.getPayload());
addQueryStringIdentityToPayload(request.getPayload());
return ensureSingleIdentity({ request, onResponse, onRequestFailure });
},
onResponse({ response }) {
Expand Down Expand Up @@ -55,6 +60,23 @@ export default ({
};
});
}
},
appendIdentityToUrl: {
optionsValidator: appendIdentityToUrlOptionsValidator,
run: options => {
return consent
.withConsent()
.then(() => {
return ecid ? undefined : getIdentity(options.namespaces);
})
.then(() => {
return { url: appendIdentityToUrl(ecid, options.url) };
})
.catch(error => {
logger.warn(`Unable to append identity to url. ${error.message}`);
return options;
});
}
}
}
};
Expand Down
2 changes: 1 addition & 1 deletion src/components/Identity/createLegacyIdentity.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ export default ({ config, getEcidFromVisitor }) => {
return Promise.resolve();
},
setEcid(ecid) {
if (idMigrationEnabled && !cookieJar.get(amcvCookieName)) {
if (idMigrationEnabled && getEcidFromLegacyCookies() !== ecid) {
cookieJar.set(amcvCookieName, `MCMID|${ecid}`, {
domain: apexDomain,
// Without `expires` this will be a session cookie.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ governing permissions and limitations under the License.

import {
createAddIdentity,
createHasIdentity,
createRequestPayload
} from "../../../utils/request";

Expand All @@ -25,6 +26,7 @@ export default namespaces => {
};
return createRequestPayload({
content,
addIdentity: createAddIdentity(content)
addIdentity: createAddIdentity(content),
hasIdentity: createHasIdentity(content)
});
};
Loading