Aller au contenu

omnisense/omniauth Référence de l’API

Étage
Famille
Version
1.x
Suite de conformité
branchée, CI verte
Vérifié en réel
Vérifié en partie
Licence
MIT
composer require omnisense/omniauth:1.x-dev

Lue dans les fichiers PHP du paquet au commit 66aa549da28c : ses espaces de noms, classes, interfaces et énumérations, leurs constantes, propriétés et méthodes publiques avec leur PHPDoc, et les options que prennent ses fabriques. Rien n’en est exécuté.

Omniauth

  • ArrayState class

    The visitor's state in a PHP array: plain PHP (hand it $_SESSION by reference, or keep the array yourself) and tests. Symfony and Laravel keep it in their session (omnibridge/symfony, omnibridge/laravel).

    6 methods

  • Attempt class

    One visitor's attempt to sign in, kept by the host between two requests (Contract\State): the account found by the first factor, the factors passed, the second factors offered, and what a ceremony keeps between its beginning and its verification (a challenge, a code's deadline) - never in the response.

    14 methods

  • CacheThrottle class

    A fixed window per scope and key, counted in any PSR-6 pool or PSR-16 cache: plain PHP and tests. Under Symfony and Laravel, their rate limiters answer the same contract.

    3 methods · 1 constant

  • Context class

    What the host knows of the request, with no object of its framework: the same values under Symfony, Laravel and plain PHP.

    1 method · 6 properties

  • Enrolled class

    What setting a factor up ended with; the secrets to show once (backup codes) when it worked.

    4 methods · 3 properties

  • EnrolledSecondFactors class

    The core's second-factor rule: an account that set up a second factor (an authenticator application, the code by e-mail) must pass one of them - or a backup code - after a first factor of $after; a second factor already passed (a trusted device among them) is enough.

    2 methods · 1 constant

  • Enrolment class

    Setting a factor up for an account, in the same two steps as a sign-in: begin() (a code sent, a secret, options to sign, or nothing), confirm() (what the visitor sent back; the secrets to show once).

    3 methods · 2 properties

  • Factor enum

    What a person proves when signing in, named once for every method, every policy and every host: the stable key a host's routes, settings and translations use.

    2 methods · 8 cases

  • Failure enum

    Why an attempt failed: stable keys a page translates (omnibase's "@notifications.login.*" catalogues), never a message.

    10 cases

  • Message class

    What a method asks the host to send to an account: a sign-in link, a code.

    1 method · 3 properties

  • Omniauth class

    The conductor: who signs in, by which factors, and what remains to pass.

    8 methods · 3 constants

  • PendingIdentity class

    Someone a provider vouched for whom no account is linked to: what a sign-up form fills in, and what links the account once it exists.

    1 method · 5 properties

  • Proof class

    What the visitor sent: ['identifier' => ..., 'password' => ...], ['code' => ...], ['token' => ...], a WebAuthn assertion's JSON under 'json', an OAuth return's 'code' and 'state'. Two keys mean the same to every method: 'remember' (the host sets its remember-me) and 'trust' (a trusted-device cookie after a second factor).

    7 methods

  • Result class

    What a verification ended with: who, by which factors, what remains to pass, and what the host does next (open its session, set its remember-me, set the trusted-device cookie).

    4 methods · 9 properties

  • Role enum

    A first factor finds the account; a second one is asked of an account already found.

    2 cases

  • Status enum

    Where an attempt stands after a verification.

    4 cases

  • SystemClock class

    The machine's clock, when the host gives none (Testing\FixedClock in tests).

    1 method

Omniauth\BackupCode

  • BackupCodeMethod class

    Backup codes: handed out once (8 by default, "A1B2C3-D4E5F6" as omnibase draws them), kept as password_hash() hashes - the very hashes omnibase's user.backupCodes holds - and each good once: the one that serves is removed. They stand in for another second factor; alone, they ask for none (EnrolledSecondFactors).

    8 methods

Omniauth\Contract

  • Accounts interface

    The application's accounts: opaque objects to omniauth, which implement nothing of it (omnibase fills this in on its UserRepository).

    7 methods

  • BackupCodes interface

    The backup codes' hashes, password_hash()'s (omnibase: user.backupCodes, a JSON list).

    2 methods

  • EmailCodes interface

    The code sent by e-mail (omnibase: user.emailAuthEnabled, user.emailAuthCode). The store keeps the code's keyed hash, never the code; its deadline is kept in the attempt.

    6 methods

  • Identities interface

    The accounts linked to an identity provider, by the person's identifier there ("sub"); read by Omniauth\OAuth\OAuthMethod (omnibase: the userIdentity table).

    5 methods

  • LinkTokens interface

    The sign-in links in flight. The store sees their SHA-256, never their value: whoever reads the table cannot sign in with it.

    2 methods

  • Passwords interface

    The accounts' password hashes (omnibase: user.password).

    2 methods

  • PlainLinkTokens interface

    For the switch only: the links issued before omniauth were kept in clear (omnibase's userToken "login-token"). A LinkTokens store that still holds some also implements this, and they are read as they are until they expire; once none is left, the store stops implementing it.

    1 method

  • SecondFactorPolicy interface

    The second-factor rule: what an account must still pass.

    1 method

  • Sender interface

    Sends a sign-in link or a code to an account (omnibase: its Notifier; tests: Testing\RecordingSender).

    1 method

  • State interface

    The visitor's own state between two requests: the session under Symfony and Laravel, Omniauth\ArrayState in plain PHP and tests. Values are arrays of scalars.

    3 methods

  • Throttle interface

    Limits the tries: Symfony's RateLimiterFactory, Laravel's RateLimiter, Omniauth\CacheThrottle on any PSR-6 or PSR-16 cache.

    2 methods

  • TotpSecrets interface

    The authenticator applications' secrets, base32 (omnibase: user.secret); read by omniauth/otp (SealedTotpSecrets seals them at rest).

    3 methods

  • TrustedVersion interface

    The trusted devices' version (omnibase: user.trustedTokenVersion, scheb's TrustedDeviceInterface): a token of another version no longer counts.

    2 methods

Omniauth\EmailCode

  • EmailCodeMethod class

    A code sent by e-mail once a first factor passed: 6 digits (as scheb/2fa draws them), good for 10 minutes and once. By Marco's decision QA5 (2026-10-20) the store keeps its hash, never the code: an HMAC-SHA256 keyed by the host's secret and bound to the account, compared in constant time; the deadline is kept in the attempt.

    9 methods · 1 constant

Omniauth\Event

  • AccountRefused class

    The proof was good but the account may not sign in (omnibase: the "welcome back" e-mail of a disabled account).

    1 method · 4 properties

  • Beginning class

    Before a ceremony begins (a link or a code is about to be sent): a listener may refuse.

    3 methods · 4 properties

  • CredentialAdded class

    A factor was set up, or a password changed.

    1 method · 2 properties

  • CredentialRemoved class

    A factor was turned off; the trusted devices fell back with it.

    1 method · 2 properties

  • Revoking class

    Before a factor is turned off: omnibase's rule ("the administrator wins": a role that requires a second factor keeps it) refuses here, so a hidden button and a forged POST meet the same predicate.

    3 methods · 2 properties

  • SecondFactorRequired class

    A first factor passed and a second one is due.

    1 method · 4 properties

  • SignInFailed class

    A verification failed: logged with the address and the factor, never with the secret; SignInGuard counts it.

    1 method · 6 properties

  • SignedIn class

    Signed in: omnibase's UserTracker, the "new device" e-mail, the account brought up to date listen here.

    1 method · 3 properties

  • Verifying class

    Before a method checks a proof (and after the throttle let it through): a listener may refuse - omnishield when the visitor is not a human, omnibase's SignInGuard when a captcha is due.

    3 methods · 4 properties

Omniauth\Exception

  • AccountRefused class

    Thrown by Accounts::check(): the account may not sign in now, for a stable reason the page translates.

    1 method · 1 property

  • OmniauthException interface

    Every exception omniauth throws.

  • UnknownMethod class

    A factor no method was mounted for: a host's mistake, not a visitor's.

    1 method

Omniauth\Link

  • LinkMethod class

    A sign-in link sent to the account's address, by Marco's decision QA4 (2026-10-20): good for 15 minutes, once, and kept hashed (SHA-256) - the store never sees its value. Each request sends a new link (the conductor limits them: "link.begin", 3 an hour by default), and the answer is the same whether the address has an account or not.

    5 methods · 1 constant

Omniauth\Method

  • Ambient interface

    A second factor the visitor does not present: it is read from the context (the trusted-device cookie). The conductor tries it by itself when a second factor is required, and asks it what to leave on the browser when the visitor asked to be trusted after passing another one.

    1 method

  • Challenge class

    What the host gives back after a beginning: JSON for a script, an address to go to, "it was sent", or why not.

    6 methods · 5 constants

  • Challenging interface

    A method that first gives something to the browser or the mailbox: options to sign, an address to go to, a link or a code sent. What it must remember goes into the attempt (Attempt::keep()), never into the challenge.

    1 method

  • Enrolling interface

    A method an account sets up from its settings, in the same two steps as a sign-in. The attempt is the enrolment's own (its account identified), kept apart from any sign-in.

    2 methods

  • Method interface

    A way to sign in: it checks a proof within an attempt, and touches neither request nor response.

    2 methods

  • Methods class

    The methods a host mounted, one per factor.

    8 methods

  • Revocable interface

    A factor an account can turn off (or, for trusted devices, forget every browser).

    1 method

  • SecondFactor interface

    A second factor an account sets up: the policy asks it whether this one has.

    1 method

  • Verdict class

    What a method concluded of a proof.

    5 methods

Omniauth\OAuth

  • Authorization class

    What one trip to a provider is bound to, drawn at the beginning and kept in the attempt (never in the answer): the state the return must bring back (against a forged return: login CSRF), the PKCE verifier whose S256 challenge goes in the authorization address (against a code stolen or injected: only the one who began can exchange it, RFC 7636), and the nonce an ID token must carry (OpenID Connect, against a token replayed) - and the origin of the request that began the trip, against which a provider configured with a return path ("/login/google/callback") makes its return address: the host the visitor is on at that request, not the first one a long-running worker served, and the same address again at the exchange (RFC 6749 §4.1.3: the token request repeats the authorization's).

    8 methods · 4 properties

  • Identity class

    Whom a provider vouched for, read from its ID token or its userinfo - never from the return address's parameters.

    3 methods · 6 properties

  • NoSecondFactorLinking class

    QA3's rule for the address: an account that set up a second factor (an authenticator application, the code by e-mail) is not linked by its address - whoever holds the provider's account would then be one factor away from it for good. It reads the methods mounted, so it can be built before the OAuth method is added to them.

    2 methods

  • OAuthMethod class

    Signing in through an identity provider (OAuth 2, OpenID Connect), a first factor: begin() draws a state, a PKCE verifier and a nonce, keeps them in the attempt and answers with the provider's authorization address (Challenge::redirect()); verify() takes the return's ['code' => …, 'state' => …] (or its ['error' => …]), takes what the attempt kept - once: a return comes back once -, compares the state in constant time, has the provider exchange the code with the verifier and read the person (an ID token or a userinfo, never the address's parameters), then finds the account (Marco's decision QA3):

    8 methods

Omniauth\OAuth\Contract

  • EmailLinking interface

    May an account be linked to an identity by its address alone - the provider having said the address is verified, and no account linked to that identity yet? Marco's decision QA3: never for an account that has a second factor or an administration role (NoSecondFactorLinking says the first; omnibase's rule, step AU4, adds its roles); the person then signs in otherwise and links the provider from the account's settings.

    1 method

Omniauth\OAuth\Exception

  • ProviderError class

    The provider did not vouch for anyone: the code refused at the exchange, an ID token not for this client, past its time or without the nonce, a profile without its subject. $reason is the stable word the method answers with; the message (and the previous exception: league's, Guzzle's) is for the server's log, not the page.

    1 method · 1 property · 4 constants

Omniauth\OAuth\Provider

  • LeagueProvider class

    A league/oauth2-client provider as omniauth's Provider - the ground of the provider packages built on league (omniauth/google's GoogleProvider), and any other league provider by hand (league/oauth2-facebook, a GenericProvider on a provider's three addresses...) until it has a package of its own. league/oauth2-client is not required by omniauth: the provider packages require it (this class loads without it, and is only usable with it). The authorization address with the state and the S256 challenge omniauth drew, the code exchanged with its verifier, the person read from the resource owner's details through the access token. $fields names the details' keys: the subject (its resource owner's getId() when null), the address, whether it is verified (null: the provider says nothing, so it is not - no account is ever linked by such an address), the name.

    5 methods · 1 constant

  • Provider interface

    An identity provider: where to send the visitor, then whom the code that came back vouches for. Its return address is its own, fixed at its configuration (never taken from a request).

    3 methods

  • ProviderFactory interface

    Makes the providers of one kind from a host's configuration: each provider package has its own (omniauth/google's GoogleFactory), so this package needs no OAuth library. Providers::configured() asks each factory in turn.

    1 method

  • Providers class

    The providers a host serves, by name. Marco's decisions: Google alone today (QA7), and one package per provider (2026-10-21) - omniauth/google first; configured() makes them through each package's factory.

    8 methods

Omniauth\OAuth\Testing

  • FakeProvider class

    An authorization server in memory, for a host's tests and a first try: it answers the authorization address as a provider does once the person consented (consent(): the return's code and state), and exchanges a code as a provider does - once, for the client and the return address it was given to, and only with the PKCE verifier whose S256 challenge the authorization carried (RFC 7636 §4.6); the nonce is the one the address carried. So a test signs in with no network, and a forged or injected return fails here as it would at Google.

    7 methods · 3 properties · 1 constant

Omniauth\Password

  • PasswordMethod class

    The password, checked by symfony/password-hasher under every host - never by password_verify() alone: Symfony prehashes a password longer than 72 bytes (or holding a NUL byte) before bcrypt, so password_verify(), and Laravel's Hash with it, would refuse those accounts. The same hasher says when to rehash (cost 13 here, 12 by Laravel's default: without it, each sign-in on the other host would rehash).

    5 methods

Omniauth\Testing

  • ArrayCache class

    A PSR-16 cache in a PHP array, its lifetimes read on the clock given: what CacheThrottle counts in for tests and a first try, with no cache library.

    9 methods

  • FixedClock class

    A clock that stands still until told: deadlines and windows tested to the second.

    4 methods

  • InMemoryEmailCodes class

    The e-mailed codes on TestAccount objects (InMemoryStore::emailCodes()).

    6 methods

  • InMemoryStore class

    Every store contract in memory, over TestAccount objects: tests, the harness's bare script, a first try in plain PHP. An application fills the same contracts on its tables (omnibase: Base\Auth\). The e-mailed codes and the TOTP secrets, whose contracts share method names, are emailCodes() and totpSecrets(), over the same accounts.

    25 methods

  • InMemoryTotpSecrets class

    The TOTP secrets on TestAccount objects (InMemoryStore::totpSecrets()), for omniauth/otp's tests.

    3 methods

  • ProviderContractTests trait

    What every identity provider does, as PHPUnit tests: a provider package's test case uses this trait, gives the provider and a way to make its server answer (recorded answers: no test reaches the network), and the suite checks the Provider contract:

    5 methods

  • RecordingSender class

    Sends nothing, keeps every message: the link or the code a test reads back.

    3 methods · 1 property

  • StubMethod class

    A first factor that passes for the account of a handle: a passkey or an identity provider without their library, so a host's tests (and the core's) drive the conductor and the policy. Proof: ['handle' => ...].

    3 methods

  • StubSecondFactor class

    A second factor (an authenticator application without otphp) enrolled for the handles given, passing on the code given. Proof: ['code' => ...].

    4 methods

  • TestAccount class

    An account for tests and first steps, every proof on it: what InMemoryStore reads and writes.

    1 method · 15 properties

Omniauth\TrustedDevice

  • TrustedDevices class

    A second factor already passed on this browser: the trusted_device cookie, written in scheb/2fa's very format so the devices trusted before the switch stay trusted - JWTs signed HS256 by the host's secret (Symfony's kernel.secret), claims iat, exp, "usr" (the account's identifier), "fwl" (the firewall), "vsn" (the account's trusted version), several tokens joined by ";". Encoded with PHP's own HMAC, byte for byte what lcobucci/jwt writes for scheb's JwtTokenEncoder (dates with their microseconds), and read as it reads them (signature, iat <= now < exp).

    8 methods · 2 constants

Omniauth

ArrayState class

final class ArrayState implements State ArrayState.php:12

The visitor's state in a PHP array: plain PHP (hand it $_SESSION by reference, or keep the array yourself) and tests. Symfony and Laravel keep it in their session (omnibridge/symfony, omnibridge/laravel).

ArrayState::__construct()

public function __construct(array &$values = [])

ArrayState::session()

public static function session(): self

Over $_SESSION (session_start() first): the attempt lives as long as the PHP session.

ArrayState::get()

public function get(string $key): mixed

ArrayState::set()

public function set(string $key, mixed $value): void

ArrayState::remove()

public function remove(string $key): void

ArrayState::all()

public function all(): array

Attempt class

final class Attempt Attempt.php:15

One visitor's attempt to sign in, kept by the host between two requests (Contract\State): the account found by the first factor, the factors passed, the second factors offered, and what a ceremony keeps between its beginning and its verification (a challenge, a code's deadline) - never in the response.

It holds the account's handle, not the account: the conductor loads the account again (Accounts::byHandle()) each time it reads the attempt back.

Attempt::__construct()

public function __construct(readonly int $startedAt, ?string $handle = null, array $passed = [], array $offered = [], array $kept = [], bool $remember = false)

Attempt::startedAt()

public function startedAt(): int

Attempt::account()

public function account(): ?object

The account the first factor found, or the account an enrolment is for.

Attempt::handle()

public function handle(): ?string

Attempt::passed()

public function passed(): array

Attempt::hasPassed()

public function hasPassed(Factor $factor): bool

Attempt::offered()

public function offered(): array

Attempt::remembers()

public function remembers(): bool

Attempt::keep()

public function keep(Factor|string $key, string|int|float|bool|array|null $value): void

Keeps what a ceremony needs at its verification (scalars and arrays: the attempt is serialised).

Attempt::kept()

public function kept(Factor|string $key): string|int|float|bool|array|null

Attempt::take()

public function take(Factor|string $key): string|int|float|bool|array|null

Reads what was kept and forgets it: a challenge serves once.

Attempt::restart()

public function restart(int $now): self

A new attempt that keeps only what ceremonies kept (a challenge begun before this first factor).

Attempt::toArray()

public function toArray(): array
Returns

what Contract\State keeps

Attempt::fromArray()

public static function fromArray(mixed $data): ?self
$data

what Contract\State gave back: null (or anything malformed) is no attempt

CacheThrottle class

final class CacheThrottle implements Throttle CacheThrottle.php:18

A fixed window per scope and key, counted in any PSR-6 pool or PSR-16 cache: plain PHP and tests. Under Symfony and Laravel, their rate limiters answer the same contract.

Read then written, not atomic: two requests at the same instant may both pass the last try. A host that needs it exact uses its rate limiter.

const LIMITS = ['password' => [5, 60], 'link.begin' => [3, 3600], 'email-code' => [5, 900], 'email-code.begin' => [3, 900], 'backup-code' => [5, 900], 'totp' => [5, 900], '…

Tries per window, in seconds. "*": any other scope. "<factor>.begin" limits what sends something (a link, a code).

CacheThrottle::__construct()

public function __construct(readonly CacheItemPoolInterface|CacheInterface $cache, array $limits = [], ?ClockInterface $clock = null, readonly string $prefix = 'omniauth.throttle.')
$limits

replace the defaults scope by scope

CacheThrottle::consume()

public function consume(string $scope, string $key): bool

CacheThrottle::reset()

public function reset(string $scope, string $key): void

Context class

final readonly class Context Context.php:9

What the host knows of the request, with no object of its framework: the same values under Symfony, Laravel and plain PHP.

public string $origin

https://lesringostars.localhost:8443: WebAuthn compares the origin.

public string $host

The relying party's identifier depends on it.

public ?string $ip

Throttling keys, devices.

public ?string $userAgent
public ?string $trustedDevice

The trusted_device cookie, as the host read it.

public string $firewall

The trusted-device tokens' "fwl" claim (scheb/2fa's firewall name).

Context::__construct()

public function __construct(public string $origin = '', public string $host = '', public ?string $ip = null, public ?string $userAgent = null, public ?string $trustedDevice = null, public string $firewall = 'main')

Enrolled class

final readonly class Enrolled Enrolled.php:6

What setting a factor up ended with; the secrets to show once (backup codes) when it worked.

public ?Failure $failure
public array $secrets
public ?string $reason

Enrolled::__construct()

public function __construct(public ?Failure $failure = null, public array $secrets = [], public ?string $reason = null)

Enrolled::done()

public static function done(array $secrets = []): self

Enrolled::failed()

public static function failed(Failure $failure, ?string $reason = null): self

Enrolled::isDone()

public function isDone(): bool

EnrolledSecondFactors class

final class EnrolledSecondFactors implements SecondFactorPolicy EnrolledSecondFactors.php:23

The core's second-factor rule: an account that set up a second factor (an authenticator application, the code by e-mail) must pass one of them - or a backup code - after a first factor of $after; a second factor already passed (a trusted device among them) is enough.

$after holds Marco's decisions of 2026-10-20: QA1, no second factor after a passkey (it already proves the device and the gesture that unlocks it: Passkey is not in the list); QA2, a second factor after an identity provider (OAuth is). Backup codes alone ask for nothing: they stand in for another second factor, as in scheb/2fa.

omnibase decorates it with its SecurityPolicy (a role that requires a second factor and has none is sent to enrol; the demo account never is).

const AFTER = [Factor::Password, Factor::Link, Factor::OAuth]

EnrolledSecondFactors::__construct()

public function __construct(readonly Methods $methods, readonly array $after = self::AFTER)
$after

the first factors a second one follows

EnrolledSecondFactors::required()

public function required(object $account, array $passed, Context $context): array

Enrolment class

final readonly class Enrolment Enrolment.php:12

Setting a factor up for an account, in the same two steps as a sign-in: begin() (a code sent, a secret, options to sign, or nothing), confirm() (what the visitor sent back; the secrets to show once).

public Factor $factor
public object $account

Enrolment::__construct()

public function __construct(Omniauth $omniauth, public Factor $factor, public object $account)

Enrolment::begin()

public function begin(Context $context, Proof|array $proof = []): Challenge

Enrolment::confirm()

public function confirm(Context $context, Proof|array $proof = []): Enrolled

Factor enum

enum Factor Factor.php:10

What a person proves when signing in, named once for every method, every policy and every host: the stable key a host's routes, settings and translations use.

case Password = 'password'

What one knows.

The mailbox: a link sent to the account's address.

case Passkey = 'passkey'

The device and the gesture or the code that unlocks it (omniauth/webauthn).

case OAuth = 'oauth'

An identity provider (Omniauth\OAuth\OAuthMethod).

case Totp = 'totp'

The authenticator application (omniauth/otp).

case EmailCode = 'email-code'

A code sent to the account's address once a first factor passed.

case BackupCode = 'backup-code'

One of the codes handed out once, each good once.

case TrustedDevice = 'trusted-device'

A second factor already passed on this browser: the trusted_device cookie.

Factor::role()

public function role(): Role

What it can be: a first factor (it finds the account) or a second (the account is known).

Factor::of()

public static function of(self|string $factor): self

Failure enum

enum Failure Failure.php:9

Why an attempt failed: stable keys a page translates (omnibase's "@notifications.login.*" catalogues), never a message.

case BadCredentials = 'bad-credentials'

No such account, or not that secret: one answer for both.

case BadCode = 'bad-code'

A code (e-mailed, backup, from an application) that is not the right one.

A sign-in link unknown, already used or past its time.

case Expired = 'expired'

What the ceremony began with is gone: a code past its time, an attempt older than its lifetime.

case Throttled = 'throttled'

Too many tries: the throttle refused before anything was checked.

case AccountRefused = 'account-refused'

The account exists and the proof is good, but it may not sign in now (Result::$reason: banned, locked...).

case Refused = 'refused'

A listener of Verifying or Beginning refused (omnishield: not a human); Result::$reason says its word.

case NoAttempt = 'no-attempt'

A second factor with no first one passed before it in this attempt.

case NotOffered = 'not-offered'

A second factor the attempt did not offer.

case NotEnrolled = 'not-enrolled'

A factor this account has not set up.

Message class

final readonly class Message Message.php:10

What a method asks the host to send to an account: a sign-in link, a code.

The host's Sender renders it (omnibase: its Notifier and the "@Base/security/email/*" templates).

public Factor $factor
public string $secret

Link: the link's token; email-code: the code. Never logged, never stored as is.

public \DateTimeImmutable $until

Message::__construct()

public function __construct(public Factor $factor, public string $secret, public \DateTimeImmutable $until)

Omniauth class

final class Omniauth Omniauth.php:39

The conductor: who signs in, by which factors, and what remains to pass.

Whatever the method, verify() does the same sequence - throttle, Verifying, the method, Accounts::check(), the second-factor rule (an ambient factor tried on the way), SignedIn or SignInFailed, the attempt kept or cleared - and that single sequence is what makes the behaviour the same under every host. The host keeps the request, the response, its session cookie (renewed at each sign-in), its remember-me and its pages; it checks the form's CSRF token (Omnibridge\Contract\Tokens) before calling.

const ATTEMPT = 'omniauth.attempt'

Where Contract\State keeps the sign-in attempt.

const ENROLMENT = 'omniauth.enrolment.'

Where it keeps an enrolment, per factor.

const LIFETIME = 600

An attempt older than this (seconds) is gone.

Omniauth::__construct()

public function __construct(readonly Accounts $accounts, readonly Methods $methods, readonly State $state, ?SecondFactorPolicy $policy = null, readonly ?Throttle $throttle = null, readonly ?EventDispatcherInterface $events = null, ?ClockInterface $clock = null, readonly int $lifetime = self::LIFETIME)

Omniauth::methods()

public function methods(): Methods

Omniauth::attempt()

public function attempt(): ?Attempt

The sign-in attempt in progress (the second-factor page reads Attempt::offered()), null when none.

Omniauth::abandon()

public function abandon(): void

Forgets the attempt in progress: abandoned, or signed out.

Omniauth::begin()

public function begin(Factor|string $factor, Context $context, Proof|array $proof = []): Challenge

Begins a ceremony: a link sent for an address, a code sent for the account the first factor found, options to sign. What the method keeps goes into the attempt.

Omniauth::verify()

public function verify(Factor|string $factor, Context $context, Proof|array $proof = []): Result

Verifies a proof: a first factor starts a new attempt, a second one goes on with the attempt in progress.

Omniauth::enrol()

public function enrol(Factor|string $factor, object $account): Enrolment

Sets a factor up for an account, from its settings: begin(), then confirm().

Omniauth::revoke()

public function revoke(Factor|string $factor, object $account): Enrolled

Turns a factor off for an account (TrustedDevice: forgets every browser). A listener of Revoking may refuse; turning off any other factor makes the trusted devices fall back too.

PendingIdentity class

final readonly class PendingIdentity PendingIdentity.php:9

Someone a provider vouched for whom no account is linked to: what a sign-up form fills in, and what links the account once it exists.

public string $provider
public string $subject

The person's stable identifier at the provider ("sub").

public ?string $email
public bool $emailVerified

Whether the provider says the address is verified (Google's email_verified).

public ?string $name

PendingIdentity::__construct()

public function __construct(public string $provider, public string $subject, public ?string $email = null, public bool $emailVerified = false, public ?string $name = null)

Proof class

final readonly class Proof Proof.php:12

What the visitor sent: ['identifier' => ..., 'password' => ...], ['code' => ...], ['token' => ...], a WebAuthn assertion's JSON under 'json', an OAuth return's 'code' and 'state'. Two keys mean the same to every method: 'remember' (the host sets its remember-me) and 'trust' (a trusted-device cookie after a second factor).

Proof::__construct()

public function __construct(array $values = [])

Proof::of()

public static function of(self|array $proof): self

Proof::string()

public function string(string $key): string

The value as a string; '' when it is missing or is not a scalar.

Proof::bool()

public function bool(string $key): bool

Proof::has()

public function has(string $key): bool

Proof::json()

public function json(): string

The JSON a browser posted (a WebAuthn assertion): the 'json' key, or every value encoded.

Proof::all()

public function all(): array

Result class

final readonly class Result Result.php:10

What a verification ended with: who, by which factors, what remains to pass, and what the host does next (open its session, set its remember-me, set the trusted-device cookie).

public Status $status
public ?object $account
public array $passed

The factors passed in this attempt, in their order.

public array $offered

SecondFactorRequired: what the second-factor page offers (any one suffices).

public ?Failure $failure
public ?string $reason

AccountRefused: the account's reason (banned, locked...); Refused: the listener's.

public bool $remember

The host sets its own remember-me.

public ?string $trustDevice

The trusted_device cookie's new value, to set as is (scheb/2fa's format).

public ?PendingIdentity $identity

IdentityUnknown: what a sign-up form fills in.

Result::__construct()

public function __construct(public Status $status, public ?object $account = null, public array $passed = [], public array $offered = [], public ?Failure $failure = null, public ?string $reason = null, public bool $remember = false, public ?string $trustDevice = null, public ?PendingIdentity $identity = null)

Result::isAuthenticated()

public function isAuthenticated(): bool

Result::needsSecondFactor()

public function needsSecondFactor(): bool

Result::failed()

public static function failed(Failure $failure, ?string $reason = null, ?object $account = null): self

Role enum

enum Role Role.php:6

A first factor finds the account; a second one is asked of an account already found.

case First = 'first'
case Second = 'second'

Status enum

enum Status Status.php:6

Where an attempt stands after a verification.

case Authenticated = 'authenticated'

Signed in: the host opens its session.

case SecondFactorRequired = 'second-factor-required'

A first factor passed; one of Result::$offered must follow.

case IdentityUnknown = 'identity-unknown'

A provider vouched for someone no account is linked to (Result::$identity: what a sign-up form fills in).

case Failed = 'failed'

Nothing passed: Result::$failure says why.

SystemClock class

final class SystemClock implements ClockInterface SystemClock.php:8

The machine's clock, when the host gives none (Testing\FixedClock in tests).

SystemClock::now()

public function now(): \DateTimeImmutable

Omniauth\BackupCode

BackupCodeMethod class

final class BackupCodeMethod implements SecondFactor, Enrolling, Revocable BackupCodeMethod.php:28

Backup codes: handed out once (8 by default, "A1B2C3-D4E5F6" as omnibase draws them), kept as password_hash() hashes - the very hashes omnibase's user.backupCodes holds - and each good once: the one that serves is removed. They stand in for another second factor; alone, they ask for none (EnrolledSecondFactors).

verify(): ['code' => ...]; enrolConfirm() draws a new set, replaces the old one and returns the codes, to show once.

BackupCodeMethod::__construct()

public function __construct(readonly BackupCodes $codes, readonly int $count = 8)

BackupCodeMethod::factor()

public function factor(): Factor

BackupCodeMethod::enrolled()

public function enrolled(object $account): bool

BackupCodeMethod::verify()

public function verify(Attempt $attempt, Proof $proof, Context $context): Verdict

BackupCodeMethod::enrolBegin()

public function enrolBegin(Attempt $enrolment, Proof $proof, Context $context): Challenge

BackupCodeMethod::enrolConfirm()

public function enrolConfirm(Attempt $enrolment, Proof $proof, Context $context): Enrolled

BackupCodeMethod::revoke()

public function revoke(object $account): void

BackupCodeMethod::draw()

public static function draw(int $count): array

Omniauth\Contract

Accounts interface

interface Accounts Accounts.php:11

The application's accounts: opaque objects to omniauth, which implement nothing of it (omnibase fills this in on its UserRepository).

Accounts::byIdentifier()

public function byIdentifier(string $identifier): ?object

By what the person types: an address, a user name.

Accounts::byHandle()

public function byHandle(string $handle): ?object

By the stable handle (the id; it is also the passkeys' userHandle).

Accounts::handle()

public function handle(object $account): string

Accounts::identifier()

public function identifier(object $account): string

What the account is known by to its host: Symfony's getUserIdentifier().

The trusted-device tokens carry it (scheb/2fa's "usr" claim).

Accounts::displayName()

public function displayName(object $account): string

Accounts::email()

public function email(object $account): ?string

Accounts::check()

public function check(object $account): void

May the account sign in now? Throws AccountRefused (banned, locked, restricted, disabled...) with a stable reason the page translates.

Throws

AccountRefused

BackupCodes interface

interface BackupCodes BackupCodes.php:6

The backup codes' hashes, password_hash()'s (omnibase: user.backupCodes, a JSON list).

BackupCodes::hashes()

public function hashes(object $account): array

BackupCodes::replace()

public function replace(object $account, array $hashes): void

EmailCodes interface

interface EmailCodes EmailCodes.php:10

The code sent by e-mail (omnibase: user.emailAuthEnabled, user.emailAuthCode). The store keeps the code's keyed hash, never the code; its deadline is kept in the attempt.

EmailCodes::enabled()

public function enabled(object $account): bool

EmailCodes::enable()

public function enable(object $account): void

EmailCodes::disable()

public function disable(object $account): void

EmailCodes::remember()

public function remember(object $account, string $hash): void

EmailCodes::remembered()

public function remembered(object $account): ?string

EmailCodes::forget()

public function forget(object $account): void

Identities interface

interface Identities Identities.php:11

The accounts linked to an identity provider, by the person's identifier there ("sub"); read by Omniauth\OAuth\OAuthMethod (omnibase: the userIdentity table).

One subject of a provider has one account; an account has one identity per provider.

Identities::owner()

public function owner(string $provider, string $subject): ?object
public function link(object $account, string $provider, string $subject, ?string $email = null, bool $emailVerified = false): void

Links the account to that identity, with what the provider said of the address at that moment ($email, and whether it vouched for it).

public function unlink(object $account, string $provider): void

Unlinks the account from that provider (the settings' "disconnect"); nothing when it was not linked.

Identities::used()

public function used(string $provider, string $subject, \DateTimeImmutable $at): void

The provider vouched for that identity again (a sign-in by it): its last-used date.

Identities::providers()

public function providers(object $account): array
Returns

the providers the account is linked to

LinkTokens interface

interface LinkTokens LinkTokens.php:9

The sign-in links in flight. The store sees their SHA-256, never their value: whoever reads the table cannot sign in with it.

LinkTokens::issue()

public function issue(object $account, string $hash, \DateTimeImmutable $until): void

Keeps a link issued to the account until that moment (beside the others, or in their place: the store's choice).

LinkTokens::consume()

public function consume(string $hash, \DateTimeImmutable $now): ?object

The account the link of that hash was issued to, when it is still valid at $now; the link is used up on the way: it serves once.

Passwords interface

interface Passwords Passwords.php:6

The accounts' password hashes (omnibase: user.password).

Passwords::hash()

public function hash(object $account): ?string

The hash as the hasher wrote it, or null when the account has no password.

Passwords::update()

public function update(object $account, string $hash): void

Keeps a new hash: a rehash at sign-in, or a new password.

PlainLinkTokens interface

interface PlainLinkTokens PlainLinkTokens.php:11

For the switch only: the links issued before omniauth were kept in clear (omnibase's userToken "login-token"). A LinkTokens store that still holds some also implements this, and they are read as they are until they expire; once none is left, the store stops implementing it.

PlainLinkTokens::consumePlain()

public function consumePlain(string $value, \DateTimeImmutable $now): ?object

SecondFactorPolicy interface

interface SecondFactorPolicy SecondFactorPolicy.php:9

The second-factor rule: what an account must still pass.

SecondFactorPolicy::required()

public function required(object $account, array $passed, Context $context): array

The second factors this account must still pass after $passed (none: it is done).

Returns

those it may present (any one suffices)

Sender interface

interface Sender Sender.php:8

Sends a sign-in link or a code to an account (omnibase: its Notifier; tests: Testing\RecordingSender).

Sender::send()

public function send(object $account, Message $message): void

State interface

interface State State.php:10

The visitor's own state between two requests: the session under Symfony and Laravel, Omniauth\ArrayState in plain PHP and tests. Values are arrays of scalars.

State::get()

public function get(string $key): mixed

State::set()

public function set(string $key, mixed $value): void

State::remove()

public function remove(string $key): void

Throttle interface

interface Throttle Throttle.php:12

Limits the tries: Symfony's RateLimiterFactory, Laravel's RateLimiter, Omniauth\CacheThrottle on any PSR-6 or PSR-16 cache.

A scope names what is limited ("password", "email-code", "link.begin"...), a key whom (an identifier and an address, an account).

Throttle::consume()

public function consume(string $scope, string $key): bool

Counts one try; false when the limit is reached (nothing is checked then).

Throttle::reset()

public function reset(string $scope, string $key): void

Forgets the tries: after a success.

TotpSecrets interface

interface TotpSecrets TotpSecrets.php:6

The authenticator applications' secrets, base32 (omnibase: user.secret); read by omniauth/otp (SealedTotpSecrets seals them at rest).

TotpSecrets::secret()

public function secret(object $account): ?string

TotpSecrets::enable()

public function enable(object $account, string $secret): void

TotpSecrets::disable()

public function disable(object $account): void

TrustedVersion interface

interface TrustedVersion TrustedVersion.php:9

The trusted devices' version (omnibase: user.trustedTokenVersion, scheb's TrustedDeviceInterface): a token of another version no longer counts.

TrustedVersion::version()

public function version(object $account): int

TrustedVersion::revokeAll()

public function revokeAll(object $account): void

Raises the version: every device trusted so far falls back to the second factor.

Omniauth\EmailCode

EmailCodeMethod class

final class EmailCodeMethod implements Challenging, SecondFactor, Enrolling, Revocable EmailCodeMethod.php:35

A code sent by e-mail once a first factor passed: 6 digits (as scheb/2fa draws them), good for 10 minutes and once. By Marco's decision QA5 (2026-10-20) the store keeps its hash, never the code: an HMAC-SHA256 keyed by the host's secret and bound to the account, compared in constant time; the deadline is kept in the attempt.

begin() sends it (the second-factor page calls it, or a "send again" button); verify(): ['code' => ...]. Enrolment: enrolBegin() sends a code, enrolConfirm() checks it and turns the factor on.

const LIFETIME = 600

EmailCodeMethod::__construct()

public function __construct(readonly Accounts $accounts, readonly EmailCodes $codes, readonly Sender $sender, readonly string $secret, readonly int $digits = 6, readonly int $lifetime = self::LIFETIME, ?ClockInterface $clock = null)

EmailCodeMethod::factor()

public function factor(): Factor

EmailCodeMethod::enrolled()

public function enrolled(object $account): bool

EmailCodeMethod::begin()

public function begin(Attempt $attempt, Proof $proof, Context $context): Challenge

EmailCodeMethod::verify()

public function verify(Attempt $attempt, Proof $proof, Context $context): Verdict

EmailCodeMethod::enrolBegin()

public function enrolBegin(Attempt $enrolment, Proof $proof, Context $context): Challenge

EmailCodeMethod::enrolConfirm()

public function enrolConfirm(Attempt $enrolment, Proof $proof, Context $context): Enrolled

EmailCodeMethod::revoke()

public function revoke(object $account): void

EmailCodeMethod::hash()

public function hash(object $account, string $code): string

What the store keeps of a code.

Omniauth\Event

AccountRefused class

final class AccountRefused AccountRefused.php:9

The proof was good but the account may not sign in (omnibase: the "welcome back" e-mail of a disabled account).

public readonly object $account
public readonly string $reason
public readonly Factor $factor
public readonly Context $context

AccountRefused::__construct()

public function __construct(public readonly object $account, public readonly string $reason, public readonly Factor $factor, public readonly Context $context)

Beginning class

final class Beginning Beginning.php:10

Before a ceremony begins (a link or a code is about to be sent): a listener may refuse.

public readonly Factor $factor
public readonly Context $context
public readonly Attempt $attempt
public readonly ?string $identifier

Beginning::__construct()

public function __construct(public readonly Factor $factor, public readonly Context $context, public readonly Attempt $attempt, public readonly ?string $identifier = null)

Beginning::refuse()

public function refuse(string $reason): void

Beginning::refusal()

public function refusal(): ?string

CredentialAdded class

final class CredentialAdded CredentialAdded.php:8

A factor was set up, or a password changed.

public readonly object $account
public readonly Factor $factor

CredentialAdded::__construct()

public function __construct(public readonly object $account, public readonly Factor $factor)

CredentialRemoved class

final class CredentialRemoved CredentialRemoved.php:8

A factor was turned off; the trusted devices fell back with it.

public readonly object $account
public readonly Factor $factor

CredentialRemoved::__construct()

public function __construct(public readonly object $account, public readonly Factor $factor)

Revoking class

final class Revoking Revoking.php:12

Before a factor is turned off: omnibase's rule ("the administrator wins": a role that requires a second factor keeps it) refuses here, so a hidden button and a forged POST meet the same predicate.

public readonly object $account
public readonly Factor $factor

Revoking::__construct()

public function __construct(public readonly object $account, public readonly Factor $factor)

Revoking::refuse()

public function refuse(string $reason): void

Revoking::refusal()

public function refusal(): ?string

SecondFactorRequired class

final class SecondFactorRequired SecondFactorRequired.php:9

A first factor passed and a second one is due.

public readonly object $account
public readonly array $passed
public readonly array $offered
public readonly Context $context

SecondFactorRequired::__construct()

public function __construct(public readonly object $account, public readonly array $passed, public readonly array $offered, public readonly Context $context)

SignInFailed class

final class SignInFailed SignInFailed.php:10

A verification failed: logged with the address and the factor, never with the secret; SignInGuard counts it.

public readonly Factor $factor
public readonly Failure $failure
public readonly Context $context
public readonly ?string $identifier
public readonly ?object $account
public readonly ?string $reason

SignInFailed::__construct()

public function __construct(public readonly Factor $factor, public readonly Failure $failure, public readonly Context $context, public readonly ?string $identifier = null, public readonly ?object $account = null, public readonly ?string $reason = null)

SignedIn class

final class SignedIn SignedIn.php:9

Signed in: omnibase's UserTracker, the "new device" e-mail, the account brought up to date listen here.

public readonly object $account
public readonly array $passed
public readonly Context $context

SignedIn::__construct()

public function __construct(public readonly object $account, public readonly array $passed, public readonly Context $context)

Verifying class

final class Verifying Verifying.php:14

Before a method checks a proof (and after the throttle let it through): a listener may refuse - omnishield when the visitor is not a human, omnibase's SignInGuard when a captcha is due.

public readonly Factor $factor
public readonly Context $context
public readonly Attempt $attempt
public readonly ?string $identifier

What the visitor typed to be found (first factors), or null.

Verifying::__construct()

public function __construct(public readonly Factor $factor, public readonly Context $context, public readonly Attempt $attempt, public readonly ?string $identifier = null)

Verifying::refuse()

public function refuse(string $reason): void

Verifying::refusal()

public function refusal(): ?string

Omniauth\Exception

AccountRefused class

final class AccountRefused extends RuntimeException implements OmniauthException AccountRefused.php:6

Thrown by Accounts::check(): the account may not sign in now, for a stable reason the page translates.

public readonly string $reason

AccountRefused::__construct()

public function __construct(public readonly string $reason, ?\Throwable $previous = null)

OmniauthException interface

interface OmniauthException extends Throwable OmniauthException.php:6

Every exception omniauth throws.

UnknownMethod class

final class UnknownMethod extends InvalidArgumentException implements OmniauthException UnknownMethod.php:8

A factor no method was mounted for: a host's mistake, not a visitor's.

UnknownMethod::for()

public static function for(Factor|string $factor, array $mounted): self

final class LinkMethod implements Challenging LinkMethod.php:32

A sign-in link sent to the account's address, by Marco's decision QA4 (2026-10-20): good for 15 minutes, once, and kept hashed (SHA-256) - the store never sees its value. Each request sends a new link (the conductor limits them: "link.begin", 3 an hour by default), and the answer is the same whether the address has an account or not.

begin(): ['identifier' => the address]; verify(): ['token' => the link's token]. The link's page signs in a GET from a mailbox; the host's remember-me follows (as omnibase's link did).

public function __construct(readonly Accounts $accounts, readonly LinkTokens $tokens, readonly Sender $sender, readonly int $lifetime = self::LIFETIME, ?ClockInterface $clock = null)
public function factor(): Factor
public function begin(Attempt $attempt, Proof $proof, Context $context): Challenge
public function verify(Attempt $attempt, Proof $proof, Context $context): Verdict
public static function hash(string $value): string

What the store keeps of a link's token.

Omniauth\Method

Ambient interface

interface Ambient extends Method Ambient.php:13

A second factor the visitor does not present: it is read from the context (the trusted-device cookie). The conductor tries it by itself when a second factor is required, and asks it what to leave on the browser when the visitor asked to be trusted after passing another one.

Ambient::remember()

public function remember(object $account, Context $context): string

What the host keeps on the browser (a cookie's value) so that this factor passes next time.

Challenge class

final readonly class Challenge Challenge.php:8

What the host gives back after a beginning: JSON for a script, an address to go to, "it was sent", or why not.

const NONE = 'none'
const JSON = 'json'
const REDIRECT = 'redirect'
const SENT = 'sent'
const FAILED = 'failed'

Challenge::none()

public static function none(array $data = []): self

Nothing to give: the visitor goes on to the next step.

Challenge::json()

public static function json(string $json): self

Options for a script (WebAuthn's), as they are.

Challenge::redirect()

public static function redirect(string $url): self

Challenge::sent()

public static function sent(array $data = []): self

A link or a code went out - or the address has no account: the same answer for both.

Challenge::failed()

public static function failed(Failure $failure, ?string $reason = null): self

Challenge::hasFailed()

public function hasFailed(): bool

Challenging interface

interface Challenging extends Method Challenging.php:14

A method that first gives something to the browser or the mailbox: options to sign, an address to go to, a link or a code sent. What it must remember goes into the attempt (Attempt::keep()), never into the challenge.

Challenging::begin()

public function begin(Attempt $attempt, Proof $proof, Context $context): Challenge
$proof

what the visitor sent to begin with (the link's address), often nothing

Enrolling interface

interface Enrolling extends Method Enrolling.php:15

A method an account sets up from its settings, in the same two steps as a sign-in. The attempt is the enrolment's own (its account identified), kept apart from any sign-in.

Enrolling::enrolBegin()

public function enrolBegin(Attempt $enrolment, Proof $proof, Context $context): Challenge

What the visitor needs to set it up: a code sent, a secret and its otpauth:// address, options to sign, or nothing.

Enrolling::enrolConfirm()

public function enrolConfirm(Attempt $enrolment, Proof $proof, Context $context): Enrolled

Checks what the visitor sent back and keeps the credential; the secrets to show once, if any.

Method interface

interface Method Method.php:11

A way to sign in: it checks a proof within an attempt, and touches neither request nor response.

Method::factor()

public function factor(): Factor

Method::verify()

public function verify(Attempt $attempt, Proof $proof, Context $context): Verdict

Methods class

final class Methods implements IteratorAggregate, Countable Methods.php:9

The methods a host mounted, one per factor.

Methods::__construct()

public function __construct(iterable $methods = [])

Methods::add()

public function add(Method $method): void

Methods::has()

public function has(Factor|string $factor): bool

Methods::get()

public function get(Factor|string $factor): Method
Throws

UnknownMethod

Methods::secondFactors()

public function secondFactors(): array
Returns

the second factors an account sets up (not the ambient ones)

Methods::ambient()

public function ambient(): array

Methods::getIterator()

public function getIterator(): \ArrayIterator

Methods::count()

public function count(): int

Revocable interface

interface Revocable extends Method Revocable.php:6

A factor an account can turn off (or, for trusted devices, forget every browser).

Revocable::revoke()

public function revoke(object $account): void

SecondFactor interface

interface SecondFactor extends Method SecondFactor.php:6

A second factor an account sets up: the policy asks it whether this one has.

SecondFactor::enrolled()

public function enrolled(object $account): bool

Verdict class

final readonly class Verdict Verdict.php:9

What a method concluded of a proof.

Verdict::passed()

public static function passed(object $account, bool $remember = false): self
$remember

the method itself asks the host for its remember-me (a link from a mailbox)

Verdict::refused()

public static function refused(Failure $failure, ?string $reason = null): self

Verdict::expired()

public static function expired(): self

Verdict::unknown()

public static function unknown(PendingIdentity $identity): self

A provider vouched for someone no account is linked to.

Verdict::hasPassed()

public function hasPassed(): bool

Omniauth\OAuth

Authorization class

final readonly class Authorization Authorization.php:18

What one trip to a provider is bound to, drawn at the beginning and kept in the attempt (never in the answer): the state the return must bring back (against a forged return: login CSRF), the PKCE verifier whose S256 challenge goes in the authorization address (against a code stolen or injected: only the one who began can exchange it, RFC 7636), and the nonce an ID token must carry (OpenID Connect, against a token replayed) - and the origin of the request that began the trip, against which a provider configured with a return path ("/login/google/callback") makes its return address: the host the visitor is on at that request, not the first one a long-running worker served, and the same address again at the exchange (RFC 6749 §4.1.3: the token request repeats the authorization's).

public string $state
public string $verifier

43 characters of base64url: RFC 7636 §4.1 asks 43 to 128.

public string $nonce
public ?string $origin

The request's scheme and host at the beginning ("https://example.org:8443"), null when it is not known.

Authorization::__construct()

public function __construct(public string $state, public string $verifier, public string $nonce, public ?string $origin = null)

Authorization::fresh()

public static function fresh(?string $origin = null): self

Three new values, 32 random bytes each; the beginning request's origin when the host knows it.

Authorization::returnAddress()

public function returnAddress(string $configured): string

A provider's return address for this trip: an absolute address as it is; a path ("/login/google/callback") on the trip's origin.

Authorization::challenge()

public function challenge(): string

The verifier's S256 challenge: BASE64URL(SHA-256(verifier)), RFC 7636 §4.2.

Authorization::matches()

public function matches(string $state): bool

Whether a return brings back this state: compared in constant time.

Authorization::toArray()

public function toArray(): array
Returns

what the attempt keeps

Authorization::fromArray()

public static function fromArray(mixed $kept): ?self

What the attempt gave back; null when anything is missing.

Authorization::base64url()

public static function base64url(string $bytes): string

Identity class

final readonly class Identity Identity.php:11

Whom a provider vouched for, read from its ID token or its userinfo - never from the return address's parameters.

public string $provider
public string $subject

The person's stable identifier at the provider ("sub"): what links an account.

public ?string $email
public bool $emailVerified

Whether the provider says the address is the person's (Google's email_verified); false when it says nothing.

public ?string $name
public array $claims

what the provider said, as it said it

Identity::__construct()

public function __construct(public string $provider, public string $subject, public ?string $email = null, public bool $emailVerified = false, public ?string $name = null, public array $claims = [])
$claims

what the provider said, as it said it

Identity::pending()

public function pending(): PendingIdentity

What the conductor's IdentityUnknown result carries to the host's sign-up.

Identity::verified()

public static function verified(mixed $claim): bool

A provider's "true": a JSON boolean, or the string some of them send.

NoSecondFactorLinking class

final readonly class NoSecondFactorLinking implements EmailLinking NoSecondFactorLinking.php:15

QA3's rule for the address: an account that set up a second factor (an authenticator application, the code by e-mail) is not linked by its address - whoever holds the provider's account would then be one factor away from it for good. It reads the methods mounted, so it can be built before the OAuth method is added to them.

NoSecondFactorLinking::__construct()

public function __construct(Methods $methods)

NoSecondFactorLinking::allows()

public function allows(object $account, Identity $identity): bool

OAuthMethod class

final class OAuthMethod implements Challenging, Enrolling OAuthMethod.php:67

Signing in through an identity provider (OAuth 2, OpenID Connect), a first factor: begin() draws a state, a PKCE verifier and a nonce, keeps them in the attempt and answers with the provider's authorization address (Challenge::redirect()); verify() takes the return's ['code' => …, 'state' => …] (or its ['error' => …]), takes what the attempt kept - once: a return comes back once -, compares the state in constant time, has the provider exchange the code with the verifier and read the person (an ID token or a userinfo, never the address's parameters), then finds the account (Marco's decision QA3):

1. the account linked to that provider's subject ("sub"), whatever its address says now; 2. else, only when the provider says the address is verified, the account of that address - linked there and then, unless $byEmail refuses it (false: never; an EmailLinking: NoSecondFactorLinking, omnibase's rule), and never an account already linked to another subject of that provider: then not-enrolled (reason link-from-settings): the person signs in otherwise and links the provider from the settings; 3. else nobody: the conductor answers Status::IdentityUnknown with the PendingIdentity a sign-up form fills in - no account is ever created here (lesringostars' transient account and its 500 "User identifier is NULL" are gone); once the host has made the account, adopt() links it.

Then the conductor does what it does after any first factor: the account checked, and the second factor asked when the account has one (QA2: EnrolledSecondFactors::AFTER holds OAuth).

From the settings, enrolBegin()/enrolConfirm() link a provider to the account signed in (['provider' => 'google']): the same trip, the subject refused when another account holds it; Identities::unlink() undoes it. A link keeps the address the provider gave then and whether it vouched for it; each sign-in by an identity marks it used (Identities::used()).

Refusals are bad-credentials with a stable reason: unknown-provider, state-mismatch, provider-denied (the person said no), provider-error, missing-code, exchange-failed (a code refused - a PKCE verifier that is not the code's among the causes), invalid-id-token, no-subject, hosted-domain, linked-elsewhere, other-identity; and expired when the attempt kept nothing (no beginning, a return replayed, ten minutes gone).

OAuthMethod::__construct()

public function __construct(readonly Accounts $accounts, readonly Identities $identities, readonly Providers $providers, readonly EmailLinking|bool $byEmail = true, ?ClockInterface $clock = null)

OAuthMethod::factor()

public function factor(): Factor

OAuthMethod::providers()

public function providers(): Providers

OAuthMethod::begin()

public function begin(Attempt $attempt, Proof $proof, Context $context): Challenge

OAuthMethod::verify()

public function verify(Attempt $attempt, Proof $proof, Context $context): Verdict

OAuthMethod::enrolBegin()

public function enrolBegin(Attempt $enrolment, Proof $proof, Context $context): Challenge

OAuthMethod::enrolConfirm()

public function enrolConfirm(Attempt $enrolment, Proof $proof, Context $context): Enrolled

OAuthMethod::adopt()

public function adopt(object $account, PendingIdentity $identity): Enrolled

After a sign-up from Status::IdentityUnknown: links the new account to the identity the result carried - as the host kept it on its side (its session), never as a form posted it back.

Omniauth\OAuth\Contract

EmailLinking interface

interface EmailLinking EmailLinking.php:15

May an account be linked to an identity by its address alone - the provider having said the address is verified, and no account linked to that identity yet? Marco's decision QA3: never for an account that has a second factor or an administration role (NoSecondFactorLinking says the first; omnibase's rule, step AU4, adds its roles); the person then signs in otherwise and links the provider from the account's settings.

EmailLinking::allows()

public function allows(object $account, Identity $identity): bool

Omniauth\OAuth\Exception

ProviderError class

final class ProviderError extends RuntimeException implements OmniauthException ProviderError.php:14

The provider did not vouch for anyone: the code refused at the exchange, an ID token not for this client, past its time or without the nonce, a profile without its subject. $reason is the stable word the method answers with; the message (and the previous exception: league's, Guzzle's) is for the server's log, not the page.

const EXCHANGE = 'exchange-failed'
const ID_TOKEN = 'invalid-id-token'
const SUBJECT = 'no-subject'
const HOSTED_DOMAIN = 'hosted-domain'
public readonly string $reason

ProviderError::__construct()

public function __construct(public readonly string $reason, string $message = '', ?\Throwable $previous = null)

Omniauth\OAuth\Provider

LeagueProvider class

class LeagueProvider implements Provider LeagueProvider.php:34

A league/oauth2-client provider as omniauth's Provider - the ground of the provider packages built on league (omniauth/google's GoogleProvider), and any other league provider by hand (league/oauth2-facebook, a GenericProvider on a provider's three addresses...) until it has a package of its own. league/oauth2-client is not required by omniauth: the provider packages require it (this class loads without it, and is only usable with it). The authorization address with the state and the S256 challenge omniauth drew, the code exchanged with its verifier, the person read from the resource owner's details through the access token. $fields names the details' keys: the subject (its resource owner's getId() when null), the address, whether it is verified (null: the provider says nothing, so it is not - no account is ever linked by such an address), the name.

PKCE is omniauth's, not league's: leave the league provider's "pkceMethod" unset (it would draw a verifier of its own that no attempt keeps).

$redirectUri, when given, is the return address of each trip: an absolute address, or a path the origin of the request that began the trip completes (Authorization::returnAddress()) - sent in the authorization address and again at the exchange, whatever the league provider was built with.

const FIELDS = ['subject' => null, 'email' => 'email', 'verified' => null, 'name' => 'name']

LeagueProvider::__construct()

public function __construct(protected readonly string $name, protected readonly AbstractProvider $league, protected readonly array $scopes = [], array $fields = [], protected readonly ?string $redirectUri = null)

LeagueProvider::name()

public function name(): string

LeagueProvider::league()

public function league(): AbstractProvider

The league provider underneath (its HTTP client, its options).

LeagueProvider::authorizationUrl()

public function authorizationUrl(Authorization $authorization): string

LeagueProvider::identify()

public function identify(string $code, Authorization $authorization): Identity

Provider interface

interface Provider Provider.php:14

An identity provider: where to send the visitor, then whom the code that came back vouches for. Its return address is its own, fixed at its configuration (never taken from a request).

Provider::name()

public function name(): string

Its key: the {provider} of the host's routes, Identities' $provider ("google").

Provider::authorizationUrl()

public function authorizationUrl(Authorization $authorization): string

The address to send the visitor to, carrying the state, the PKCE challenge and the nonce.

Provider::identify()

public function identify(string $code, Authorization $authorization): Identity

Exchanges the code (with the PKCE verifier) and reads whom it vouches for.

Throws

ProviderError

ProviderFactory interface

interface ProviderFactory ProviderFactory.php:10

Makes the providers of one kind from a host's configuration: each provider package has its own (omniauth/google's GoogleFactory), so this package needs no OAuth library. Providers::configured() asks each factory in turn.

ProviderFactory::create()

public function create(string $name, array $entry): ?Provider

The provider of this entry, or null when the entry is not of this factory's kind.

$entry

the entry as configured: client_id, client_secret, redirect_uri (an absolute address, or a path the trip's origin completes), scopes, type, and the kind's own keys

Providers class

final class Providers implements IteratorAggregate, Countable Providers.php:12

The providers a host serves, by name. Marco's decisions: Google alone today (QA7), and one package per provider (2026-10-21) - omniauth/google first; configured() makes them through each package's factory.

Providers::__construct()

public function __construct(Provider ...$providers)

Providers::configured()

public static function configured(array $config, ProviderFactory ...$factories): self

From a host's configuration (omniauth.yaml's "oauth" tree, config/omniauth.php's):

google: { client_id: …, client_secret: …, redirect_uri: /login/google/callback, scopes: [openid, email, profile] }

Each entry is made by the first factory that knows its kind (omniauth/google's GoogleFactory for "google": one package per provider). An entry with no client_id is left out: a provider without credentials is not served. Its redirect_uri is an absolute address, or a path the origin of each trip's request completes (Authorization::returnAddress()): under a long-running worker the visitor returns to the host they left from.

Providers::fromConfig()

public static function fromConfig(array $config, ?ClockInterface $clock = null, array $collaborators = []): self

use Providers::configured() with omniauth/google's GoogleFactory: Google moved to its own package (this one requires no OAuth library any more). Kept for one release; it serves Google entries alone.

$collaborators

league's, for every provider (an "httpClient")

Providers::has()

public function has(string $name): bool

Providers::get()

public function get(string $name): ?Provider

Providers::names()

public function names(): array
Returns

what the sign-in page offers

Providers::getIterator()

public function getIterator(): \ArrayIterator

Providers::count()

public function count(): int

Omniauth\OAuth\Testing

FakeProvider class

final class FakeProvider implements Provider FakeProvider.php:26

An authorization server in memory, for a host's tests and a first try: it answers the authorization address as a provider does once the person consented (consent(): the return's code and state), and exchanges a code as a provider does - once, for the client and the return address it was given to, and only with the PKCE verifier whose S256 challenge the authorization carried (RFC 7636 §4.6); the nonce is the one the address carried. So a test signs in with no network, and a forged or injected return fails here as it would at Google.

Its return address may be a path ("/login/google/callback"): each trip's address is then that path on the origin the trip began at (Authorization::returnAddress()), and a code is exchanged only for the address it was given to - as Google compares the token request's redirect_uri with the authorization's.

const AUTHORIZE = 'https://provider.test/authorize'
public array $exchanges = []
public readonly string $redirectUri
public readonly string $clientId

FakeProvider::__construct()

public function __construct(readonly string $name = 'google', public readonly string $redirectUri = 'https://example.org/login/google/callback', public readonly string $clientId = 'omniauth-test-client', readonly array $scopes = ['openid', 'email', 'profile'])

FakeProvider::name()

public function name(): string

FakeProvider::authorizationUrl()

public function authorizationUrl(Authorization $authorization): string
public function consent(string $authorizationUrl, Identity|array $person): array

The person consents at the authorization address: what the browser brings back to the return address (['code' => …, 'state' => …]).

FakeProvider::deny()

public function deny(string $authorizationUrl): array
Returns

the person said no

FakeProvider::returnUrl()

public function returnUrl(array $return): string

The return address the browser lands on with $return's parameters.

FakeProvider::identify()

public function identify(string $code, Authorization $authorization): Identity

Omniauth\Password

PasswordMethod class

final class PasswordMethod implements Enrolling PasswordMethod.php:31

The password, checked by symfony/password-hasher under every host - never by password_verify() alone: Symfony prehashes a password longer than 72 bytes (or holding a NUL byte) before bcrypt, so password_verify(), and Laravel's Hash with it, would refuse those accounts. The same hasher says when to rehash (cost 13 here, 12 by Laravel's default: without it, each sign-in on the other host would rehash).

Proof: ['identifier' => ..., 'password' => ...]; an enrolment (a new password): ['password' => ..., 'current' => ...], 'current' checked when given.

PasswordMethod::__construct()

public function __construct(readonly Accounts $accounts, readonly Passwords $passwords, ?PasswordHasherInterface $hasher = null)
$hasher

the one security.yaml sets; by default bcrypt, cost 13, Symfony's "auto"

PasswordMethod::factor()

public function factor(): Factor

PasswordMethod::verify()

public function verify(Attempt $attempt, Proof $proof, Context $context): Verdict

PasswordMethod::enrolBegin()

public function enrolBegin(Attempt $enrolment, Proof $proof, Context $context): Challenge

PasswordMethod::enrolConfirm()

public function enrolConfirm(Attempt $enrolment, Proof $proof, Context $context): Enrolled

Omniauth\Testing

ArrayCache class

final class ArrayCache implements CacheInterface ArrayCache.php:12

A PSR-16 cache in a PHP array, its lifetimes read on the clock given: what CacheThrottle counts in for tests and a first try, with no cache library.

ArrayCache::__construct()

public function __construct(readonly ?ClockInterface $clock = null)

ArrayCache::get()

public function get(string $key, mixed $default = null): mixed

ArrayCache::set()

public function set(string $key, mixed $value, \DateInterval|int|null $ttl = null): bool

ArrayCache::delete()

public function delete(string $key): bool

ArrayCache::clear()

public function clear(): bool

ArrayCache::getMultiple()

public function getMultiple(iterable $keys, mixed $default = null): iterable

ArrayCache::setMultiple()

public function setMultiple(iterable $values, \DateInterval|int|null $ttl = null): bool

ArrayCache::deleteMultiple()

public function deleteMultiple(iterable $keys): bool

ArrayCache::has()

public function has(string $key): bool

FixedClock class

final class FixedClock implements ClockInterface FixedClock.php:8

A clock that stands still until told: deadlines and windows tested to the second.

FixedClock::__construct()

public function __construct(\DateTimeImmutable|string $now = '2026-10-20 12:00:00 UTC')

FixedClock::now()

public function now(): \DateTimeImmutable

FixedClock::set()

public function set(\DateTimeImmutable|string $now): void

FixedClock::advance()

public function advance(int $seconds): void

InMemoryEmailCodes class

final class InMemoryEmailCodes implements EmailCodes InMemoryEmailCodes.php:8

The e-mailed codes on TestAccount objects (InMemoryStore::emailCodes()).

InMemoryEmailCodes::enabled()

public function enabled(object $account): bool

InMemoryEmailCodes::enable()

public function enable(object $account): void

InMemoryEmailCodes::disable()

public function disable(object $account): void

InMemoryEmailCodes::remember()

public function remember(object $account, string $hash): void

InMemoryEmailCodes::remembered()

public function remembered(object $account): ?string

InMemoryEmailCodes::forget()

public function forget(object $account): void

InMemoryStore class

final class InMemoryStore implements Accounts, Passwords, LinkTokens, PlainLinkTokens, BackupCodes, TrustedVersion, Identities InMemoryStore.php:21

Every store contract in memory, over TestAccount objects: tests, the harness's bare script, a first try in plain PHP. An application fills the same contracts on its tables (omnibase: Base\Auth\). The e-mailed codes and the TOTP secrets, whose contracts share method names, are emailCodes() and totpSecrets(), over the same accounts.

InMemoryStore::__construct()

public function __construct(TestAccount ...$accounts)

InMemoryStore::add()

public function add(TestAccount $account): TestAccount

InMemoryStore::emailCodes()

public function emailCodes(): InMemoryEmailCodes

InMemoryStore::totpSecrets()

public function totpSecrets(): InMemoryTotpSecrets

InMemoryStore::byIdentifier()

public function byIdentifier(string $identifier): ?object

InMemoryStore::byHandle()

public function byHandle(string $handle): ?object

InMemoryStore::handle()

public function handle(object $account): string

InMemoryStore::identifier()

public function identifier(object $account): string

InMemoryStore::displayName()

public function displayName(object $account): string

InMemoryStore::email()

public function email(object $account): ?string

InMemoryStore::check()

public function check(object $account): void

InMemoryStore::hash()

public function hash(object $account): ?string

InMemoryStore::update()

public function update(object $account, string $hash): void

InMemoryStore::issue()

public function issue(object $account, string $hash, \DateTimeImmutable $until): void

InMemoryStore::consume()

public function consume(string $hash, \DateTimeImmutable $now): ?object

InMemoryStore::consumePlain()

public function consumePlain(string $value, \DateTimeImmutable $now): ?object

InMemoryStore::hashes()

public function hashes(object $account): array

InMemoryStore::replace()

public function replace(object $account, array $hashes): void

InMemoryStore::version()

public function version(object $account): int

InMemoryStore::revokeAll()

public function revokeAll(object $account): void

InMemoryStore::owner()

public function owner(string $provider, string $subject): ?object
public function link(object $account, string $provider, string $subject, ?string $email = null, bool $emailVerified = false): void
public function unlink(object $account, string $provider): void

InMemoryStore::used()

public function used(string $provider, string $subject, \DateTimeImmutable $at): void

InMemoryStore::providers()

public function providers(object $account): array

InMemoryTotpSecrets class

final class InMemoryTotpSecrets implements TotpSecrets InMemoryTotpSecrets.php:8

The TOTP secrets on TestAccount objects (InMemoryStore::totpSecrets()), for omniauth/otp's tests.

InMemoryTotpSecrets::secret()

public function secret(object $account): ?string

InMemoryTotpSecrets::enable()

public function enable(object $account, string $secret): void

InMemoryTotpSecrets::disable()

public function disable(object $account): void

ProviderContractTests trait

trait ProviderContractTests ProviderContractTests.php:44

What every identity provider does, as PHPUnit tests: a provider package's test case uses this trait, gives the provider and a way to make its server answer (recorded answers: no test reaches the network), and the suite checks the Provider contract:

  • the authorization address carries the trip's state, its S256 challenge (never the verifier), the nonce, response_type=code, the client and the return address - the configured one, or its path on the trip's origin (none known: a LogicException) - and asks the server nothing;
  • a code the server vouches for is exchanged for an Identity of this provider: its subject, an address said verified only when there is one;
  • a code the server refuses, and a server that does not answer, are a ProviderError (reason "exchange-failed") - never another exception, and never with the client's secret in its message;
  • made by its factory (when it has one) from a configuration entry: named after the entry's key, null for an entry of another kind; through Providers::configured(), an entry without a client_id is left out and one without a redirect_uri is a mistake.

final class GoogleContractTest extends TestCase { use ProviderContractTests;

    protected function provider(string $redirectUri): Provider { ... a GoogleProvider on a Guzzle MockHandler ... }
    protected function consented(Provider $provider, Authorization $authorization): string { ... the token answer queued, with the nonce ...; return 'a-code'; }
    protected function refused(Provider $provider): string { ... an invalid_grant queued ...; return 'a-code'; }
    protected function unanswered(Provider $provider): string { ... a 503 queued ...; return 'a-code'; }
}

Requires phpunit/phpunit (the package's require-dev).

ProviderContractTests::testTheAuthorizationAddressCarriesTheTrip()

public function testTheAuthorizationAddressCarriesTheTrip(): void

ProviderContractTests::testAReturnPathIsCompletedByTheTripsOrigin()

public function testAReturnPathIsCompletedByTheTripsOrigin(): void

ProviderContractTests::testACodeTheServerVouchesForIsAnIdentityOfThisProvider()

public function testACodeTheServerVouchesForIsAnIdentityOfThisProvider(): void

ProviderContractTests::testARefusedOrUnansweredExchangeIsAProviderError()

public function testARefusedOrUnansweredExchangeIsAProviderError(): void

ProviderContractTests::testItsFactoryMakesItFromAConfigurationEntry()

public function testItsFactoryMakesItFromAConfigurationEntry(): void

RecordingSender class

final class RecordingSender implements Sender RecordingSender.php:9

Sends nothing, keeps every message: the link or the code a test reads back.

public array $sent = []

RecordingSender::send()

public function send(object $account, Message $message): void

RecordingSender::last()

public function last(): ?Message

RecordingSender::count()

public function count(): int

StubMethod class

final class StubMethod implements Method StubMethod.php:20

A first factor that passes for the account of a handle: a passkey or an identity provider without their library, so a host's tests (and the core's) drive the conductor and the policy. Proof: ['handle' => ...].

StubMethod::__construct()

public function __construct(readonly Factor $factor, readonly Accounts $accounts)

StubMethod::factor()

public function factor(): Factor

StubMethod::verify()

public function verify(Attempt $attempt, Proof $proof, Context $context): Verdict

StubSecondFactor class

final class StubSecondFactor implements SecondFactor StubSecondFactor.php:19

A second factor (an authenticator application without otphp) enrolled for the handles given, passing on the code given. Proof: ['code' => ...].

StubSecondFactor::__construct()

public function __construct(readonly Factor $factor, readonly Accounts $accounts, readonly array $enrolled = [], readonly string $code = '123456')
$enrolled

the handles that set it up

StubSecondFactor::factor()

public function factor(): Factor

StubSecondFactor::enrolled()

public function enrolled(object $account): bool

StubSecondFactor::verify()

public function verify(Attempt $attempt, Proof $proof, Context $context): Verdict

TestAccount class

final class TestAccount TestAccount.php:9

An account for tests and first steps, every proof on it: what InMemoryStore reads and writes.

public array $backupCodes = []
public array $identities = []
public array $identityDetails = []
public string $handle
public string $identifier
public ?string $email
public ?string $password

A password_hash() or Symfony hash.

public ?string $name
public ?string $refused

Why it may not sign in (banned, locked...), or null.

public ?string $totpSecret
public bool $emailCodeEnabled
public ?string $emailCode
public int $trustedVersion

TestAccount::__construct()

public function __construct(public string $handle, public string $identifier, public ?string $email = null, public ?string $password = null, public ?string $name = null, public ?string $refused = null, public ?string $totpSecret = null, public bool $emailCodeEnabled = false, public ?string $emailCode = null, public int $trustedVersion = 0)

Omniauth\TrustedDevice

TrustedDevices class

final class TrustedDevices implements Ambient, Revocable TrustedDevices.php:31

A second factor already passed on this browser: the trusted_device cookie, written in scheb/2fa's very format so the devices trusted before the switch stay trusted - JWTs signed HS256 by the host's secret (Symfony's kernel.secret), claims iat, exp, "usr" (the account's identifier), "fwl" (the firewall), "vsn" (the account's trusted version), several tokens joined by ";". Encoded with PHP's own HMAC, byte for byte what lcobucci/jwt writes for scheb's JwtTokenEncoder (dates with their microseconds), and read as it reads them (signature, iat <= now < exp).

A token counts while its version is the account's: TrustedVersion::revokeAll() (a second factor turned off, a password changed) makes every device fall back.

const LIFETIME = 2592000

TrustedDevices::__construct()

public function __construct(readonly Accounts $accounts, readonly TrustedVersion $versions, readonly string $secret, readonly int $lifetime = self::LIFETIME, ?ClockInterface $clock = null)

TrustedDevices::factor()

public function factor(): Factor

TrustedDevices::verify()

public function verify(Attempt $attempt, Proof $proof, Context $context): Verdict

TrustedDevices::trusts()

public function trusts(object $account, string $cookie, string $firewall = 'main'): bool

Whether the cookie holds a valid token of this account, this firewall and the account's version.

TrustedDevices::remember()

public function remember(object $account, Context $context): string

The cookie's new value with this browser trusted for the account: as scheb's TrustedDeviceTokenStorage writes it - the invalid and expired tokens dropped, those of the account and firewall replaced by a new one, the others kept.

TrustedDevices::revoke()

public function revoke(object $account): void

TrustedDevices::encode()

public function encode(string $username, string $firewall, int $version, \DateTimeImmutable $issuedAt, \DateTimeImmutable $until): string

One token, as scheb's JwtTokenEncoder::generateToken() makes it with lcobucci/jwt.

TrustedDevices::decode()

public function decode(string $token): ?array

One token's claims when it is signed with the key and valid now, as scheb's JwtTokenEncoder::decodeToken() accepts it; null otherwise.