# Identity verification: user_hash and the company signature

Introduce the signed-in user and their company to the widget with proof: user_hash is computed on the server and the company id is part of the signature; widget, Node.js, Go, PHP and curl samples.

URL: https://getclickfix.com/docs/integration-guide/identity?lang=en

`user_hash` is the only way to introduce a signed-in user to the widget with proof:

```text
# user that belongs to a company (identify sends company.id): both ids are signed together
user_hash = hex(HMAC-SHA256(widget_identity_secret, user_id + "\n" + company_id))

# no company: the user id alone
user_hash = hex(HMAC-SHA256(widget_identity_secret, user_id))
```

`user_id` is the user's id in **your** system and is the external id of the next section; `company_id` is the company's id in your system and goes to `identify` as `company: { id }`. The value is computed **on your server only**; your site and your mobile app receive the finished `user_hash`, never the secret. A contact verified with a valid `user_hash` gets the **Verified** badge in the panel and their anonymous history moves to the same contact. When the company id is signed, the contact is bound to that company by the signed value, the company gets the **Verified** badge in the panel too, and a verified user cannot claim another company; the `identify` event reports `company_verified: true`. When `identify` is rejected the widget keeps working but the user stays anonymous, and the `error` event carries `WIDGET_IDENTITY_DISABLED` (no secret generated yet) or `WIDGET_IDENTITY_INVALID` (mismatch, including a hash that signs another company id). Neither id may contain a line break (`\n`).

**Migration.** The original `user_hash` over `user_id` alone keeps working (backward compatible). When such a call also carries `company.id`, the user is still verified but the company id is **not linked and not stored**: `company.name` and `company.domain` are used only as matching hints, the widget prints a fixed console warning and the `identify` event reports `company_verified: false`. To bind the company by its id, add `company_id` to the signature on your server: Node `identityHash(secret, userId, companyId)`, Go `IdentityCompanyHash(secret, userID, companyID)`; the PHP and Laravel helpers get the same formula in their own releases.

{/* example: widget/01-identify.js */}

```js tab="Widget"
// Adım 1: sunucunuzdan aldığınız user_hash ile giriş yapmış kullanıcıyı tanıtın.
// @ts-check
ClickFix("boot", { widgetId: "WIDGET_ID", locale: "tr" });

fetch("/api/support-session") // sizin ucunuz: { user_id, user_hash, company_id } döner
  .then((r) => r.json())
  .then((session) =>
    ClickFix("identify", {
      user_id: session.user_id, // kişinin dış kimliği
      // Firma ile: hex(HMAC-SHA256(anahtar, user_id + "\n" + company_id)), sunucuda.
      // Firmasız: hex(HMAC-SHA256(anahtar, user_id)).
      user_hash: session.user_hash,
      name: "Ayşe Yılmaz",
      email: "ayse@example.com",
      company: { id: session.company_id }, // imzadaki firma kimliği
    }),
  );

ClickFix("on", "error", (e) => {
  if (e.code === "WIDGET_IDENTITY_INVALID")
    console.warn("user_hash eşleşmedi:", e.message);
});
```

{/* example: node/01-user-hash.ts */}

```ts tab="Node.js"
// Adım 1: user_hash sunucuda hesaplanır ve giriş yapmış kullanıcıya verilir.
import { identityHash } from "@getclickfix/node";

// Panel > Web widget > Güvenlik > Kimlik doğrulama anahtarı (bir kez gösterilir)
const secret = process.env.CLICKFIX_IDENTITY_SECRET!;

// Oturum açmış kullanıcının sizin sisteminizdeki kimliği (dış kimlik) ve
// firmasının kimliği (identify'daki company.id). Firma verilirse imza ikisini
// birden kapsar: hex(HMAC-SHA256(anahtar, user_id + "\n" + company_id)).
export function userHashFor(userId: string, companyId?: string): string {
  return identityHash(secret, userId, companyId);
}

// Örnek: /api/support-session uç noktanız bunu döner, tarayıcı identify'a verir
console.log(
  JSON.stringify({
    user_id: "1001",
    user_hash: userHashFor("1001"), // firmasız: eski formül
    company_id: "F-77",
    company_user_hash: userHashFor("1001", "F-77"), // company: { id: "F-77" } ile gönderilir
  }),
);
```

{/* example: go/user_hash.go */}

```go tab="Go"
package main

import (
	"context"
	"encoding/json"
	"os"

	clickfix "github.com/ClickSolveMainTR/clickfix-sdks/go"
)

// Adım 1: user_hash sunucuda hesaplanır; anahtar tarayıcıya ve uygulamaya gitmez.
func userHash(ctx context.Context) error {
	secret := os.Getenv("CLICKFIX_IDENTITY_SECRET") // Panel > Web widget > Güvenlik
	userID := "1001"                                // kullanıcının sizin sisteminizdeki kimliği
	companyID := "F-77"                             // firmasının kimliği (identify: company.id)
	return json.NewEncoder(os.Stdout).Encode(map[string]string{
		"user_id":   userID,
		"user_hash": clickfix.IdentityHash(secret, userID), // firmasız: eski formül
		// Firma ile: imza user_id + "\n" + company_id üzerinden; company: { id } ile gönderilir.
		"company_id":        companyID,
		"company_user_hash": clickfix.IdentityCompanyHash(secret, userID, companyID),
	})
}
```

{/* example: php/01-user-hash.php */}

```php tab="PHP"
<?php
// Adım 1: user_hash sunucuda hesaplanır ve giriş yapmış kullanıcıya verilir.
require __DIR__.'/vendor/autoload.php'; // composer require clickfix/sdk

use ClickFix\UserHash;

// Panel > Web widget > Güvenlik > Kimlik doğrulama anahtarı (bir kez gösterilir)
$secret = getenv('CLICKFIX_IDENTITY_SECRET');

// Oturum açmış kullanıcının sizin sisteminizdeki kimliği (dış kimlik)
function userHashFor(string $secret, string $userId): string
{
    return UserHash::make($secret, $userId);
}

// Örnek: /api/support-session uç noktanız bunu döner, tarayıcı identify'a verir
echo json_encode(['user_id' => '1001', 'user_hash' => userHashFor($secret, '1001')]), PHP_EOL;
```

{/* example: curl/01-user-hash.sh */}

```bash tab="curl"
# Adım 1: user_hash = hex(HMAC-SHA256(kimlik_anahtari, user_id)); yalnız sunucuda.
USER_ID="1001"
printf '%s' "$USER_ID" | openssl dgst -sha256 -hmac "$CLICKFIX_IDENTITY_SECRET" | sed 's/^.* //'
# Firma ile (company: { id }): imza user_id + "\n" + company_id üzerinden hesaplanır.
COMPANY_ID="F-77"
printf '%s\n%s' "$USER_ID" "$COMPANY_ID" | openssl dgst -sha256 -hmac "$CLICKFIX_IDENTITY_SECRET" | sed 's/^.* //'
```

If you **Rotate** the secret, values made with the old one stop working; update the secret on your server at the same time. On sites that need cookie consent pass it with `boot({ consent })` or `ClickFix("consent", true)`; nothing is written to the browser before consent.
