PHPackages                             getsupertab/connect-sdk-php - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Authentication &amp; Authorization](/categories/authentication)
4. /
5. getsupertab/connect-sdk-php

ActiveLibrary[Authentication &amp; Authorization](/categories/authentication)

getsupertab/connect-sdk-php
===========================

Supertab Connect PHP SDK

v1.3.1(1mo ago)0403↓13.9%[1 PRs](https://github.com/getsupertab/connect-sdk-php/pulls)MITPHPPHP &gt;=8.1CI passing

Since Mar 17Pushed 2mo agoCompare

[ Source](https://github.com/getsupertab/connect-sdk-php)[ Packagist](https://packagist.org/packages/getsupertab/connect-sdk-php)[ Docs](https://github.com/getsupertab/connect-sdk-php)[ RSS](/packages/getsupertab-connect-sdk-php/feed)WikiDiscussions main Synced 1w ago

READMEChangelog (10)Dependencies (9)Versions (23)Used By (0)

Supertab Connect PHP SDK
========================

[](#supertab-connect-php-sdk)

Check our [documentation](https://connect-docs.supertab.co/introduction/overview) for more information on Supertab Connect.

[![Packagist Version](https://camo.githubusercontent.com/599199090a0bfd0e0b67ec3ed173606ee9923be0c425f5c0427d6829d60c897c/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f67657473757065727461622f636f6e6e6563742d73646b2d7068702e737667)](https://packagist.org/packages/getsupertab/connect-sdk-php)[![License](https://camo.githubusercontent.com/4c2e6f32a2913bf1364801b5d2f582e7e5d4b3524a0ce3bf14c85f077fa6aa81/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f6c2f67657473757065727461622f636f6e6e6563742d73646b2d7068702e737667)](https://github.com/getsupertab/connect-sdk-php/blob/main/LICENSE)[![CI](https://camo.githubusercontent.com/442cd08a07d62328648f6c721713de436e96cca82d976ca99f87b60b06bf0bac/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f616374696f6e732f776f726b666c6f772f7374617475732f67657473757065727461622f636f6e6e6563742d73646b2d7068702f63692e796d6c3f6272616e63683d6d61696e)](https://github.com/getsupertab/connect-sdk-php/actions/workflows/ci.yml)[![PHP Version](https://camo.githubusercontent.com/ad316784910ca54846d85e2c2cc56186fc2df86bbb434aca5a3720ca4b592999/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f7068702d762f67657473757065727461622f636f6e6e6563742d73646b2d7068702e737667)](https://packagist.org/packages/getsupertab/connect-sdk-php)[![Code Style](https://camo.githubusercontent.com/1cdac822177ca99889bcb8da98bbcdfd1333dce685ced7e59da5776c3a7dd915/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f636f64652532307374796c652d50485043532d3446354239332e737667)](https://github.com/PHPCSStandards/PHP_CodeSniffer)

Installation
------------

[](#installation)

```
composer require getsupertab/connect-sdk-php
```

**Requirements:** PHP 8.1+, extensions: `ext-curl`, `ext-json`, `ext-openssl`, `ext-simplexml`

Quick Start
-----------

[](#quick-start)

**Publisher — verify incoming requests:**

```
use Supertab\Connect\SupertabConnect;
use Supertab\Connect\Enum\EnforcementMode;
use Supertab\Connect\Enum\HandlerAction;

$connect = new SupertabConnect(
    apiKey: 'stc_live_your_api_key',
    enforcement: EnforcementMode::ENFORCE,
);

$result = $connect->handleRequest();

// Send returned headers (RSL headers such as Link / WWW-Authenticate, or
// Cache-Control for the self-report status endpoint).
foreach ($result->headers as $name => $value) {
    header("{$name}: {$value}");
}

// Any non-ALLOW result carries a status + body to emit: BLOCK (invalid/missing
// token) and RESPOND (the self-report status endpoint) both end the request.
if ($result->action !== HandlerAction::ALLOW) {
    http_response_code($result->status);
    echo $result->body;
    exit;
}

// Token is valid — serve content
```

**Bot — obtain a license token:**

```
use Supertab\Connect\SupertabConnect;

$token = SupertabConnect::obtainLicenseToken(
    clientId: 'your_client_id',
    clientSecret: 'your_client_secret',
    resourceUrl: 'https://example.com/article/my-slug',
);

$ch = curl_init('https://example.com/article/my-slug');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => ["Authorization: License {$token}"],
]);
$response = curl_exec($ch);
```

Enforcement Modes
-----------------

[](#enforcement-modes)

The `EnforcementMode` enum controls how `handleRequest()` responds to detected bots when a token is absent or invalid. Non-bot requests without a token are always allowed regardless of mode. Requests with an invalid token are always blocked (except in DISABLED mode).

ModeBehavior`ENFORCE`Bots without a valid token are blocked (401/403 with `WWW-Authenticate` header). Invalid tokens from any source are rejected.`OBSERVE`All requests allowed. Bots without a token receive `X-RSL-Status: token_required` and `Link` headers to signal that licensing is available. Invalid tokens are still rejected.`DISABLED`All requests allowed unconditionally — no bot detection, no token verification, even if a token is present.Default is `OBSERVE`.

---

Analytics
---------

[](#analytics)

When enabled, the SDK emits **one analytics event per request** to the Supertab Connect relay at `{analyticsBaseUrl}/ingest/events` — defaulting to the dedicated ingest service (`https://ingest-connect.supertab.co`) — carrying bot-classification signals (user agent, client IP, request metadata, and the verification/enforcement decision). It is **off by default** — enable it with `analyticsEnabled: true`:

```
$connect = new SupertabConnect(
    apiKey: 'stc_live_your_api_key',
    analyticsEnabled: true,
);
```

- **Fail-open.** Emission never throws or alters request handling; errors are swallowed and the relay POST uses a short timeout.
- **Isolated from billing.** Analytics goes only to `/ingest/events`; the billing `/events` path is untouched.
- **Independent of `setBaseUrl`.** The analytics host defaults to the ingest service and is separate from the API base URL (token/JWKS/verify). Point it at another environment (or localhost) with `setAnalyticsBaseUrl()`, or per-instance via the `analyticsBaseUrl` constructor option.

Events are emitted with `schema_version: 2` (Capture v2), adding spoof-detection signals read from the request: `Sec-Fetch-*` and client hints (`Sec-CH-UA*`), `accept`, `host`, cookie presence, and the stripped/sorted `header_names` set, plus query-shape signals (`query_length`, `query_param_count`, `query_suspicious`). The raw query string is never stored. CDN-only transport signals (TLS version/cipher, JA4, verified-bot category, AS organization, …) are emitted as `null` at a PHP origin unless injected explicitly via `RequestContext`'s `cdnSignals` (see below).

### Delivery

[](#delivery)

By default the relay POST is wrapped in `DeferredAnalyticsTransport`: on FastCGI-style SAPIs (PHP-FPM, LiteSpeed, FrankenPHP) it runs **after the response has been flushed to the visitor** via `fastcgi_finish_request()`, so analytics never adds to user-perceived latency. Where that function is unavailable (Apache `mod_php`, the built-in CLI server), it falls back to a bounded **synchronous** POST. Either way the PHP worker stays occupied until the POST finishes — this hides latency from the visitor, it does not reduce server-side work.

To route delivery yourself — e.g. onto a job queue so it leaves the request worker entirely — inject an `AnalyticsTransportInterface` via `analyticsTransport`. An injected transport is used exactly as given (the SDK does not wrap it). `CallbackAnalyticsTransport` adapts a closure, and `AnalyticsEvent::toArray()` / `fromArray()` serialize and rehydrate an event across a queue boundary. A WordPress plugin using [Action Scheduler](https://actionscheduler.org/):

```
use Supertab\Connect\Analytics\AnalyticsEvent;
use Supertab\Connect\Analytics\CallbackAnalyticsTransport;
use Supertab\Connect\Analytics\HttpAnalyticsTransport;
use Supertab\Connect\Http\HttpClient;

$connect = new SupertabConnect(
    apiKey: $apiKey,
    analyticsEnabled: true,
    // emit() just enqueues — returns immediately, off the visitor request
    analyticsTransport: new CallbackAnalyticsTransport(
        fn (AnalyticsEvent $e) => as_enqueue_async_action('supertab_connect_emit_analytics', [$e->toArray()]),
    ),
);

// The scheduled job runs in a cron/loopback worker; the POST is plain synchronous there.
add_action('supertab_connect_emit_analytics', function (array $payload) use ($apiKey) {
    (new HttpAnalyticsTransport($apiKey, SupertabConnect::getAnalyticsBaseUrl(), new HttpClient))
        ->emit(AnalyticsEvent::fromArray($payload));
});
```

---

API Reference
-------------

[](#api-reference)

### `new SupertabConnect()`

[](#new-supertabconnect)

Creates a singleton instance. Returns the existing instance if one already exists with the same `apiKey`. Throws if an instance with a different `apiKey` already exists.

ParameterTypeRequiredDefaultDescription`apiKey``string`Yes—Your Supertab Connect API key (`stc_live_...` or `stc_sandbox_...`)`enforcement``EnforcementMode`No`OBSERVE`How to handle missing or invalid tokens`debug``bool`No`false`Emit debug logs via `error_log()``baseUrl``?string`No`null`Set the global default base URL (same as `setBaseUrl()`)`httpClient``?HttpClientInterface`No`null`Inject a custom HTTP client (defaults to built-in cURL client)`botDetector``?BotDetectorInterface`No`null`Inject a custom bot detector (defaults to `DefaultBotDetector`)`analyticsEnabled``bool`No`false`Emit one relay analytics event per request to `{analyticsBaseUrl}/ingest/events` (see [Analytics](#analytics))`analyticsTransport``?AnalyticsTransportInterface`No`null`Route analytics through a custom delivery path (e.g. a job queue). Used as-is when provided — bypasses the default deferred HTTP transport (see [Delivery](#delivery))`analyticsBaseUrl``?string`No`null`Base URL of the analytics ingest service (resolves to `https://ingest-connect.supertab.co` by default). Independent of `baseUrl`/`setBaseUrl()` (token/JWKS/verify). Also settable globally via `setAnalyticsBaseUrl()`; the per-instance option wins### `handleRequest(?RequestContext $context): HandlerResult`

[](#handlerequestrequestcontext-context-handlerresult)

Handles an incoming request end-to-end: extracts the license token from the `Authorization` header, verifies it, runs bot detection, records a billing event, emits one relay analytics event (when analytics is enabled), and applies the enforcement mode. When a token is present, it is verified (unless DISABLED mode). When no token is present, bot detection determines whether enforcement kicks in — non-bot requests are always allowed. Returns a result object — the caller is responsible for sending HTTP headers and status codes.

ParameterTypeRequiredDefaultDescription`context``?RequestContext`No`null`Request info. Defaults to `RequestContext::fromGlobals()` which reads from `$_SERVER`.**Returns:** `HandlerResult` — `AllowResult` (action: ALLOW), `BlockResult` (action: BLOCK, with `status`, `body`, and `headers`), or `RespondResult` (action: RESPOND, same shape as `BlockResult`) for the [self-report status endpoint](#self-report-status-endpoint). Emit `status`/`body`/`headers` for any non-ALLOW result.

When integrating with a framework, pass a `RequestContext` instead of relying on `$_SERVER`:

```
use Supertab\Connect\Analytics\CdnRequestSignals;
use Supertab\Connect\Http\RequestContext;

// `headers` must be a flat array. Join multi-value headers
// (e.g. `implode(', ', $values)`) before passing them in; non-string values
// are silently dropped.
$headers = [];
foreach ($request->headers->all() as $name => $values) {
    $headers[$name] = is_array($values) ? implode(', ', $values) : (string) $values;
}

$ctx = new RequestContext(
    url: $request->getUri(),
    authorizationHeader: $request->header('Authorization'),
    userAgent: $request->header('User-Agent'),
    accept: $request->header('Accept'),           // used by bot detection
    acceptLanguage: $request->header('Accept-Language'), // used by bot detection
    secChUa: $request->header('Sec-CH-UA'),        // used by bot detection
    headers: $headers,                             // forwarded into event properties (h_* prefix)
    method: $request->method(),                    // analytics
    clientIp: $request->ip(),                      // analytics — your framework's trusted client IP
    // Optionally inject edge signals if your stack provides them (never auto-derived):
    // requestCountry: $request->header('CF-IPCountry'),
    // requestAsn: 13335,
    // tlsFingerprint: $request->header('CF-JA3'),
    // cdnSignals: new CdnRequestSignals(  // Capture-v2 CDN plumbing, if a CDN fronts the origin
    //     tlsVersion: $request->header('CF-Visitor-TLS'),
    //     cdnVerifiedBotCategory: $request->header('CF-Verified-Bot-Category'),
    // ),
);

$result = $connect->handleRequest($ctx);
```

All entries in `headers` are forwarded to the analytics event under an `h_` key. Credential and PII headers (`authorization`, `cookie`, `set-cookie`, `proxy-authorization`, `x-api-key`, `x-amz-security-token`, `user-agent`, `x-license-auth`, `forwarded`, `x-forwarded-for`, `x-real-ip`, `cf-connecting-ip`, `true-client-ip`) are filtered out. `RequestContext::fromGlobals()` populates `headers` automatically from `$_SERVER`.

### `SupertabConnect::verify()` (static)

[](#supertabconnectverify-static)

Pure token verification without creating an instance. Does not apply enforcement mode or set response headers.

ParameterTypeRequiredDefaultDescription`token``string`Yes—Raw JWT token (without the `License ` prefix)`resourceUrl``string`Yes—The URL being accessed`baseUrl``?string`No`null`Per-call override (does not change the global default)`debug``bool`No`false`Emit debug logs`httpClient``?HttpClientInterface`No`null`Inject a custom HTTP client**Returns:** `VerificationResult` with `valid: bool` and `error: ?string`.

```
$result = SupertabConnect::verify(
    token: $token,
    resourceUrl: 'https://example.com/article/my-slug',
);

if (! $result->valid) {
    http_response_code(401);
    echo $result->error;
    exit;
}
```

### `$connect->verifyAndRecord()`

[](#connect-verifyandrecord)

Verifies a license token and records an analytics event. Requires an instance (uses the instance's `apiKey` for event recording).

ParameterTypeRequiredDefaultDescription`token``string`Yes—Raw JWT token (without the `License ` prefix)`resourceUrl``string`Yes—The URL being accessed`userAgent``?string`No`null`User-Agent string for analytics`requestHeaders``?array`No`null`Incoming request headers to forward into event properties under an `h_` prefix (credential/PII headers filtered out)**Returns:** `VerificationResult` with `valid: bool` and `error: ?string`.

```
$connect = new SupertabConnect(apiKey: 'stc_live_your_api_key');

$result = $connect->verifyAndRecord(
    token: $token,
    resourceUrl: 'https://example.com/article/my-slug',
    userAgent: $_SERVER['HTTP_USER_AGENT'] ?? null,
    requestHeaders: getallheaders() ?: [],
);

if (! $result->valid) {
    http_response_code(401);
    echo $result->error;
    exit;
}
```

### `SupertabConnect::fetchLicenseXml()` (static)

[](#supertabconnectfetchlicensexml-static)

Fetches the RSL license XML for a merchant system from the Supertab Connect API.

ParameterTypeRequiredDefaultDescription`merchantSystemUrn``string`Yes—Your merchant system URN (`urn:supertab:system:...`)`baseUrl``?string`No`null`Per-call override (does not change the global default)`httpClient``?HttpClientInterface`No`null`Inject a custom HTTP client**Returns:** `string` (the raw XML body). Throws `SupertabConnectException` on failure.

```
$xml = SupertabConnect::fetchLicenseXml(
    merchantSystemUrn: 'urn:supertab:system:your_system_id',
);

header('Content-Type: application/rsl+xml');
echo $xml;
```

### `SupertabConnect::obtainLicenseToken()` (static)

[](#supertabconnectobtainlicensetoken-static)

Obtains a license token for accessing a protected resource using the OAuth2 `client_credentials` flow.

ParameterTypeRequiredDefaultDescription`clientId``string`Yes—OAuth2 client ID`clientSecret``string`Yes—OAuth2 client secret`resourceUrl``string`Yes—Full URL of the protected resource`debug``bool`No`false`Emit debug logs`httpClient``?HttpClientInterface`No`null`Inject a custom HTTP client**Returns:** `string` (the access token). Throws `SupertabConnectException` on failure.

The SDK handles the full RSL flow automatically:

1. Fetches `{origin}/license.xml` from the resource URL
2. Parses content blocks and finds the best matching URL pattern (exact &gt; path pattern &gt; wildcard by specificity)
3. POSTs to the token endpoint using OAuth2 `client_credentials`
4. Caches the token in memory (keyed by `clientId:resourceUrl`, reused until 30s before expiry)

### `SupertabConnect::setBaseUrl()` (static)

[](#supertabconnectsetbaseurl-static)

Sets the global default base URL for all API requests. Useful for sandbox/testing environments. This affects all subsequent calls (both instance and static methods).

```
SupertabConnect::setBaseUrl('https://api-connect.sbx.supertab.co');
```

### `SupertabConnect::getBaseUrl()` (static)

[](#supertabconnectgetbaseurl-static)

Returns the current global default base URL.

### `SupertabConnect::setAnalyticsBaseUrl()` (static)

[](#supertabconnectsetanalyticsbaseurl-static)

Sets the global base URL of the analytics ingest relay (default `https://ingest-connect.supertab.co`). Independent of `setBaseUrl()` — token/JWKS/verify traffic is unaffected.

```
SupertabConnect::setAnalyticsBaseUrl('https://ingest-connect.sbx.supertab.co');
```

### `SupertabConnect::getAnalyticsBaseUrl()` (static)

[](#supertabconnectgetanalyticsbaseurl-static)

Returns the current base URL of the analytics ingest relay.

### `SupertabConnect::resetInstance()` (static)

[](#supertabconnectresetinstance-static)

Clears the singleton instance, allowing a new one to be created with different configuration.

---

Self-Report Status Endpoint
---------------------------

[](#self-report-status-endpoint)

`handleRequest()` also serves `GET /.well-known/supertab/status`, which lets the Supertab backend observe an SDK's live health by comparing configured-vs-confirmed settings. The branch short-circuits at the top of `handleRequest()` — before token verification, bot detection, and analytics — so a probe never looks like real traffic or emits an event.

A request carrying a valid, backend-minted challenge (`Authorization: Bearer `, an ES256 token with `purpose: "status-probe"` and `aud` equal to the request origin) gets the live running config back:

```
{ "runtime": null, "sdkVersion": "v1.4.0", "component": { "kind": "php-sdk", "version": "v1.4.0" }, "enforcement": "enforce", "eventReporting": true }
```

Any other request (missing, malformed, expired, or wrong-audience challenge) gets a minimal `{"supertab": true}` with a `404` status. Both responses set `Cache-Control: no-store`. Challenge verification reuses the same platform JWKS the SDK already fetches for license tokens — no extra configuration.

Because the endpoint is served through `handleRequest()`, no extra wiring is needed: the dispatch shown above (emit `status`/`body`/`headers` for any non-ALLOW `HandlerResult`) already returns the probe response. The result is a `RespondResult` (`action: RESPOND`).

---

Result Types
------------

[](#result-types)

### `HandlerResult` (returned by `handleRequest()`)

[](#handlerresult-returned-by-handlerequest)

PropertyTypeDescription`action``HandlerAction``ALLOW`, `BLOCK`, or `RESPOND``headers``array`Response headers`BlockResult` and `RespondResult` both add `status: int` and `body: string`.

```
use Supertab\Connect\Enum\HandlerAction;

foreach ($result->headers as $name => $value) {
    header("{$name}: {$value}");
}

if ($result->action !== HandlerAction::ALLOW) {
    http_response_code($result->status);
    echo $result->body;
    exit;
}

// AllowResult — serve your content
```

### `VerificationResult` (returned by `verify()`)

[](#verificationresult-returned-by-verify)

PropertyTypeDescription`valid``bool`Whether the token is valid`error``?string`Human-readable reason if invalid---

Debug Logging
-------------

[](#debug-logging)

Pass `debug: true` to the constructor or static methods to log internal steps via `error_log()`:

```
[SupertabConnect] Fetching license.xml from https://example.com/license.xml
[SupertabConnect] Found 2 content block(s)
[SupertabConnect] Best match: https://example.com/* (server: https://api-connect.supertab.co)
[SupertabConnect] Token obtained and cached

```

###  Health Score

44

—

FairBetter than 90% of packages

Maintenance87

Actively maintained with recent releases

Popularity17

Limited adoption so far

Community8

Small or concentrated contributor base

Maturity53

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 97.6% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~8 days

Total

16

Last Release

24d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/0baa8b56717561f7bf38e42ee5e6215e4f7f203808d14c52e36208ba280b72ad?d=identicon)[getsupertab](/maintainers/getsupertab)

---

Top Contributors

[![tomasstark](https://avatars.githubusercontent.com/u/983791?v=4)](https://github.com/tomasstark "tomasstark (40 commits)")[![nick434434](https://avatars.githubusercontent.com/u/20317998?v=4)](https://github.com/nick434434 "nick434434 (1 commits)")

---

Tags

connectsdkjwtlicenseconnectsupertabrsl

###  Code Quality

TestsPHPUnit

Code StylePHP\_CodeSniffer

### Embed Badge

![Health badge](/badges/getsupertab-connect-sdk-php/health.svg)

```
[![Health](https://phpackages.com/badges/getsupertab-connect-sdk-php/health.svg)](https://phpackages.com/packages/getsupertab-connect-sdk-php)
```

###  Alternatives

[google/auth

Google Auth Library for PHP

1.4k302.1M239](/packages/google-auth)[ellaisys/aws-cognito

Laravel Authentication using AWS Cognito (Web and API)

121269.8k1](/packages/ellaisys-aws-cognito)[rainlab/user-plugin

User plugin for October CMS

11955.3k15](/packages/rainlab-user-plugin)[dev-toolbelt/jwt-token-manager

Framework-agnostic JWT token manager with RSA/HMAC support

111.3k](/packages/dev-toolbelt-jwt-token-manager)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
