The signed-in visitor
When a visitor is signed in to your site, you can attach their visit to their identifier in your
system: all their page views and events in the visit carry it, including those from before
sign-in, and their previous visits under the same cookie are linked to them. The views expose it
as user_id and person.
1. Enable the feature
Section titled “1. Enable the feature”In the site’s Settings, check Accept signed-in visitors. As long as it is disabled, a received identity is ignored — the event is kept, without the identity — and counted as “Identity refused” in the Verification tab.
2. Sign a token, server-side
Section titled “2. Sign a token, server-side”The token is an HS256 JWT, signed by your server with the site secret (site Settings › Secret). The signature prevents a visitor from impersonating another: never sign it in the browser.
| Claim | Required | Role |
|---|---|---|
sub | yes | the visitor’s identifier in your system, 200 characters at most: stored as is in user_id |
exp | yes | the token’s expiry; an expired token is rejected |
Prefer an opaque identifier (a customer number, a UUID) to an email address: sub is
readable in the views, and therefore in eodia insights.
Node.js
Section titled “Node.js”import { SignJWT } from 'jose'
const secret = new TextEncoder().encode(process.env.EODIA_SITE_SECRET)
export async function eodiaIdentity(customerId) { return new SignJWT({}) .setProtectedHeader({ alg: 'HS256', typ: 'JWT' }) .setSubject(String(customerId)) .setIssuedAt() .setExpirationTime('12h') .sign(secret)}<?php// No library needed: an HS256 JWT can be signed in a few lines.function eodia_identity(string $customerId, string $secret, int $ttl = 43200): string { $b64 = fn (string $s) => rtrim(strtr(base64_encode($s), '+/', '-_'), '='); $header = $b64(json_encode(['alg' => 'HS256', 'typ' => 'JWT'])); $payload = $b64(json_encode(['sub' => $customerId, 'iat' => time(), 'exp' => time() + $ttl])); $signature = $b64(hash_hmac('sha256', "$header.$payload", $secret, true)); return "$header.$payload.$signature";}Python
Section titled “Python”import os, timeimport jwt # PyJWT
def eodia_identity(customer_id: str) -> str: now = int(time.time()) return jwt.encode( {"sub": str(customer_id), "iat": now, "exp": now + 12 * 3600}, os.environ["EODIA_SITE_SECRET"], algorithm="HS256", )3. Pass it to the script
Section titled “3. Pass it to the script”In the tag, if the page is rendered server-side:
<script defer src="https://stats.example.com/a.js" data-site="s_arvordemo001" data-identity="<?= htmlspecialchars($token) ?>"></script>Or later, at sign-in, in a single-page application:
EodiaAnalytics.identify(token) // at sign-inEodiaAnalytics.identify(null) // at sign-outWhat the views make of it
Section titled “What the views make of it”The views link identities together without rewriting anything in the raw data:
- within the visit:
user_idis the identity seen during the visit; all page views and all events of the visit carry it, including those from before sign-in; - from one visit to another:
personis the identity when the visitor’s cookie has signed in at least once, including for its earlier visits; otherwise, the visitor identifier. This is the column to count for “unique people”; - a cookie seen under two identities (a shared device) keeps the most recent one for
person, and the case is counted in the check figures of the Verification tab.
Without a measurement cookie, linking is limited to the day: the visitor hash changes every day.
The site secret
Section titled “The site secret”- Regenerate, in the site’s Settings, creates a new secret, shown only once. The old one remains accepted for 24 hours, giving you time to deploy the new one.
- The secret is encrypted in the database (AES-256-GCM) with the instance key,
SECRET_KEY.
Access and erasure requests from people identified in this way are handled from the site: see data subject rights.
eodia analytics is free software by Eodia.