Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Client Identifier Laravel Package

a4sex/client-identifier

Lightweight Laravel/PHP package for reliably identifying clients—devices, browsers, or API consumers—by generating and resolving stable client identifiers from request headers and fingerprints, with a simple API for middleware, Eloquent models, and service integration.

View on GitHub
Deep Wiki
Context7

Getting Started

Install and locate the moving parts

composer require a4sex/client-identifier

This is a framework-agnostic php/library package — there is no Laravel service provider, no config file, and no migrations shipped with it. Before writing any application code, read src/ and look for three things:

  1. The main contract (typically ClientIdentifierInterface or similar) — this is what you type-hint everywhere.
  2. The generator/strategy classes (UUID, random string, hashed fingerprint).
  3. The storage/persistence adapter (cookie, header, in-memory) — this determines where the identifier actually survives between requests.

If the README is thin (likely, given zero stars/dependents), the interfaces in src/ are the documentation. Two minutes reading them saves an hour of guessing method names.

Bind it once, in a service provider

Because the package is unopinionated, you own the wiring. Keep it to a single singleton so the identifier is resolved once per request rather than regenerated on every call:

// app/Providers/AppServiceProvider.php
use A4sex\ClientIdentifier\ClientIdentifierInterface;

public function register(): void
{
    $this->app->singleton(ClientIdentifierInterface::class, function ($app) {
        return new CookieClientIdentifier(/* generator, cookie name, ttl */);
    });
}

If the constructor needs the current Request, prefer resolving through a middleware instead of binding $app['request'] at boot — the request object is not reliably populated at container-build time.

First use case: anonymous visitor identity

The canonical day-one use for a package like this is giving every visitor a stable ID before they authenticate, so a guest cart, analytics session, and A/B bucket can all key off the same value. Wire a middleware that resolves the identifier, stashes it on the request, and writes the cookie on the response:

// app/Http/Middleware/ResolveClientIdentifier.php
public function handle($request, Closure $next)
{
    $id = $this->identifier->resolve($request);   // reads cookie/header, or mints a new one
    $request->attributes->set('client_id', $id);

    $response = $next($request);

    return $this->identifier->attach($response, $id); // sets cookie if newly minted
}

Register it in the web group, after StartSession, before anything that needs the ID.


Implementation Patterns

1. Middleware + request attribute (the default pattern)

Resolve once, read everywhere via $request->attributes->get('client_id') — no repeated cookie parsing, no chance of a second generation inside the same request. Never inject the identifier into controllers via the container without a request scope, or long-running workers will hand out a stale ID.

2. Guest cart / pre-login state stitching

// on login
$guestId = $request->attributes->get('client_id');

if ($cart = Cart::where('client_id', $guestId)->first()) {
    $cart->update(['user_id' => auth()->id(), 'client_id' => null]);
}

This is the highest-value integration. It lets you defer the "merge anonymous data into the account" step until login rather than prompting for an account up front.

3. Idempotency keys for public endpoints

For unauthenticated POSTs (newsletter signup, lead forms, checkout start), the client identifier plus a route name makes a cheap dedupe key:

$key = 'lead:' . $request->attributes->get('client_id') . ':' . $request->path();
Cache::add($key, true, now()->addMinutes(10)) or abort(429);

4. Attribution and analytics

Attach the ID to queued events so downstream jobs can correlate without re-parsing cookies:

event(new VisitorTracked($clientId, $payload)); // serialize the ID, not the Request

Pass the value into jobs, never the request or the identifier object — serializing the resolver into a queue payload is a classic source of broken jobs.

5. Referral / invite links

Treat the identifier as the "who referred" side of a link: ?ref={client_id}. Because it's stable and cookie-backed, you get multi-touch attribution for free. Store it in a signed cookie or validate the format before persisting, since it arrives from user input.

6. Storage swap via the interface

If the package's default store is a cookie, you will eventually want a server-side store (Redis/DB) so the ID can't be cleared by the user. Implement the package's storage interface and rebind it — that's the intended extension point, and it's the reason to prefer the interface type-hint over the concrete class everywhere.

7. Testing

Never let tests mint real identifiers. Bind a deterministic fake in TestCase::setUp():

$this->app->instance(ClientIdentifierInterface::class, new FixedClientIdentifier('test-client'));

Then assert on the attribute and on the outgoing cookie rather than on generated randomness.


Gotchas and Tips

No vendor:publish will do anything. There's no config or migration in this package. If you need configuration, create your own config/client-identifier.php and read it in your binding.

Two writers, one cookie. If the library sets cookies via raw setcookie() / header(), it bypasses Laravel's CookieJar and its encryption. You'll see either a plaintext cookie alongside an encrypted one, or a value that never round-trips. Inspect the response headers in a test before trusting it; if it's the raw approach, wrap the value yourself and use Cookie::queue().

SameSite and Secure defaults bite in production. Laravel 10+ defaults to SameSite=Lax, which is fine for top-level navigation but drops the cookie on cross-site iframe/XHR. If you embed checkout or run a widget, you need SameSite=None; Secure — and the library almost certainly doesn't expose that, so patch it at the response layer.

Don't regenerate on every request. The most common bug with identifier libraries is a misconfigured store that returns null, so the resolver mints a fresh ID each time. Symptom: guest carts orphaned, analytics sessions of length 1. Add a debug log at resolution time and assert continuity across two requests in a feature test.

Parallel requests from a brand-new visitor race. Two simultaneous requests both see "no cookie," both mint an ID, and the last response wins. Accept it (and merge if you can) or mint the ID in a single early middleware before anything fans out.

Never use the identifier for authorization. It's client-supplied and clearable. It is an analytics/correlation key, not an identity — anything security-relevant must go through Laravel's session/auth.

It's personal data under GDPR. A persistent device identifier is pseudonymous personal data. Decide a retention window explicitly and document it; a cookie TTL buried in an unmaintained library is not a policy.

Maintenance risk is real. Zero stars, zero dependents, no Laravel integration. Pin the version in composer.json, and if the package is small enough (likely), consider copying the relevant classes into app/Support/ rather than depending on an unmaintained transitive dependency. That removes a supply-chain and abandonment risk for what is probably 200 lines of code.

Debugging checklist when the ID looks wrong: (1) log every call to the resolver, (2) dd($request->cookies->all()) at the top of the middleware, (3) check the raw Set-Cookie header on the response, (4) confirm middleware ordering relative to StartSession and EncryptCookies. In that order — it isolates generation vs. persistence vs. encryption quickly.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
se7enxweb/flysystem
lampwick/laravel
jeslage/statamic-emojis
micahdshackelford/rmap-laravel
draftsmaninc/draftsman
square-bit/laravel-volition
himito/laravel-mailman
htinlynn/genius-helper
maco-studios/hephaestus
elmogy/larafast
xeviant/laravel-paystack
gorilladash/laravel-website-sdk
martialbe/flysystem-oss
chondal/3312client
jeroen-g/flickr
opscale-co/nova-genbi
itsjustvita/laravel-retention
tequilarapido/trackit
gregsphotography/site-builder-filament
cabaoctavio/laravel-query-log