# ITP Mitigation

:::info
Available since Jitsu v2.8.0 and npm packages v1.9.7
:::

## Safari Intelligent Tracking Prevention

Intelligent Tracking Prevention (ITP) is an automatic feature of the Safari web browser designed to limit user tracking. 

ITP employs multiple measures to achieve this, one of which is the limited lifespan of first-party cookies

:::info
In Safari browser first-party cookies will be deleted after 7 days without access.
:::
Since Jitsu relies on first-party cookies to store anonymous user IDs, ITP may negatively affect the quality of data collected by Jitsu.

For example, if some user returns to a particular site after a pause of longer than 7 days, he will receive a new anonymous ID and may appear as a new user (at least unless `identify` is call is used).

## Mitigation

Not all first-party cookies are subject to the ITP's 7-day expiry limit.

Cookies with the `HttpOnly` flag, served directly from the customer’s website server, will not be removed by ITP.

To enable ITP mitigation, customers need to add a simple service (endpoint) to their website that adheres to Jitsu’s specifications.

### Server setup

Jitsu ID endpoint must be added to customer's website.

#### Endpoint requirements: 

1. ID endpoint must respond on the same domain as the website. (That can be a subdomain only if it resolves to the same IP address as the main domain)
2. Respond to `GET` HTTP method.
3. Read **domain** parameter from request query string.
4. Read `__eventn_id` or `__eventn_id_srvr` HTTP request cookie as a source of **anonymousId** value
5. If it couldn't acquire value for **anonymousId** from either of these cookies, generate a new one (UUID string)
6. Add `__eventn_id` and `__eventn_id_srvr` cookies to "Set-Cookie" headers with the following parameters:<br/>`Set-Cookie:
   __eventn_id=anonymousId; Domain=domain; Max-Age=157680000; Path=/; SameSite=None; Secure;`<br/>`Set-Cookie:
   __eventn_id_srvr=anonymousId; Domain=domain; Max-Age=157680000; Path=/; SameSite=None; Secure; httpOnly=true;`<br/>where **domain** was acquired from query string and **anonymousId** was set previously
7. Send response with status `200` and JSON payload: `{ "anonymousId": anonymousId }`

#### Optional part:

It is also possible to protect the userId cookie from expiration, but a better approach is to `identify` the user on every new session.

8. Read `__eventn_uid` or `__eventn_uid_srvr` HTTP request cookie as a source of **userId** value
9. If it couldn't acquire value for **userId** skip the rest and do nothing
10. Add `__eventn_uid` and `__eventn_uid_srvr` cookies to "Set-Cookie" headers with the following parameters:<br/>`Set-Cookie:
   __eventn_uid=userId; Domain=domain; Max-Age=157680000; Path=/; SameSite=None; Secure;`<br/>`Set-Cookie:
   __eventn_uid=userId_srvr; Domain=domain; Max-Age=157680000; Path=/; SameSite=None; Secure; httpOnly=true;`<br/>where **domain** was acquired from query string and **userId** was set previously
11. Change response with status `200` and JSON payload: `{ "anonymousId": anonymousId, "userId": userId }`

### Client setup

Jitsu client library must be configured to use ID endpoint with `idEndpoint` parameter:

<Tabs>
  <TabItem value="html" label="HTML snippet" default>

```html
<script async src="https://your-jitsu-domain.com/p.js" data-id-endpoint="/api/jitsu-id"></script>
```
  </TabItem>
  <TabItem value="npm" label="NPM">

```javascript
    const analytics = jitsuAnalytics({
        host: "https://your-jitsu-domain.com",
        // path or full URL of ID endpoint
        idEndpoint: "/api/jitsu-id",
    });
```
  </TabItem>
  <TabItem value="react" label="React">

```javascript
  <JitsuProvider options={{host: "https://your-jitsu-domain.com", idEndpoint: "/api/jitsu-id"}}>
    <ChildComponent/>
  </JitsuProvider>
```
  </TabItem>
</Tabs>

### Server code example

Here is an example of ID endpoint implemented in Typescript language for Next.js 14 framework:

```typescript
import {NextRequest, NextResponse} from 'next/server'
import {randomUUID} from "crypto";

const USER_COOKIE = "__eventn_uid";
const ANON_COOKIE = "__eventn_id";

function getDomain(request: NextRequest) {
   let domain = request.nextUrl.searchParams.get("domain");
   if (domain) {
      return domain;
   }
   domain = request.headers.get("host")?.toString() ?? "";
   if (domain.startsWith("localhost")) return "localhost";
   return domain;
}

function renewCookies(request: NextRequest, headers: Headers, browserName: string, serverName: string, generateNew: boolean) {
   const cookie = request.cookies.get(browserName) || request.cookies.get(serverName);
   let cookieValue = cookie?.value
   if (!cookieValue) {
      if (!generateNew) return;
      cookieValue = randomUUID();
   }
   const secure = request.headers.get("x-forwarded-proto") === "https" ? " Secure;" : ""
   const sameSite = ` SameSite=${secure ? "None" : "Lax"};`
   const maxAge = 31_536_000 * 5; // 5 years in seconds
   const domain = getDomain(request);
   headers.append("Set-Cookie", `${browserName}=${cookieValue}; Max-Age=${maxAge}; Domain=${domain}; Path=/;${sameSite}${secure}`)
   headers.append("Set-Cookie", `${serverName}=${cookieValue}; Max-Age=${maxAge}; Domain=${domain}; Path=/;${sameSite}${secure} httpOnly=true;`)
   return cookieValue;
}

export async function GET(request: NextRequest) {
   const headers = new Headers({
      'Cache-Control': "must-revalidate,no-cache,no-store",
      'Content-Type': "application/json"
   })
   const anonymousId = renewCookies(request, headers, ANON_COOKIE, `${ANON_COOKIE}_srvr`, true);
   const userId = renewCookies(request, headers, USER_COOKIE, `${USER_COOKIE}_srvr`, false);
   const payload = {
      anonymousId: anonymousId,
      userId: userId
   }
   return new NextResponse(JSON.stringify(payload), {
      status: 200,
      headers: headers
   });
}
```