### Install OTPHP via Composer Source: https://github.com/spomky-labs/otphp/blob/11.6.x/doc/index.md Use this command to add the library to your PHP project. ```sh composer require spomky-labs/otphp ``` -------------------------------- ### Migration Path Examples Source: https://github.com/spomky-labs/otphp/blob/11.6.x/doc/UPGRADE_v11-v12.md Comparison of mutable v11.3 code, transitional v11.4 code, and required v12.0 immutable code. ```php use OTPHP\TOTP; use OTPHP\InternalClock; $totp = TOTP::generate(new InternalClock()); $totp->setLabel('alice@example.com'); $totp->setIssuer('My Service'); $totp->setDigits(8); // $totp is modified in place ``` ```php use OTPHP\TOTP; use OTPHP\InternalClock; // Old way still works but triggers deprecation warnings $totp = TOTP::generate(new InternalClock()); $totp->setLabel('alice@example.com'); // Deprecated warning // New immutable way $totp = TOTP::generate(new InternalClock()) ->withLabel('alice@example.com') ->withIssuer('My Service') ->withDigits(8); // Each with*() method returns a new instance ``` ```php use OTPHP\TOTP; use OTPHP\InternalClock; // Only immutable methods available $totp = TOTP::generate(new InternalClock()) ->withLabel('alice@example.com') ->withIssuer('My Service') ->withDigits(8); ``` -------------------------------- ### Production Clock Implementation Source: https://github.com/spomky-labs/otphp/blob/11.6.x/doc/UPGRADE_v11-v12.md Installing and using a PSR-20 compliant clock for production environments. ```php composer require symfony/clock ``` ```php use Symfony\Component\Clock\NativeClock; use OTPHP\TOTP; $clock = new NativeClock(); $totp = TOTP::generate($clock); ``` -------------------------------- ### Generate QR Code with BaconQrCode Source: https://github.com/spomky-labs/otphp/blob/11.6.x/doc/AppConfig.md Install the BaconQrCode library and render the provisioning URI as a base64-encoded PNG image. ```shell composer require bacon/bacon-qr-code:^3.0 ``` ```php withLabel('alice@google.com'); // The label (string) $renderer = new GDLibRenderer(250); $writer = new Writer($renderer); $qr = $writer->writeString($totp->getProvisioningUri()); echo ''; ``` -------------------------------- ### Generate QR Code with endroid/qr-code Source: https://github.com/spomky-labs/otphp/blob/11.6.x/doc/AppConfig.md Install the endroid/qr-code library and use the builder API to generate a data URI for the QR code. ```shell composer require endroid/qr-code ``` ```php withLabel('alice@google.com'); // The label (string) $builder = new Builder( writer: new PngWriter(), data: $totp->getProvisioningUri(), size: 250, ); $result = $builder->build(); echo ''; ``` -------------------------------- ### Configure TOTP Epoch Source: https://github.com/spomky-labs/otphp/blob/11.6.x/doc/Customize.md Set a custom epoch to define the starting timestamp for time-based calculations. Use dynamic secrets when sharing the epoch to avoid generating identical passwords. ```php withPeriod(5) // The period (5 seconds) ->withDigest('sha1') // The digest algorithm ->withDigits(6); // The output will generate 6 digits $password = $otp->at(1519401289); // Current period is: 1519401285 - 1519401289 $otp->verify($password, 1519401289); // Second 1: true $otp->verify($password, 1519401290); // Second 2: false // With epoch $otp = TOTP::generate(); $otp = $otp->withPeriod(5) // The period (30 seconds) ->withDigest('sha1') // The digest algorithm ->withDigits(6) // The output will generate 8 digits ->withEpoch(1519401289); // The epoch is now 02/23/2018 @ 3:54:49pm (UTC) $password = $otp->at(1519401289); // Current period is: 1519401289 - 1519401293 $otp->verify($password, 1519401289); // Second 1: true $otp->verify($password, 1519401290); // Second 2: true $otp->verify($password, 1519401291); // Second 3: true $otp->verify($password, 1519401292); // Second 4: true $otp->verify($password, 1519401293); // Second 5: true $otp->verify($password, 1519401294); // Second 6: false ``` -------------------------------- ### Digest Algorithm Enforcement Exceptions Source: https://github.com/spomky-labs/otphp/blob/11.6.x/doc/UPGRADE_v11-v12.md Examples of operations that throw exceptions when using non-compliant digest algorithms like md5. ```php // Direct configuration TOTP::generate(new InternalClock())->withDigest('md5'); // InvalidParameterException // Provisioning URI Factory::loadFromProvisioningUri( 'otpauth://totp/Foo:alice@foo.bar?algorithm=md5&secret=JDDK4U6G3BJLEZ7Y', new InternalClock() ); // InvalidProvisioningUriException ``` -------------------------------- ### Deprecation warning message Source: https://github.com/spomky-labs/otphp/blob/11.6.x/doc/UPGRADE_v11-v12.md Example of the deprecation warning message that indicates a missing PSR Clock implementation. ```text The parameter "$clock" will become mandatory in 12.0.0. Please set a valid PSR Clock implementation instead of "null". ``` -------------------------------- ### Verify HOTP with window Source: https://github.com/spomky-labs/otphp/blob/11.6.x/doc/Window.md Demonstrates how the window parameter extends the range of counters tested during HOTP verification. ```php verify('123456', 999); // Will return false $hotp->verify('123456', 999, 10); // Will return true (1000 is tested) ``` -------------------------------- ### Create and Manage OTP Objects Source: https://github.com/spomky-labs/otphp/blob/11.6.x/doc/index.md Demonstrates generating secrets, creating OTP instances with a PSR-20 clock, and retrieving current passwords. ```php getSecret()}\n"; // You can also specify a custom secret size (in bytes) // The default is 64 bytes (which encodes to 103 base32 characters) $otp = TOTP::generate($clock, 16); // 16 bytes = 26 base32 characters echo "The OTP secret is: {$otp->getSecret()}\n"; // Note: use your own way to load the user secret. // The function "load_user_secret" is simply a placeholder. $secret = load_user_secret(); $otp = TOTP::createFromSecret($secret, $clock); echo "The current OTP is: {$otp->now()}\n"; ``` -------------------------------- ### Generate Provisioning URI in PHP Source: https://github.com/spomky-labs/otphp/blob/11.6.x/doc/AppConfig.md Create a TOTP instance and generate a provisioning URI string. ```php withLabel('alice@google.com'); // The label (string) $totp->getProvisioningUri(); // Will return otpauth://totp/alice%40google.com?secret=JBSWY3DPEHPK3PXP ``` -------------------------------- ### Object Instantiation Migration Source: https://github.com/spomky-labs/otphp/blob/11.6.x/doc/UPGRADE_v8-v9.md Replaces direct constructor calls with the static create() method. ```php getProvisioningUri(), ); $otp->verify($_POST['otp']); ``` -------------------------------- ### Preparing for v12.0 Source: https://github.com/spomky-labs/otphp/blob/11.6.x/doc/UPGRADE_v11-v12.md Tools for identifying deprecated method usage and verifying immutability in tests. ```bash grep -r "->set[A-Z]" --include="*.php" | grep -v vendor ``` ```php $original = TOTP::generate(new InternalClock()); $modified = $original->withLabel('test'); // These should be different instances assert($original !== $modified); assert($original->getLabel() === null); assert($modified->getLabel() === 'test'); ``` -------------------------------- ### Verify OTP using a provisioning URI Source: https://github.com/spomky-labs/otphp/blob/11.6.x/doc/QA.md Load an OTP object from a stored provisioning URI to ensure all configuration parameters are preserved. ```php getProvisioningUri() ); $otp->verify($_POST['otp']); ``` -------------------------------- ### Factory Provisioning URI Migration Source: https://github.com/spomky-labs/otphp/blob/11.6.x/doc/UPGRADE_v11-v12.md Comparison of Factory provisioning URI loading between v11.4 and v12.0. ```php use OTPHP\Factory; $otp = Factory::loadFromProvisioningUri($uri); ``` ```php use OTPHP\Factory; use OTPHP\InternalClock; $otp = Factory::loadFromProvisioningUri($uri, new InternalClock()); ``` -------------------------------- ### Configure OTP with Immutable Methods Source: https://github.com/spomky-labs/otphp/blob/11.6.x/doc/Customize.md Use with* methods to return a new OTP object with updated parameters, leaving the original instance unchanged. ```php withPeriod(60) ->withDigest('sha256') ->withDigits(8) ->withLabel('alice@foo.bar') ->withIssuer('My Service'); // $baseOtp remains unchanged with default settings // $customOtp is a new object with the custom settings ``` -------------------------------- ### Load OTP object from provisioning URI Source: https://github.com/spomky-labs/otphp/blob/11.6.x/doc/Factory.md Instantiate a TOTP or HOTP object using the Factory class. A PSR-20 clock implementation is recommended for TOTP objects. ```php verify('654321'); // returns false // With a window 5 seconds, this will fail // because the input is tested with 147682209-5 and 147682209+5 seconds. // and this does not allow the previous OTP to be used $totp->verify('654321', null, 5); // returns false // With a window 10 seconds, this will succeed // because the input is tested with 147682209-10 and 147682209+10 seconds. // and the previous OTP is tested $totp->verify('654321', null, 10); // returns true ``` -------------------------------- ### Migrate to specific exception handling in PHP Source: https://github.com/spomky-labs/otphp/blob/11.6.x/doc/Exceptions.md Demonstrates the transition from catching generic InvalidArgumentException to specific OTPHP exception classes. ```php // Before (still works) try { $totp = TOTP::createFromSecret($secret); } catch (\InvalidArgumentException $e) { // ... } // After (more specific) try { $totp = TOTP::createFromSecret($secret); } catch (\OTPHP\Exception\InvalidParameterException $e) { // Handle parameter errors with access to parameter name/value } catch (\OTPHP\Exception\SecretDecodingException $e) { // Handle Base32 decoding errors } ``` -------------------------------- ### Verify TOTP using individual parameters Source: https://github.com/spomky-labs/otphp/wiki/What-should-I-store-in-my-server-database Initializes a TOTP object using specific configuration parameters retrieved from the user model. ```php getEmail(), $user->getOtpSecret(), $period, $digest, $digits ); $totp->verify($_POST['otp']); ``` -------------------------------- ### Testing with Mock Clock Source: https://github.com/spomky-labs/otphp/blob/11.6.x/doc/UPGRADE_v11-v12.md Using a fixed or mock clock for testing purposes. ```php use Symfony\Component\Clock\MockClock; use OTPHP\TOTP; $clock = new MockClock('2024-01-01 12:00:00'); $totp = TOTP::generate($clock); ``` -------------------------------- ### Run PHPUnit tests Source: https://github.com/spomky-labs/otphp/blob/11.6.x/doc/UPGRADE_v11-v12.md Execute the test suite to verify that no deprecation warnings are triggered. ```bash vendor/bin/phpunit ``` -------------------------------- ### Configure OTP with Mutable Methods Source: https://github.com/spomky-labs/otphp/blob/11.6.x/doc/Customize.md Use set* methods to modify an OTP object in place, which is suitable for step-by-step configuration. ```php setPeriod(60); // Modifies the object $otp->setDigest('sha256'); // Modifies the object $otp->setDigits(8); // Modifies the object $otp->setLabel('alice@foo.bar'); // Modifies the object $otp->setIssuer('My Service'); // Modifies the object // $otp is now configured with all the above settings ``` -------------------------------- ### Handling InvalidProvisioningUriException Source: https://github.com/spomky-labs/otphp/blob/11.6.x/doc/Exceptions.md Caught when a provided provisioning URI string is malformed or missing required components. ```php try { $otp = Factory::loadFromProvisioningUri('invalid-uri'); } catch (\OTPHP\Exception\InvalidProvisioningUriException $e) { echo $e->getMessage(); // "Not a valid OTP provisioning URI" } ``` -------------------------------- ### Generate QR Code URI with Online Service Source: https://github.com/spomky-labs/otphp/blob/11.6.x/doc/AppConfig.md Use an external QR code service by passing the service URI and a custom placeholder to the getQrCodeUri method. ```php withLabel('alice@google.com'); // The label (string) $qr_code_url = $totp->getQrCodeUri( 'https://api.qrserver.com/v1/create-qr-code/?color=5330FF&bgcolor=70FF7E&data=[DATA]&qzone=2&margin=0&size=300x300&ecc=M', '[DATA]' ); echo ""; ``` -------------------------------- ### Generate QR Code URI Source: https://github.com/spomky-labs/otphp/blob/11.6.x/doc/index.md Configures the OTP label and generates a URI for QR code rendering. ```php withLabel('Label of your web'); $grCodeUri = $otp->getQrCodeUri( 'https://api.qrserver.com/v1/create-qr-code/?data=[DATA]&size=300x300&ecc=M', '[DATA]' ); echo ""; ``` -------------------------------- ### Demonstrate TOTP lifetime behavior Source: https://github.com/spomky-labs/otphp/blob/11.6.x/doc/QA.md Observe how TOTP values change over time based on the defined period. ```php now(); sleep(1); } ``` -------------------------------- ### Add custom parameters to TOTP Source: https://github.com/spomky-labs/otphp/blob/11.6.x/doc/Customize.md Use the withParameter method to include arbitrary key-value pairs in the provisioning URI. ```php withLabel('alice@google.com') // The label ->withParameter('foo', 'bar'); $totp->getProvisioningUri(); // Will return otpauth://totp/alice%40google.com?secret=JBSWY3DPEHPK3PXP&foo=bar ``` -------------------------------- ### Common Migration Patterns Source: https://github.com/spomky-labs/otphp/blob/11.6.x/doc/UPGRADE_v11-v12.md Standard refactoring patterns for updating property values, conditional logic, and object initialization. ```php // Before (v11.3) $otp->setSecret('NEWSECRET'); $otp->setDigits(8); // After (v11.4+) $otp = $otp->withSecret('NEWSECRET') ->withDigits(8); ``` ```php // Before (v11.3) if ($useCustomLabel) { $otp->setLabel($customLabel); } // After (v11.4+) if ($useCustomLabel) { $otp = $otp->withLabel($customLabel); } ``` ```php // Before (v11.3) $hotp->setCounter($hotp->getCounter() + 1); // After (v11.4+) $hotp = $hotp->withCounter($hotp->getCounter() + 1); ``` ```php // Before (v11.3) - Required multiple statements $totp = TOTP::createFromSecret('secret', new InternalClock()); $totp->setLabel('user@example.com'); $totp->setIssuer('MyApp'); $totp->setParameter('image', 'https://example.com/logo.png'); // After (v11.4+) - Clean method chaining $totp = TOTP::createFromSecret('secret', new InternalClock()) ->withLabel('user@example.com') ->withIssuer('MyApp') ->withParameter('image', 'https://example.com/logo.png'); ``` -------------------------------- ### Verify TOTP with Google Authenticator Source: https://github.com/spomky-labs/otphp/blob/11.6.x/doc/AppConfig.md Generate a TOTP instance and display the current token for verification against the Google Authenticator app. ```php withLabel('alice@google.com'); // The label (string) echo 'Current OTP: ' . $totp->now(); ``` -------------------------------- ### Configure a Custom Secret Source: https://github.com/spomky-labs/otphp/blob/11.6.x/doc/Customize.md Use a manually generated secret string instead of the default 512-bit secret. ```php getOtpSecret()); $totp = $totp->withPeriod($period) ->withDigest($digest) ->withDigits($digits) ->withLabel($user->getEmail()); $totp->verify($_POST['otp']); ``` -------------------------------- ### OTP Label Assignment Migration Source: https://github.com/spomky-labs/otphp/blob/11.6.x/doc/UPGRADE_v8-v9.md Updates label handling by removing it from the constructor and using the setLabel() method instead. ```php setLabel($label); ``` -------------------------------- ### Catching library-specific exceptions Source: https://github.com/spomky-labs/otphp/blob/11.6.x/doc/Exceptions.md Use the OTPExceptionInterface to catch any exception thrown by the OTPHP library. ```php try { $totp = TOTP::createFromSecret('invalid secret!@#'); } catch (\OTPHP\Exception\OTPExceptionInterface $e) { // Catches any OTPHP exception } ``` -------------------------------- ### Configure TOTP with custom parameters in PHP Source: https://github.com/spomky-labs/otphp/blob/11.6.x/doc/AppConfig.md Creates a TOTP instance with a custom secret, period, digest algorithm, and digit count. Note that these specific settings may not be compatible with all authenticator apps. ```php withPeriod(10) // The period (int) ->withDigest('sha512') // The digest algorithm (string) ->withDigits(8) // The number of digits (int) ->withLabel('alice@google.com'); // The label (string) echo 'Current OTP: ' . $totp->now(); ``` -------------------------------- ### Configure issuer for TOTP Source: https://github.com/spomky-labs/otphp/blob/11.6.x/doc/Customize.md Set an issuer to distinguish between multiple OTPs with the same label. The issuer is automatically added as a label prefix and a query parameter. ```php withLabel('alice@google.com') // The label (string) ->withIssuer('My Service'); ``` ```php getProvisioningUri(); // Will return otpauth://totp/My%20Service%3Aalice%40google.com?issuer=My%20Service&secret=JBSWY3DPEHPK3PXP ``` -------------------------------- ### Search for TOTP usage Source: https://github.com/spomky-labs/otphp/blob/11.6.x/doc/UPGRADE_v11-v12.md Use grep to identify instances of TOTP usage that require a clock parameter update. ```bash # Search for TOTP usage without clock parameter grep -r "TOTP::generate()" --include="*.php" grep -r "TOTP::create(" --include="*.php" grep -r "new TOTP(" --include="*.php" grep -r "Factory::loadFromProvisioningUri(" --include="*.php" ``` -------------------------------- ### Handling ParameterNotFoundException Source: https://github.com/spomky-labs/otphp/blob/11.6.x/doc/Exceptions.md Caught when attempting to retrieve a parameter that does not exist on the OTP object. ```php try { $totp = TOTP::createFromSecret('SECRET'); $value = $totp->getParameter('nonexistent'); } catch (\OTPHP\Exception\ParameterNotFoundException $e) { echo $e->getMessage(); // "Parameter \"nonexistent\" does not exist" echo $e->parameterName; // "nonexistent" } ``` -------------------------------- ### Internal Clock Usage Source: https://github.com/spomky-labs/otphp/blob/11.6.x/doc/UPGRADE_v11-v12.md Using the built-in InternalClock for simple use cases. ```php use OTPHP\InternalClock; use OTPHP\TOTP; $totp = TOTP::generate(new InternalClock()); ``` -------------------------------- ### Access Exception Properties Source: https://github.com/spomky-labs/otphp/blob/11.6.x/doc/Exceptions.md Retrieve detailed error information using public readonly properties provided by specific OTPHP exceptions. ```php try { $totp = TOTP::create('SECRET'); $totp = $totp->withDigits($_POST['digits']); } catch (\OTPHP\Exception\InvalidParameterException $e) { echo "Error: {$e->getMessage()}\n"; echo "Parameter: {$e->parameterName}\n"; echo "Invalid value: " . json_encode($e->parameterValue) . "\n"; // Output: // Error: Digits must be at least 1. // Parameter: digits // Invalid value: 0 } ``` -------------------------------- ### Catch All OTPHP Exceptions via Marker Interface Source: https://github.com/spomky-labs/otphp/blob/11.6.x/doc/Exceptions.md Use the OTPExceptionInterface to catch any library-specific error, separating them from general application exceptions. ```php try { $totp = TOTP::createFromSecret($_POST['secret']); $totp = $totp->withLabel($_POST['label']) ->withIssuer($_POST['issuer']); } catch (\OTPHP\Exception\OTPExceptionInterface $e) { // Handle any OTPHP-specific error return response()->json(['error' => $e->getMessage()], 400); } catch (\Exception $e) { // Handle unexpected errors log()->error('Unexpected error: ' . $e->getMessage()); return response()->json(['error' => 'Internal server error'], 500); } ``` -------------------------------- ### Inject Custom Secret Source: https://github.com/spomky-labs/otphp/wiki/OTP-Secret Manually generate a secret, encode it using Base32, and provide it during TOTP instantiation. ```php getSecret(); // Return the value of $encoded_secret ``` -------------------------------- ### TOTP Generation Migration Source: https://github.com/spomky-labs/otphp/blob/11.6.x/doc/UPGRADE_v11-v12.md Comparison of TOTP generation between v11.4 and v12.0. ```php use OTPHP\TOTP; // This triggers a deprecation warning in v11.4 $totp = TOTP::generate(); ``` ```php use OTPHP\TOTP; use OTPHP\InternalClock; // You must provide a Clock implementation $totp = TOTP::generate(new InternalClock()); ``` -------------------------------- ### Generate Secret with Built-in Method Source: https://github.com/spomky-labs/otphp/wiki/OTP-Secret Uses the library's internal generator to create a 256-bit Base32 encoded secret. ```php getSecret(); // Return the Base32 encoded secret generated by this library ``` -------------------------------- ### Handle Specific OTPHP Exceptions Source: https://github.com/spomky-labs/otphp/blob/11.6.x/doc/Exceptions.md Catch targeted exceptions to handle specific error scenarios like URI parsing or parameter validation. ```php try { $otp = Factory::loadFromProvisioningUri($uri); $isValid = $otp->verify($code); } catch (\OTPHP\Exception\InvalidProvisioningUriException $e) { // Handle URI parsing errors log('Invalid provisioning URI: ' . $e->getMessage()); } catch (\OTPHP\Exception\InvalidParameterException $e) { // Handle parameter validation errors log("Invalid {$e->parameterName}: {$e->getMessage()}"); } ``` -------------------------------- ### Handling SecretDecodingException Source: https://github.com/spomky-labs/otphp/blob/11.6.x/doc/Exceptions.md Caught when the provided secret string fails Base32 decoding. ```php try { $totp = TOTP::createFromSecret('INVALID!@#$%'); $totp->now(); // Triggers secret decoding } catch (\OTPHP\Exception\SecretDecodingException $e) { echo $e->getMessage(); // "Unable to decode the secret. Is it correctly base32 encoded?" } ``` -------------------------------- ### Migrate Digest Algorithm Source: https://github.com/spomky-labs/otphp/blob/11.6.x/doc/UPGRADE_v11-v12.md Update code to use a spec-compliant digest algorithm like sha256 instead of insecure alternatives. ```php // Before $totp = $totp->withDigest('md5'); // After $totp = $totp->withDigest('sha256'); ``` -------------------------------- ### Add image parameter to TOTP Source: https://github.com/spomky-labs/otphp/blob/11.6.x/doc/Customize.md Include an image URL via the 'image' parameter for applications that support custom branding. ```php withLabel('alice@google.com') // The label (string) ->withParameter('image', 'https://foo.bar/otp.png'); $totp->getProvisioningUri(); // Will return otpauth://totp/alice%40google.com?secret=JBSWY3DPEHPK3PXP&image=https%3A%2F%2Ffoo.bar%2Fotp.png ``` -------------------------------- ### Configure Digest Algorithm Source: https://github.com/spomky-labs/otphp/blob/11.6.x/doc/Customize.md Set the HMAC digest algorithm. Only algorithms producing at least 19 bytes are supported; sha256 and sha512 are recommended. ```php withPeriod(30) // The period (30 seconds) ->withDigest('sha256'); // The digest algorithm ``` -------------------------------- ### Adjust Period and Counter Source: https://github.com/spomky-labs/otphp/blob/11.6.x/doc/Customize.md Modify the default TOTP period or HOTP counter values. ```php withPeriod(10); // The period is now 10 seconds $otp = HOTP::generate(); $otp = $otp->withCounter(1000); // The counter is now 1000. We recommend you start at `0`, but you can set any value (at least 0) ``` -------------------------------- ### Generate OTP with Custom Secret Size Source: https://github.com/spomky-labs/otphp/blob/11.6.x/doc/Customize.md Specify the secret size in bytes during OTP generation. The library automatically removes trailing '=' characters. ```php getSecret(); // Will be 26 characters long ``` -------------------------------- ### Verify OTP Input Source: https://github.com/spomky-labs/otphp/blob/11.6.x/doc/index.md Validates a user-provided OTP against the stored secret. ```php $otp = TOTP::createFromSecret($secret); // create TOTP object from the secret. $otp->verify($input); // Returns true if the input is verified, otherwise false. ``` -------------------------------- ### Configure TOTP Digits Source: https://github.com/spomky-labs/otphp/blob/11.6.x/doc/Customize.md Adjust the number of digits generated by the TOTP instance. Values greater than 10 are generally discouraged for usability. ```php withPeriod(30) // The period (30 seconds) ->withDigest('sha1') // The digest algorithm ->withDigits(8); // The output will generate 8 digits ``` -------------------------------- ### Disable issuer as query parameter Source: https://github.com/spomky-labs/otphp/blob/11.6.x/doc/Customize.md Use withIssuerIncludedAsParameter(false) to remove the issuer from the query string while keeping it in the label prefix. ```php withIssuerIncludedAsParameter(false); echo $totp->getProvisioningUri(); // Will return otpauth://totp/My%20Service%3Aalice%40google.com?secret=JBSWY3DPEHPK3PXP ``` -------------------------------- ### Handling InvalidLabelException Source: https://github.com/spomky-labs/otphp/blob/11.6.x/doc/Exceptions.md Caught when a label or issuer format violates the Google Authenticator specification. ```php try { $totp = TOTP::createFromSecret('SECRET'); $totp = $totp->withLabel('user:invalid:format'); // Invalid: multiple colons } catch (\OTPHP\Exception\InvalidLabelException $e) { echo $e->getMessage(); // "Neither issuer nor account name in label may contain a colon." echo $e->labelName; // "label" echo $e->labelValue; // "user:invalid:format" } ``` -------------------------------- ### Maintain Backward Compatibility with Standard Exceptions Source: https://github.com/spomky-labs/otphp/blob/11.6.x/doc/Exceptions.md Custom exceptions extend standard PHP exceptions, allowing existing try-catch blocks to function as expected. ```php // This still works - catches InvalidParameterException and InvalidLabelException try { $totp = TOTP::create('SECRET'); $totp = $totp->withDigits(-1); } catch (\InvalidArgumentException $e) { // Catches all exceptions extending InvalidArgumentException } // This also works - catches SecretDecodingException try { $totp = TOTP::createFromSecret('INVALID!@#'); $totp->now(); } catch (\RuntimeException $e) { // Catches all exceptions extending RuntimeException } ``` -------------------------------- ### Handling InvalidParameterException Source: https://github.com/spomky-labs/otphp/blob/11.6.x/doc/Exceptions.md Caught when an OTP parameter is provided with an invalid value, exposing the parameter name and value. ```php try { $totp = TOTP::create('SECRET'); $totp = $totp->withDigits(0); // Invalid: must be at least 1 } catch (\OTPHP\Exception\InvalidParameterException $e) { echo $e->getMessage(); // "Digits must be at least 1." echo $e->parameterName; // "digits" echo $e->parameterValue; // 0 } ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.