Looking to hire Laravel developers? Try LaraJobs
This package is not available.

laravel-truecaller-oauth maintained by kkxdev

Description
Truecaller OAuth 2.0 (SDK 3.x) server-side login for Laravel
Author
kkxdev
Last update
2026/09/23 10:30 (1.0.0)
License
Links
Downloads
2

Comments
comments powered by Disqus

laravel-truecaller-oauth

Server-side Truecaller OAuth 2.0 (SDK 3.x) login for Laravel 8–12.

An app obtains an authorization code from the Truecaller SDK; this package exchanges it for a verified identity. What you do with that identity — find a user, create one, issue a session — stays in your application.

use Kkxdev\Truecaller\Facades\Truecaller;
use Kkxdev\Truecaller\Exceptions\TruecallerException;

try {
    $profile = Truecaller::authenticate($authorizationCode, $codeVerifier);
} catch (TruecallerException $e) {
    return response()->json(['code' => $e->errorCode()], $e->clientStatus());
}

$profile->phone()->e164();            // "+919876543210"
$profile->phone()->dialCode();        // "91"   (bare, never "+91")
$profile->phone()->nationalNumber();  // "9876543210"
$profile->sub();                      // stable Truecaller subject id

Install

composer require kkxdev/laravel-truecaller-oauth

Auto-discovery registers the provider and the Truecaller facade. Set the partner key from the Truecaller developer portal:

TRUECALLER_CLIENT_ID=your-partner-key
TRUECALLER_REGION=noneu          # or "eu"

To pin the ISO → dial-code map in your own repo:

php artisan vendor:publish --tag=truecaller-config

What it guarantees

A code can be spent once. CacheReplayGuard claims the authorization code atomically before the exchange. A claim is only given back when the spend provably did not happen — a timeout or a provider outage — so a transport blip never burns a code the user could still redeem, while a code Truecaller has already rejected stays dead.

A number is normalised or refused, never guessed. Truecaller returns an ISO alpha-2 country and an E.164-ish number; most user stores want a bare dial code plus a bare national number. PhoneNormaliser tries the plausible readings in order and returns the first that satisfies the configured rule for its dial code, repairing the two malformations that actually occur (a doubled country code, a leading trunk zero). If nothing validates it throws, because a guessed dial code writes a number the real owner can never match — which silently gives them a second account.

Secrets never reach the log. SecretMasker redacts recursively and case-insensitively. Authorization codes are logged as a 12-character fingerprint so one login can be followed across log lines without the code itself being written down.

Errors

Every failure is a TruecallerException carrying errorCode(), clientStatus() and a user-safe clientMessage().

Exception Meaning Status
CodeReplayedException code already exchanged — restart the SDK flow, do not retry 409
CodeRejectedException provider rejected the code or PKCE verifier 422
TokenRejectedException provider rejected the access token 422
ProfileIncompleteException no subject, or no phone number shared 422
PhoneUnsupportedException number not normalisable with confidence 422
UnavailableException provider 5xx/429, or a 200 with no token 503
TimeoutException connect or read timeout 503
NotConfiguredException no client id configured 503

isTransient() tells you whether retrying the same code could work; it is what drives the replay guard's release.

Security model

There is no signature to verify in the OAuth flow, so what makes this safe is: the authorization code is single-use at Truecaller, PKCE binds it to the app instance that began the flow, the replay guard stops it being spent twice, TLS protects it in transit, and TRUECALLER_CLIENT_ID stays secret and unlogged.

Access tokens are used once, in the request that obtained them, and are never cached.

Configuration to verify before production

The OAuth hosts, paths and userinfo field names come from Truecaller's developer portal and are the only values here not derivable from your own code. All are env-overridable, so confirming them is an ops change rather than a code change. The normaliser accepts phone_number as "+919876543210", "919876543210" or the JSON number 919876543210.

Tests

composer install && vendor/bin/phpunit

config.audit.block-insecure is disabled in composer.json because the dev tree resolves to the Laravel 8 line on PHP 8.0 — the versions this package must keep working against. It affects the test tree only.

Licence

MIT