If your website or app already has its own sign-up and login, for example a SaaS product built with Laravel, Rails or Node.js, you keep it. UniSignIn connects to it so every user has one profile with their account details, their data from your system and what they do in your app.
There are three parts:
You can then use all of it in Segments, Orchestration, Experiences and Data Exchange exports.
Connect > SSO Token. You get an SSO_PUBLIC_KEY and an SSO_PRIVATE_KEY.Connect > API Keys and copy its Secret Key, see API Keys.Keep the SSO private key and the API Secret Key on your server only. Never put them in page code or an app.
The token is your user's email and profile, signed with your SSO private key and the current time, see SSO for the format. A token is valid for 10 minutes, so create it when you render the page. In PHP:
function unisigninSSOToken(string $email, array $profile, string $registeredAt): string
{
$ts = time();
$profileJson = json_encode($profile);
return base64_encode(json_encode([
'email' => $email,
'ts' => $ts,
'profile' => $profileJson,
'reg_date' => $registeredAt,
'public_key' => getenv('UNISIGNIN_SSO_PUBLIC_KEY'),
'signature' => hash('sha512', $email . '-' . $ts . '-' . hash('sha256', $profileJson) . '-' . getenv('UNISIGNIN_SSO_PRIVATE_KEY')),
]));
}
$token = unisigninSSOToken($user->email, [
'first_name' => $user->first_name,
'last_name' => $user->last_name,
'plan' => $user->plan,
'company' => $user->company,
], $user->created_at->toIso8601String());
Each login updates the profile fields in profile. Add custom fields first in Data Management > User Data > Data Schema, see Profile Fields.
On pages where your user is logged in, check whether they are also logged into UniSignIn, and log them in if not. To update their profile when it changes in your system, log them in again:
window.unisignin = window.unisignin || {}
window.unisignin.cmd = window.unisignin.cmd || []
window.unisignin.cmd.push([
'getUser',
function (user) {
if (!user.isLogin || localStorage.getItem('unisignin_sso') !== 'SSO_VERSION') {
window.unisignin.cmd.push([
'sso_login',
'SSO_TOKEN',
function (result) {
if (result.status) localStorage.setItem('unisignin_sso', 'SSO_VERSION')
},
])
}
},
])
Replace SSO_TOKEN with the token from your server, and SSO_VERSION with a hash of the user's email and profile, so they log in again when either changes.
The first time a user logs in, UniSignIn creates their account with the email in the token. Users are matched by email, so use the same email as in your system. Add reg_date, the date the user registered in your system, so their UniSignIn registration date matches your records. Use an ISO 8601 date with a time zone, e.g. 2024-05-01T09:30:00Z, or a Unix timestamp.
When the user logs out of your app, log them out of UniSignIn too:
window.unisignin.cmd.push(['client_logout'])
The profile in the SSO token is updated when the user logs in. To update users who aren't logged in, such as after a billing change, or to load all your existing users, push the data from your server.
Data Management > Data Exchange and click New.Import Data.email, and choose email as the UniSignIn field.plan to Plan. Map the date the user registered in your system, e.g. created_at, to Registration Date. It is used when the user is created in UniSignIn and doesn't change existing users. Add custom fields first in Data Management > User Data > Data Schema if you need them, see Profile Fields.Send one user or a list of up to 100 users per request:
curl -X POST https://secure.signinservice.com/api/v1/user/EXCHANGE_ID \
-H "Authorization: Bearer SECRET_KEY" \
-H "x-license: YOUR_LICENSE" \
-H "Content-Type: application/json" \
-d '[{"email": "user@unisignin.com", "created_at": "2024-05-01T09:30:00Z", "plan": "pro", "role": "admin", "company": "Acme"}]'
The response tells you how many users were updated and how many were new:
{ "status": 1, "updated": 1, "created": 0 }
Send data whenever it changes in your system, e.g. when a user upgrades their plan or changes their role. To load your existing users, send them in batches of 100.
Send an event when a user does something you want to segment or report on:
window.unisignin.cmd.push(['trace', 'project_created'])
window.unisignin.cmd.push(['trace', 'teammate_invited'])
Use short lowercase names. Events are then available in segments with Actions (last 30 days) contains project_created.
To label a user, add a tag:
window.unisignin.cmd.push(['tag', 'power-user'])
Tags appear in the Tags (last 30 days) profile field. See Events and the JavaScript API.
Share the SSO token with your layout for logged-in users:
// app/Providers/AppServiceProvider.php
use Illuminate\Support\Facades\View;
public function boot(): void
{
View::composer('layouts.app', function ($view) {
$user = auth()->user();
if (! $user) {
return $view->with('unisigninToken', null);
}
$ts = time();
$profile = json_encode([
'full_name' => $user->name,
'plan' => $user->plan,
'company' => $user->team?->name,
]);
$view->with('unisigninVersion', hash('sha256', $user->email . $profile));
$view->with('unisigninToken', base64_encode(json_encode([
'email' => $user->email,
'ts' => $ts,
'profile' => $profile,
'reg_date' => $user->created_at->toIso8601String(),
'public_key' => config('services.unisignin.sso_public'),
'signature' => hash('sha512', $user->email . '-' . $ts . '-' . hash('sha256', $profile) . '-' . config('services.unisignin.sso_private')),
])));
});
}
{{-- resources/views/layouts/app.blade.php --}}
@if ($unisigninToken)
<script>
window.unisignin = window.unisignin || {};
window.unisignin.cmd = window.unisignin.cmd || [];
window.unisignin.cmd.push(['getUser', function (user) {
if (!user.isLogin || localStorage.getItem('unisignin_sso') !== @json($unisigninVersion)) {
window.unisignin.cmd.push(['sso_login', @json($unisigninToken), function (result) {
if (result.status) localStorage.setItem('unisignin_sso', @json($unisigninVersion));
}]);
}
}]);
</script>
@endif
Push profile data when a user changes, from a queued job:
// app/Jobs/SyncUserToUniSignIn.php
use Illuminate\Support\Facades\Http;
public function handle(): void
{
Http::withToken(config('services.unisignin.secret'))
->withHeaders(['x-license' => config('services.unisignin.license')])
->post('https://secure.signinservice.com/api/v1/user/' . config('services.unisignin.exchange_id'), [[
'email' => $this->user->email,
'created_at' => $this->user->created_at->toIso8601String(),
'plan' => $this->user->plan,
'role' => $this->user->role,
'company' => $this->user->team?->name,
]])
->throw();
}
// app/Observers/UserObserver.php
public function saved(User $user): void
{
SyncUserToUniSignIn::dispatch($user);
}
Load existing users once with a console command:
User::query()->chunk(100, function ($users) {
Http::withToken(config('services.unisignin.secret'))
->withHeaders(['x-license' => config('services.unisignin.license')])
->post('https://secure.signinservice.com/api/v1/user/' . config('services.unisignin.exchange_id'),
$users->map(fn ($user) => ['email' => $user->email, 'created_at' => $user->created_at->toIso8601String(), 'plan' => $user->plan])->all());
});
Log out of UniSignIn on your logout page or button:
window.unisignin.cmd.push(['client_logout'])
Identity, consent, audience, and subscription insights for publishers. A few emails a month, no spam.