Skip to content

SocialAuth

Viames Marino edited this page May 4, 2026 · 1 revision

Pair framework: SocialAuth

Pair\Services\SocialAuth is Pair's lightweight OAuth/OIDC helper for social sign-in flows.

It builds provider authorization URLs, stores single-use callback state in the native PHP session, exchanges authorization codes for tokens, and normalizes provider profiles into one stable object shape.

Built-in providers

Pair ships built-in metadata for:

  • Google
  • Apple
  • Microsoft
  • WhatsApp / Facebook OAuth

Custom providers can be passed directly to the constructor when they expose compatible OAuth authorization, token, and profile endpoints.

Configuration from .env

Enable the providers that should be available in UI:

PAIR_SOCIAL_AUTH_PROVIDERS=google,apple,microsoft,whatsapp
PAIR_SOCIAL_AUTH_TIMEOUT=15
PAIR_SOCIAL_AUTH_CONNECT_TIMEOUT=5

Provider credentials use the PAIR_SOCIAL_<PROVIDER>_ prefix:

PAIR_SOCIAL_GOOGLE_CLIENT_ID=
PAIR_SOCIAL_GOOGLE_CLIENT_SECRET=

PAIR_SOCIAL_APPLE_CLIENT_ID=
PAIR_SOCIAL_APPLE_CLIENT_SECRET=
PAIR_SOCIAL_APPLE_TEAM_ID=
PAIR_SOCIAL_APPLE_KEY_ID=
PAIR_SOCIAL_APPLE_CLIENT_SECRET_TTL=
PAIR_SOCIAL_APPLE_PRIVATE_KEY=
PAIR_SOCIAL_APPLE_PRIVATE_KEY_PATH=

PAIR_SOCIAL_MICROSOFT_CLIENT_ID=
PAIR_SOCIAL_MICROSOFT_CLIENT_SECRET=

PAIR_SOCIAL_WHATSAPP_CLIENT_ID=
PAIR_SOCIAL_WHATSAPP_CLIENT_SECRET=
PAIR_SOCIAL_WHATSAPP_CONFIG_ID=

Advanced provider overrides follow the same prefix:

  • LABEL
  • ICON
  • AUTHORIZE_URL
  • TOKEN_URL
  • PROFILE_URL
  • SCOPES
  • PROFILE_QUERY
  • AUTHORIZE_PARAMS

SCOPES accepts comma-separated or space-separated values. PROFILE_QUERY and AUTHORIZE_PARAMS accept query-string syntax.

Methods

__construct(?array $providers = null, ?int $timeout = null, ?int $connectTimeout = null)

Builds the helper from explicit providers or from .env configuration.

When $providers is omitted, PAIR_SOCIAL_AUTH_PROVIDERS controls which built-in providers are loaded.

providers(): array

Returns UI-safe provider summaries:

use Pair\Services\SocialAuth;

$providers = (new SocialAuth())->providers();

Each row contains:

  • key
  • label
  • icon

hasProvider(string $providerKey): bool

Checks whether a provider is configured and usable.

use Pair\Services\SocialAuth;

if ((new SocialAuth())->hasProvider('google')) {
	// Render the Google sign-in button.
}

begin(string $providerKey, string $redirectUri, array $context = [], array $params = []): string

Creates the authorization URL and stores the anti-forgery state in the native PHP session.

use Pair\Services\SocialAuth;

$auth = new SocialAuth();

$url = $auth->begin(
	'google',
	'https://app.example.test/auth/social/google/callback',
	['flow' => 'login']
);

$this->redirect($url, externalUrl: true);

$context is stored with the state and restored after callback completion. Use it for flow details such as login/register intent, tenant, or return URL.

complete(string $providerKey, array $query, ?string $redirectUri = null): stdClass

Validates callback state, exchanges the authorization code, loads the profile, and returns a normalized object.

use Pair\Services\SocialAuth;

$auth = new SocialAuth();
$profile = $auth->complete(
	'google',
	$_GET,
	'https://app.example.test/auth/social/google/callback'
);

$externalId = $profile->provider . ':' . $profile->subject;

Returned fields include:

  • provider
  • provider_label
  • subject
  • email
  • email_verified
  • name
  • given_name
  • family_name
  • avatar_url
  • raw_profile
  • token
  • state
  • context

Apple notes

Apple can return profile data through both id_token and the user form payload. SocialAuth merges both sources.

Apple's built-in provider metadata uses response_mode=form_post, so callback controllers should pass the posted payload to complete() for Apple routes and the query payload for standard redirect providers.

If PAIR_SOCIAL_APPLE_CLIENT_SECRET is empty, Pair can generate the Sign in with Apple client-secret JWT from:

  • PAIR_SOCIAL_APPLE_TEAM_ID
  • PAIR_SOCIAL_APPLE_KEY_ID
  • PAIR_SOCIAL_APPLE_CLIENT_SECRET_TTL
  • PAIR_SOCIAL_APPLE_PRIVATE_KEY
  • PAIR_SOCIAL_APPLE_PRIVATE_KEY_PATH

Applications may also provide the private key directly with PAIR_SOCIAL_APPLE_PRIVATE_KEY, even though the default .env template prefers a file path.

Security behavior

  • State values are random, stored server-side, and single-use.
  • Stored state expires after 10 minutes.
  • complete() can enforce the exact redirect URI used by begin().
  • Provider HTTP errors are converted to PairException with ErrorCodes::SOCIAL_AUTH_ERROR.
  • ID token payloads are decoded only for profile claims; they are not treated as proof of authorization by themselves.

Controller shape

use Pair\Http\JsonResponse;
use Pair\Services\SocialAuth;
use Pair\Web\Controller;

final class SocialController extends Controller {

	/**
	 * Redirect the browser to the selected social provider.
	 */
	public function loginAction(): void {

		$provider = (string)$this->input()->string('provider');
		$redirectUri = BASE_HREF . 'social/callback/' . $provider;
		$url = (new SocialAuth())->begin($provider, $redirectUri, [
			'flow' => 'login',
		]);

		$this->redirect($url, externalUrl: true);

	}

	/**
	 * Complete the provider callback and map the profile to an application user.
	 */
	public function callbackAction(): JsonResponse {

		$provider = (string)$this->input()->string('provider');
		$redirectUri = BASE_HREF . 'social/callback/' . $provider;
		$profile = (new SocialAuth())->complete($provider, $_GET, $redirectUri);

		return $this->json([
			'provider' => $profile->provider,
			'subject' => $profile->subject,
			'email' => $profile->email,
		]);

	}

}

See also: User, Session, Configuration-file, ErrorCodes, Integrations.

Clone this wiki locally