Signing In as a Client
Hand a signed-in session to a client from your own system, without ever holding their password.
Overview
Two endpoints cover the moment a client moves from your system into theirs.
Use the second one when you already know who the visitor is: your own portal, a control panel, a desk tool. Use the first only when you are the one asking for the password.
Prerequisites
Clients/ValidateClient and Clients/CreateClientSsoToken. Grant only the one you use.
Structure
The ticket is a random secret, not a package of data. Everything the sign-in needs — the expiry and the landing page — is stored on the account, so nothing the caller receives can be edited into a different destination or a longer window.
Step by Step
Sending a Client In
- Your system decides which client this visitor is. Call
POST /clients/ssowith that client's id. You get back atoken, aurland anexpires_at. - Redirect the visitor to
urlstraight away. Do not show it as a link for later — the ticket is valid for 60 seconds. - The visitor lands signed in, on their dashboard or on the page you named in
destination. The ticket is spent; the same URL will not work twice.
Choosing Where They Land
- Leave
destinationout and the client lands on their dashboard. - Send a route key with its parameters to land deeper:
"destination": "services", "destination_values": [128]. - An absolute URL works too, as long as it belongs to this installation. Anything pointing elsewhere is dropped and the client lands on the dashboard instead.
Reference
Ticket Request
user_id is accepted as its former name.
destination is a route key.
How the Ticket Behaves
Full field tables, error codes and response shapes are in the endpoint reference for clients.
Example
curl -X POST 'https://panel.example.com/api/v1/admin/clients/sso' \
-H "Authorization: Bearer $API_KEY" \
-H 'Content-Type: application/json' \
-d '{"client_id":64,"destination":"services","destination_values":[128]}'const res = await fetch('https://panel.example.com/api/v1/admin/clients/sso', {
method: 'POST',
headers: {
Authorization: `Bearer ${apiKey}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
client_id: 64,
destination: 'services',
destination_values: [128],
}),
});
const body = await res.json();
// Redirect from the server that holds the key, not from the visitor's browser:
// the ticket is a credential and should not pass through untrusted code.
res.ok && console.log(body.data.url);<?php
$ch = curl_init('https://panel.example.com/api/v1/admin/clients/sso');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . $apiKey,
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => json_encode(['client_id' => 64]),
]);
$body = json_decode(curl_exec($ch), true);
curl_close($ch);
// Send the visitor there now; the ticket expires in 60 seconds.
header('Location: ' . $body['data']['url']);
exit;$response = Api::Clients()->CreateClientSsoToken([
'client_id' => 64,
'destination' => 'services',
'destination_values' => [128],
]);
// No key, no HTTP round trip — same resource, called in process.
Utility::redirect($response['data']['url']);Pitfalls
A ticket is a credential. Whoever opens the URL becomes that client. Do not log it, email it or put it in a page a third party can read.
Do not mint tickets in advance. They last 60 seconds and a client holds only one at a time, so issuing a batch leaves you with a single working ticket.
Validate does not sign anyone in. It answers whether a password matches. If you want a session, issue a ticket afterwards.
Related Articles
Thanks for your feedback!
Our support team is here around the clock for anything you can't find above.