Skip to content

Comments

Sdk 2767 support central auth tokens#399

Merged
mehmet-yoti merged 13 commits intodevelopmentfrom
SDK-2767-support-central-auth-tokens
Feb 25, 2026
Merged

Sdk 2767 support central auth tokens#399
mehmet-yoti merged 13 commits intodevelopmentfrom
SDK-2767-support-central-auth-tokens

Conversation

@mehmet-yoti
Copy link
Contributor

Central Auth Token Support (SDK-2767)

Adds OAuth2-based central authentication tokens alongside the existing signed-request (PEM) authentication, mirroring the Java SDK's yoti-sdk-auth module.

What Changed

  • New AuthStrategy abstraction (BearerTokenStrategy, SignedRequestStrategy, NoAuthStrategy)
  • New Auth module for generating tokens via OAuth2 client_credentials grant with PS384-signed JWT
  • DocScanClient and DigitalIdentityClient now support a fluent builder() pattern with two auth modes
  • Full backward compatibility — existing PEM-based constructors continue to work unchanged
  • New dependency: firebase/php-jwt ^6.0

Auth Modes

The two modes are mutually exclusive:

Mode Method Description
Token Auth (new) withAuthenticationToken($token) Use a pre-obtained bearer token
Signed Request (legacy) withClientSdkId($id) + withPemFilePath($path) Use SDK ID and PEM key pair

Usage

Generating a Token

$generator = AuthenticationTokenGenerator::builder()
    ->withSdkId('your-sdk-id')
    ->withPemFilePath('/path/to/key.pem')
    ->build();

$response = $generator->generate(['scope1', 'scope2']);
$token = $response->getAccessToken();

DocScan Client — Token Auth (new)

$client = DocScanClient::builder()
    ->withAuthenticationToken($token)
    ->build();

DocScan Client — Signed Request (existing)

$client = DocScanClient::builder()
    ->withClientSdkId('your-sdk-id')
    ->withPemFilePath('/path/to/key.pem')
    ->build();

Digital Identity Client — Token Auth (new)

$client = DigitalIdentityClient::builder()
    ->withAuthenticationToken($token)
    ->build();

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds central OAuth2 authentication token support (alongside existing signed-request/PEM auth) by introducing an AuthStrategy abstraction, a token generator module, and fluent builder() APIs for DocScanClient and DigitalIdentityClient. It also extends Doc Scan SDK configuration support to include suppressed_screens in request/response models and documentation.

Changes:

  • Introduce HTTP auth strategies (BearerTokenStrategy, SignedRequestStrategy, NoAuthStrategy) and update RequestBuilder to use them.
  • Add an OAuth2 client_credentials auth token generator (PS384 JWT client assertion) and related response/exception/builders.
  • Add fluent builders for Doc Scan / Digital Identity clients and extend Doc Scan SDK config with suppressed_screens (plus tests/docs/examples).

Reviewed changes

Copilot reviewed 37 out of 46 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
tests/Http/RequestBuilderTest.php Updates expectation message for missing auth config and ensures a method is set before build.
tests/Http/AuthStrategy/SignedRequestStrategyTest.php Adds unit tests for signed-request auth header and query param generation.
tests/Http/AuthStrategy/NoAuthStrategyTest.php Adds unit tests for no-auth behavior (empty headers/params).
tests/Http/AuthStrategy/BearerTokenStrategyTest.php Adds unit tests for bearer auth header behavior and validation.
tests/DocScan/Session/Retrieve/Configuration/SessionConfigurationResponseTest.php Adds coverage for sdk_config and suppressed_screens retrieval behavior.
tests/DocScan/Session/Create/SdkConfigBuilderTest.php Adds coverage for suppressed screens builder methods and JSON serialization.
tests/DocScan/DocScanClientBuilderTest.php Adds coverage for Doc Scan client builder auth-mode validation and options.
tests/DigitalIdentityClientBuilderTest.php Adds coverage for Digital Identity client builder auth-mode validation.
tests/Auth/CreateAuthenticationTokenResponseTest.php Adds parsing/typing tests for auth token response fields.
tests/Auth/BuilderTest.php Adds coverage for token generator builder validation and options.
tests/Auth/AuthenticationTokenGeneratorTest.php Adds coverage for invalid scope input on token generation.
test_suppressed_screens.php Adds a standalone script to manually test suppressed screens behavior.
src/Identity/DigitalIdentityService.php Adds auth-strategy support and applies it to request construction.
src/Http/RequestBuilder.php Adds withAuthStrategy() and resolves auth strategy during build.
src/Http/AuthStrategy/SignedRequestStrategy.php Implements signed-request digest header + nonce/timestamp query params.
src/Http/AuthStrategy/NoAuthStrategy.php Implements a no-op auth strategy.
src/Http/AuthStrategy/BearerTokenStrategy.php Implements Authorization: Bearer header strategy.
src/Http/AuthStrategy/AuthStrategyInterface.php Introduces strategy interface for auth headers and query params.
src/DocScan/Session/Retrieve/Configuration/SessionConfigurationResponse.php Adds sdk_config storage and getSuppressedScreens() accessor.
src/DocScan/Session/Create/SdkConfigBuilder.php Adds suppressed screens builder APIs and passes into SdkConfig.
src/DocScan/Session/Create/SdkConfig.php Adds suppressed_screens field + accessor + JSON serialization.
src/DocScan/Service.php Adds auth-strategy mode and centralizes auth application to requests.
src/DocScan/DocScanClientBuilder.php Adds fluent builder supporting signed-request vs bearer token auth.
src/DocScan/DocScanClient.php Adds builder() and internal fromService() factory.
src/DigitalIdentityClientBuilder.php Adds fluent builder supporting signed-request vs bearer token auth.
src/DigitalIdentityClient.php Adds builder() and internal fromService() factory.
src/Constants.php Bumps SDK version and introduces auth URL/env constants.
src/Auth/Properties.php Adds auth-module constants for default auth URL and env var key.
src/Auth/Exception/AuthException.php Adds a dedicated exception type for auth token generation failures.
src/Auth/CreateAuthenticationTokenResponse.php Adds response wrapper for OAuth token endpoint data.
src/Auth/Builder.php Adds builder for AuthenticationTokenGenerator with auth URL resolution.
src/Auth/AuthenticationTokenGenerator.php Implements OAuth2 token generation via PS384 JWT + form POST.
examples/doc-scan/server.php Formatting adjustments in example app bootstrap.
examples/doc-scan/public/index.php Formatting adjustments in example app bootstrap.
examples/doc-scan/config/yoti.php Formatting adjustments in example config closure spacing.
examples/doc-scan/config/session.php Formatting adjustments in example config concatenation.
examples/doc-scan/config/filesystems.php Formatting adjustments in example config concatenation.
examples/doc-scan/config/database.php Formatting adjustments in example config concatenation.
examples/doc-scan/config/cache.php Formatting adjustments in example config concatenation.
examples/doc-scan/app/Http/Controllers/HomeController.php Demonstrates suppressed screens usage and fixes builder instantiation typo.
examples/doc-scan/app/Console/Kernel.php Formatting adjustment in example path concatenation.
docs/DOCSCAN.md Adds suppressed screens documentation section.
composer.json Bumps version and adds firebase/php-jwt dependency.
README.md Bumps referenced SDK version in install instructions.
Instructions.md Adds an implementation guide for suppressed screens (new doc).
AI_PLAN.md Included in PR payload (no diff content shown).
Comments suppressed due to low confidence (9)

src/Auth/AuthenticationTokenGenerator.php:175

  • The token request uses cURL without any connect/overall timeout options. A stalled network connection can hang the PHP process indefinitely. Set reasonable CURLOPT_CONNECTTIMEOUT/CURLOPT_TIMEOUT (and consider exposing them via the builder/options) so token generation fails fast and predictably.
        $ch = curl_init($this->authApiUrl);
        if ($ch === false) {
            throw new AuthException('Failed to initialize cURL session');
        }

        curl_setopt_array($ch, [
            CURLOPT_POST => true,
            CURLOPT_POSTFIELDS => $postData,
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_HTTPHEADER => [
                'Content-Type: application/x-www-form-urlencoded',
                'Content-Length: ' . strlen($postData),
            ],
            CURLOPT_FOLLOWLOCATION => false,
        ]);

src/Auth/Builder.php:168

  • generateUuidV4() relies on mt_rand(), which is not a cryptographically secure source of randomness. Since this value is used as a JWT ID (jti), generate UUIDs using random_bytes() (or another CSPRNG-backed approach) to avoid predictable identifiers.
    private static function generateUuidV4(): string
    {
        return sprintf(
            '%04x%04x-%04x-%04x-%04x-%04x%04x%04x',
            mt_rand(0, 0xffff),
            mt_rand(0, 0xffff),
            mt_rand(0, 0xffff),
            mt_rand(0, 0x0fff) | 0x4000,
            mt_rand(0, 0x3fff) | 0x8000,
            mt_rand(0, 0xffff),
            mt_rand(0, 0xffff),
            mt_rand(0, 0xffff)
        );

docs/DOCSCAN.md:113

  • This documentation states that the SDK validates screen identifiers and ignores invalid values, but the implementation in SdkConfigBuilder/SdkConfig does not perform any validation or filtering. Either implement validation as described or update the docs to match actual behavior.
### Best Practices

1. **Test thoroughly:** When suppressing screens, ensure that users still have enough context to complete the flow successfully.

2. **Validation:** The SDK will validate that screen identifiers correspond to known screens. Invalid identifiers will be ignored.

Instructions.md:150

  • This guide claims SessionConfigurationResponse::$sdkConfig is a SdkConfig object and uses nullsafe access ($this->sdkConfig?->getSuppressedScreens()), but the actual implementation stores sdk_config as a raw array and getSdkConfig() returns ?array. Please align the guide with the implementation (or vice versa).
### 3. SessionConfigurationResponse Enhancements

#### Added Properties
```php
/**
 * @var SdkConfig|null
 */
private $sdkConfig;

New Methods

/**
 * @return SdkConfig|null
 */
public function getSdkConfig(): ?SdkConfig
{
    return $this->sdkConfig;
}

/**
 * @return array<string>|null
 */
public function getSuppressedScreens(): ?array
{
    return $this->sdkConfig?->getSuppressedScreens();
}

test_suppressed_screens.php:29

  • This looks like a one-off manual test script rather than a unit/integration test or an example intended for distribution. Keeping it in the repository root increases maintenance burden and can confuse users/packaging. Consider removing it from the SDK, or moving it under examples/ (or converting it into a PHPUnit test).
    src/Constants.php:35
  • Constants::AUTH_API_URL / Constants::ENV_AUTH_API_URL are introduced but (in this PR) appear unused, while the auth module uses Yoti\Auth\Properties::DEFAULT_YOTI_AUTH_URL / ENV_YOTI_AUTH_URL instead. Keeping two parallel sources for the same URL/env var is likely to drift; consider either wiring the auth module to these constants or removing these constants to avoid confusion.
    /** Default Auth API URL */
    public const AUTH_API_URL = 'https://auth.api.yoti.com/v1/oauth/token';

    /** Environment variable to override the default Auth API URL */
    public const ENV_AUTH_API_URL = 'YOTI_AUTH_URL';

src/Http/AuthStrategy/SignedRequestStrategy.php:91

  • generateNonce() uses mt_rand(), which is not suitable for security-sensitive randomness. Nonces should be generated using a CSPRNG (e.g., random_bytes() / bin2hex() with correct UUIDv4 bit-setting) to avoid predictability.
    Instructions.md:110
  • This guide's SdkConfigBuilder example does not match the actual implementation: it shows suppressedScreens defaulting to [], merging arrays, and preventing duplicates via in_array(), but the real builder stores null, overwrites on withSuppressedScreens(), and does not deduplicate. Please update the guide to reflect the current code (or adjust the implementation to match this documented behavior).
#### Added Properties
```php
/**
 * @var array<string>
 */
private $suppressedScreens = [];

New Methods

/**
 * Set multiple suppressed screens at once
 * @param array<string> $suppressedScreens
 * @return $this
 */
public function withSuppressedScreens(array $suppressedScreens): self
{
    $this->suppressedScreens = array_merge($this->suppressedScreens, $suppressedScreens);
    return $this;
}

/**
 * Add a single suppressed screen
 * @param string $screenIdentifier
 * @return $this
 */
public function withSuppressedScreen(string $screenIdentifier): self
{
    if (!in_array($screenIdentifier, $this->suppressedScreens)) {
        $this->suppressedScreens[] = $screenIdentifier;
    }
    return $this;
}

src/DocScan/Session/Create/SdkConfigBuilder.php:284

  • The PR description focuses on central auth token support, but this change set also introduces/extends Doc Scan "suppressed_screens" configuration (builder/API response/doc updates). If suppressed screens are intended to ship in this PR, the description should mention it; otherwise consider splitting into a separate PR to keep scope clear.
    /**
     * Sets the suppressed screens array for configuration
     *
     * @param array<string> $suppressedScreens Array of screen identifiers to suppress
     * @return $this
     */
    public function withSuppressedScreens(array $suppressedScreens): self
    {
        $this->suppressedScreens = $suppressedScreens;
        return $this;
    }

    /**
     * Adds a single screen to the suppressed screens list
     *
     * @param string $screenIdentifier The screen identifier to suppress
     * @return $this
     */
    public function withSuppressedScreen(string $screenIdentifier): self
    {
        if ($this->suppressedScreens === null) {
            $this->suppressedScreens = [];
        }
        $this->suppressedScreens[] = $screenIdentifier;
        return $this;
    }

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 37 out of 46 changed files in this pull request and generated 1 comment.

Comments suppressed due to low confidence (5)

src/DocScan/Session/Create/SdkConfigBuilder.php:269

  • The withSuppressedScreens method replaces the entire array instead of merging, which is inconsistent with the documentation in Instructions.md (line 95) that states it should use array_merge. This could lead to unexpected behavior if called multiple times - the second call will completely replace screens added by the first call, rather than merging them as the documentation suggests.
    public function withSuppressedScreens(array $suppressedScreens): self
    {
        $this->suppressedScreens = $suppressedScreens;
        return $this;
    }

test_suppressed_screens.php:29

  • The test file test_suppressed_screens.php should not be committed to the repository root. This appears to be a temporary test script that should either be moved to the tests directory as a proper unit test or removed entirely before merging.
    composer.json:72
  • The composer.json includes an audit ignore for a firebase/php-jwt v6 advisory (PKSA-y2cr-5h3j-g3ys), but the dependency constraint allows both v6 and v7. Consider explicitly documenting which specific advisory is being ignored and why it's acceptable for this use case. Also verify that the advisory doesn't affect the specific usage pattern in this SDK (PS384-signed JWTs for authentication).
    "audit": {
      "ignore": {
        "PKSA-y2cr-5h3j-g3ys": "firebase/php-jwt v6 advisory - v7 requires PHP 8.0+, project supports PHP 7.4"
      }

Instructions.md:379

  • The Instructions.md file contains implementation details and should be in the docs directory for consistency with other documentation (like DOCSCAN.md). Additionally, it should be named more descriptively, such as SUPPRESSED_SCREENS_IMPLEMENTATION.md or integrated into the existing DOCSCAN.md file.
# Yoti PHP SDK - Suppressed Screens Configuration Implementation Guide

## Overview

This document provides comprehensive instructions for the **suppressed_screens configuration functionality** implemented for the Yoti PHP SDK IDV (Identity Document Verification) shortened flow. This feature allows developers to customize the user experience by suppressing specific screens during the identity verification process.

## Implementation Summary

### Feature: Suppressed Screens Configuration
- **Purpose**: Enable IDV shortened flow by allowing specific screens to be suppressed
- **Implementation Date**: July 2025
- **Branch**: `SDK-2615-php-support-configuration-for-idv-shortened-flow`
- **Status**: ✅ Complete and Production Ready

## Architecture Overview

### Core Components Modified

1. **SdkConfig** (`src/DocScan/Session/Create/SdkConfig.php`)
   - Core configuration model for DocScan SDK settings
   - Stores and manages suppressed screen identifiers

2. **SdkConfigBuilder** (`src/DocScan/Session/Create/SdkConfigBuilder.php`)
   - Builder pattern implementation for SDK configuration
   - Provides fluent interface for configuration setup

3. **SessionConfigurationResponse** (`src/DocScan/Session/Retrieve/Configuration/SessionConfigurationResponse.php`)
   - Response object for session configuration retrieval
   - Handles API responses containing SDK configuration

## Detailed Implementation

### 1. SdkConfig Class Enhancements

#### Added Properties
```php
/**
 * @var array<string>|null
 */
private $suppressedScreens;

Constructor Updates

public function __construct(
    // ... existing parameters
    ?array $suppressedScreens = null
) {
    // ... existing assignments
    $this->suppressedScreens = $suppressedScreens;
}

New Methods

/**
 * @return array<string>|null
 */
public function getSuppressedScreens(): ?array
{
    return $this->suppressedScreens;
}

JSON Serialization

public function jsonSerialize(): \stdClass
{
    return (object)Json::withoutNullValues([
        // ... existing fields
        'suppressed_screens' => $this->getSuppressedScreens()
    ]);
}

2. SdkConfigBuilder Class Enhancements

Added Properties

/**
 * @var array<string>
 */
private $suppressedScreens = [];

New Methods

/**
 * Set multiple suppressed screens at once
 * @param array<string> $suppressedScreens
 * @return $this
 */
public function withSuppressedScreens(array $suppressedScreens): self
{
    $this->suppressedScreens = array_merge($this->suppressedScreens, $suppressedScreens);
    return $this;
}

/**
 * Add a single suppressed screen
 * @param string $screenIdentifier
 * @return $this
 */
public function withSuppressedScreen(string $screenIdentifier): self
{
    if (!in_array($screenIdentifier, $this->suppressedScreens)) {
        $this->suppressedScreens[] = $screenIdentifier;
    }
    return $this;
}

Build Method Updates

public function build(): SdkConfig
{
    return new SdkConfig(
        // ... existing parameters
        !empty($this->suppressedScreens) ? $this->suppressedScreens : null
    );
}

3. SessionConfigurationResponse Enhancements

Added Properties

/**
 * @var SdkConfig|null
 */
private $sdkConfig;

New Methods

/**
 * @return SdkConfig|null
 */
public function getSdkConfig(): ?SdkConfig
{
    return $this->sdkConfig;
}

/**
 * @return array<string>|null
 */
public function getSuppressedScreens(): ?array
{
    return $this->sdkConfig?->getSuppressedScreens();
}

Usage Examples

Basic Configuration

use Yoti\DocScan\Session\Create\SdkConfigBuilder;

$builder = new SdkConfigBuilder();
$config = $builder
    ->withSuppressedScreens(['WELCOME_SCREEN', 'PRIVACY_POLICY'])
    ->withSuppressedScreen('TERMS_AND_CONDITIONS')
    ->build();

Session Creation with Suppressed Screens

use Yoti\DocScan\Session\Create\SessionSpecificationBuilder;

$sessionSpec = (new SessionSpecificationBuilder())
    ->withClientSessionTokenTtl(600)
    ->withResourcesTtl(90000)
    ->withUserTrackingId('unique-user-id')
    ->withSdkConfig($config)
    ->build();

$session = $docScanClient->createSession($sessionSpec);

Retrieving Configuration

$sessionConfig = $docScanClient->getSessionConfiguration($sessionId);
$suppressedScreens = $sessionConfig->getSuppressedScreens();

if ($suppressedScreens) {
    echo "Suppressed screens: " . implode(', ', $suppressedScreens);
}

Common Screen Identifiers

The following screen identifiers are commonly used:

  • WELCOME_SCREEN - Initial welcome/landing screen
  • PRIVACY_POLICY - Privacy policy information screen
  • TERMS_AND_CONDITIONS - Terms and conditions screen
  • DOCUMENT_SELECTION - Document type selection screen
  • CAMERA_PERMISSIONS - Camera permission request screen
  • COUNTRY_SELECTION - Country selection screen
  • INSTRUCTION_SCREENS - Various instruction screens

Testing

Test Coverage

  • 350 tests covering all DocScan functionality
  • 988 assertions validating behavior
  • 100% success rate on implementation

Key Test Files

  • tests/DocScan/Session/Create/SdkConfigBuilderTest.php
  • tests/DocScan/Session/Retrieve/Configuration/SessionConfigurationResponseTest.php

Running Tests

# Run all DocScan tests
composer test -- tests/DocScan/

# Run specific suppressed screens tests
composer test -- tests/DocScan/Session/Create/SdkConfigBuilderTest.php

JSON API Format

Request Format (Session Creation)

{
  "client_session_token_ttl": 600,
  "resources_ttl": 90000,
  "user_tracking_id": "unique-user-id",
  "sdk_config": {
    "suppressed_screens": [
      "WELCOME_SCREEN",
      "PRIVACY_POLICY",
      "TERMS_AND_CONDITIONS"
    ]
  }
}

Response Format (Configuration Retrieval)

{
  "sdk_config": {
    "suppressed_screens": [
      "WELCOME_SCREEN",
      "PRIVACY_POLICY"
    ]
  }
}

Development Guidelines

Code Standards

  • Follow existing PSR-12 coding standards
  • Maintain strict typing with declare(strict_types=1);
  • Use nullable types appropriately (?array, ?string)
  • Include comprehensive PHPDoc annotations

Adding New Screen Types

  1. Update screen identifier constants if needed
  2. Add validation in builder methods if required
  3. Update tests to cover new scenarios
  4. Update documentation and examples

Backward Compatibility

  • All changes maintain backward compatibility
  • Existing constructors work without modification
  • New parameters are optional with null defaults
  • JSON serialization excludes null values

Troubleshooting

Common Issues

  1. Empty Array vs Null

    • Empty arrays are converted to null in the build process
    • This prevents unnecessary empty arrays in JSON output
  2. Duplicate Screen Identifiers

    • The builder automatically prevents duplicates
    • Use withSuppressedScreen() for safe individual additions
  3. Type Safety

    • All methods use strict typing
    • Array type hints ensure only string arrays are accepted

Debugging

// Check if screens are properly set
$config = $builder->build();
var_dump($config->getSuppressedScreens());

// Verify JSON output
echo json_encode($config, JSON_PRETTY_PRINT);

Performance Considerations

  • Minimal memory overhead (array of strings)
  • Efficient array operations with duplicate prevention
  • JSON serialization optimized with null value filtering
  • No impact on existing functionality

Security Considerations

  • Screen identifiers are treated as strings (no code execution)
  • Input validation through type hints
  • No sensitive data stored in configuration
  • Standard JSON encoding/decoding

Future Enhancements

Potential Improvements

  1. Screen Identifier Validation

    • Add enum or constants for valid screen identifiers
    • Implement validation in builder methods
  2. Configuration Presets

    • Create predefined configurations for common use cases
    • Add factory methods for quick setup
  3. Advanced Filtering

    • Support for conditional screen suppression
    • Screen suppression based on user context

Maintenance

Regular Tasks

  • Run test suite before any changes
  • Update PHPStan analysis configuration as needed
  • Review and update screen identifier documentation
  • Monitor for new screen types in Yoti platform updates

Version Compatibility

  • Compatible with PHP 7.4, 8.0, 8.1+
  • No breaking changes to existing API
  • Follows semantic versioning principles

Related Documentation


Quick Reference

Key Files Modified

src/DocScan/Session/Create/SdkConfig.php
src/DocScan/Session/Create/SdkConfigBuilder.php
src/DocScan/Session/Retrieve/Configuration/SessionConfigurationResponse.php
tests/DocScan/Session/Create/SdkConfigBuilderTest.php
tests/DocScan/Session/Retrieve/Configuration/SessionConfigurationResponseTest.php

Key Methods Added

// SdkConfig
public function getSuppressedScreens(): ?array

// SdkConfigBuilder
public function withSuppressedScreens(array $suppressedScreens): self
public function withSuppressedScreen(string $screenIdentifier): self

// SessionConfigurationResponse
public function getSdkConfig(): ?SdkConfig
public function getSuppressedScreens(): ?array

Testing Commands

composer test -- tests/DocScan/
vendor/bin/phpstan analyse src/DocScan/

This implementation enables flexible IDV flow customization while maintaining the high code quality and backward compatibility standards of the Yoti PHP SDK.

**src/Auth/AuthenticationTokenGenerator.php:145**
* The private key resource obtained from `openssl_pkey_get_private` is not explicitly freed. While PHP will eventually free the resource through garbage collection, it's better practice to explicitly free OpenSSL resources using `openssl_pkey_free($privateKey)` after encoding the JWT to avoid resource leaks, especially in long-running processes.
    $privateKey = openssl_pkey_get_private((string) $this->pemFile);
    if ($privateKey === false) {
        throw new AuthException('Failed to load private key from PEM file');
    }

    return \Firebase\JWT\JWT::encode($claims, $privateKey, 'PS384', null, $header);
</details>



---

💡 <a href="/getyoti/yoti-php-sdk/new/master/.github/instructions?filename=*.instructions.md" class="Link--inTextBlock" target="_blank" rel="noopener noreferrer">Add Copilot custom instructions</a> for smarter, more guided reviews. <a href="https://docs.github.com/en/copilot/customizing-copilot/adding-repository-custom-instructions-for-github-copilot" class="Link--inTextBlock" target="_blank" rel="noopener noreferrer">Learn how to get started</a>.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 37 out of 46 changed files in this pull request and generated no new comments.

Comments suppressed due to low confidence (5)

Instructions.md:13

  • The implementation date references "July 2025" which is in the future. This should be corrected to the actual implementation date. Additionally, the branch name mentioned (SDK-2615-php-support-configuration-for-idv-shortened-flow) doesn't match the current PR title (SDK-2767 support central auth tokens).
- **Purpose**: Enable IDV shortened flow by allowing specific screens to be suppressed
- **Implementation Date**: July 2025
- **Branch**: `SDK-2615-php-support-configuration-for-idv-shortened-flow`
- **Status**: ✅ Complete and Production Ready

docs/DOCSCAN.md:83

  • This example uses the legacy constructor pattern. Consider updating it to use the new builder pattern to be consistent with the new authentication features added in this PR. For example: $docScanClient = DocScanClient::builder()->withClientSdkId($sdkId)->withPemFile($pemFile)->build();
$docScanClient = new DocScanClient($sdkId, $pemFile);

docs/DOCSCAN.md:112

  • The documentation states "The SDK will validate that screen identifiers correspond to known screens" but I don't see validation logic in the implementation. The SdkConfigBuilder accepts any string array without validation. Consider either implementing validation or updating this documentation to be accurate.
2. **Validation:** The SDK will validate that screen identifiers correspond to known screens. Invalid identifiers will be ignored.

test_suppressed_screens.php:29

  • This test file should not be committed to the repository. It appears to be a temporary test script used during development. Consider removing it or moving it to a proper test location if needed for future reference.
    Instructions.md:379
  • This document contains implementation notes and references a future date (July 2025) and mentions a different branch name (SDK-2615-php-support-configuration-for-idv-shortened-flow) than the current PR. This appears to be internal documentation that should not be part of the codebase. Consider either removing it or moving relevant content to the DOCSCAN.md documentation file.
# Yoti PHP SDK - Suppressed Screens Configuration Implementation Guide

## Overview

This document provides comprehensive instructions for the **suppressed_screens configuration functionality** implemented for the Yoti PHP SDK IDV (Identity Document Verification) shortened flow. This feature allows developers to customize the user experience by suppressing specific screens during the identity verification process.

## Implementation Summary

### Feature: Suppressed Screens Configuration
- **Purpose**: Enable IDV shortened flow by allowing specific screens to be suppressed
- **Implementation Date**: July 2025
- **Branch**: `SDK-2615-php-support-configuration-for-idv-shortened-flow`
- **Status**: ✅ Complete and Production Ready

## Architecture Overview

### Core Components Modified

1. **SdkConfig** (`src/DocScan/Session/Create/SdkConfig.php`)
   - Core configuration model for DocScan SDK settings
   - Stores and manages suppressed screen identifiers

2. **SdkConfigBuilder** (`src/DocScan/Session/Create/SdkConfigBuilder.php`)
   - Builder pattern implementation for SDK configuration
   - Provides fluent interface for configuration setup

3. **SessionConfigurationResponse** (`src/DocScan/Session/Retrieve/Configuration/SessionConfigurationResponse.php`)
   - Response object for session configuration retrieval
   - Handles API responses containing SDK configuration

## Detailed Implementation

### 1. SdkConfig Class Enhancements

#### Added Properties
```php
/**
 * @var array<string>|null
 */
private $suppressedScreens;

Constructor Updates

public function __construct(
    // ... existing parameters
    ?array $suppressedScreens = null
) {
    // ... existing assignments
    $this->suppressedScreens = $suppressedScreens;
}

New Methods

/**
 * @return array<string>|null
 */
public function getSuppressedScreens(): ?array
{
    return $this->suppressedScreens;
}

JSON Serialization

public function jsonSerialize(): \stdClass
{
    return (object)Json::withoutNullValues([
        // ... existing fields
        'suppressed_screens' => $this->getSuppressedScreens()
    ]);
}

2. SdkConfigBuilder Class Enhancements

Added Properties

/**
 * @var array<string>
 */
private $suppressedScreens = [];

New Methods

/**
 * Set multiple suppressed screens at once
 * @param array<string> $suppressedScreens
 * @return $this
 */
public function withSuppressedScreens(array $suppressedScreens): self
{
    $this->suppressedScreens = array_merge($this->suppressedScreens, $suppressedScreens);
    return $this;
}

/**
 * Add a single suppressed screen
 * @param string $screenIdentifier
 * @return $this
 */
public function withSuppressedScreen(string $screenIdentifier): self
{
    if (!in_array($screenIdentifier, $this->suppressedScreens)) {
        $this->suppressedScreens[] = $screenIdentifier;
    }
    return $this;
}

Build Method Updates

public function build(): SdkConfig
{
    return new SdkConfig(
        // ... existing parameters
        !empty($this->suppressedScreens) ? $this->suppressedScreens : null
    );
}

3. SessionConfigurationResponse Enhancements

Added Properties

/**
 * @var SdkConfig|null
 */
private $sdkConfig;

New Methods

/**
 * @return SdkConfig|null
 */
public function getSdkConfig(): ?SdkConfig
{
    return $this->sdkConfig;
}

/**
 * @return array<string>|null
 */
public function getSuppressedScreens(): ?array
{
    return $this->sdkConfig?->getSuppressedScreens();
}

Usage Examples

Basic Configuration

use Yoti\DocScan\Session\Create\SdkConfigBuilder;

$builder = new SdkConfigBuilder();
$config = $builder
    ->withSuppressedScreens(['WELCOME_SCREEN', 'PRIVACY_POLICY'])
    ->withSuppressedScreen('TERMS_AND_CONDITIONS')
    ->build();

Session Creation with Suppressed Screens

use Yoti\DocScan\Session\Create\SessionSpecificationBuilder;

$sessionSpec = (new SessionSpecificationBuilder())
    ->withClientSessionTokenTtl(600)
    ->withResourcesTtl(90000)
    ->withUserTrackingId('unique-user-id')
    ->withSdkConfig($config)
    ->build();

$session = $docScanClient->createSession($sessionSpec);

Retrieving Configuration

$sessionConfig = $docScanClient->getSessionConfiguration($sessionId);
$suppressedScreens = $sessionConfig->getSuppressedScreens();

if ($suppressedScreens) {
    echo "Suppressed screens: " . implode(', ', $suppressedScreens);
}

Common Screen Identifiers

The following screen identifiers are commonly used:

  • WELCOME_SCREEN - Initial welcome/landing screen
  • PRIVACY_POLICY - Privacy policy information screen
  • TERMS_AND_CONDITIONS - Terms and conditions screen
  • DOCUMENT_SELECTION - Document type selection screen
  • CAMERA_PERMISSIONS - Camera permission request screen
  • COUNTRY_SELECTION - Country selection screen
  • INSTRUCTION_SCREENS - Various instruction screens

Testing

Test Coverage

  • 350 tests covering all DocScan functionality
  • 988 assertions validating behavior
  • 100% success rate on implementation

Key Test Files

  • tests/DocScan/Session/Create/SdkConfigBuilderTest.php
  • tests/DocScan/Session/Retrieve/Configuration/SessionConfigurationResponseTest.php

Running Tests

# Run all DocScan tests
composer test -- tests/DocScan/

# Run specific suppressed screens tests
composer test -- tests/DocScan/Session/Create/SdkConfigBuilderTest.php

JSON API Format

Request Format (Session Creation)

{
  "client_session_token_ttl": 600,
  "resources_ttl": 90000,
  "user_tracking_id": "unique-user-id",
  "sdk_config": {
    "suppressed_screens": [
      "WELCOME_SCREEN",
      "PRIVACY_POLICY",
      "TERMS_AND_CONDITIONS"
    ]
  }
}

Response Format (Configuration Retrieval)

{
  "sdk_config": {
    "suppressed_screens": [
      "WELCOME_SCREEN",
      "PRIVACY_POLICY"
    ]
  }
}

Development Guidelines

Code Standards

  • Follow existing PSR-12 coding standards
  • Maintain strict typing with declare(strict_types=1);
  • Use nullable types appropriately (?array, ?string)
  • Include comprehensive PHPDoc annotations

Adding New Screen Types

  1. Update screen identifier constants if needed
  2. Add validation in builder methods if required
  3. Update tests to cover new scenarios
  4. Update documentation and examples

Backward Compatibility

  • All changes maintain backward compatibility
  • Existing constructors work without modification
  • New parameters are optional with null defaults
  • JSON serialization excludes null values

Troubleshooting

Common Issues

  1. Empty Array vs Null

    • Empty arrays are converted to null in the build process
    • This prevents unnecessary empty arrays in JSON output
  2. Duplicate Screen Identifiers

    • The builder automatically prevents duplicates
    • Use withSuppressedScreen() for safe individual additions
  3. Type Safety

    • All methods use strict typing
    • Array type hints ensure only string arrays are accepted

Debugging

// Check if screens are properly set
$config = $builder->build();
var_dump($config->getSuppressedScreens());

// Verify JSON output
echo json_encode($config, JSON_PRETTY_PRINT);

Performance Considerations

  • Minimal memory overhead (array of strings)
  • Efficient array operations with duplicate prevention
  • JSON serialization optimized with null value filtering
  • No impact on existing functionality

Security Considerations

  • Screen identifiers are treated as strings (no code execution)
  • Input validation through type hints
  • No sensitive data stored in configuration
  • Standard JSON encoding/decoding

Future Enhancements

Potential Improvements

  1. Screen Identifier Validation

    • Add enum or constants for valid screen identifiers
    • Implement validation in builder methods
  2. Configuration Presets

    • Create predefined configurations for common use cases
    • Add factory methods for quick setup
  3. Advanced Filtering

    • Support for conditional screen suppression
    • Screen suppression based on user context

Maintenance

Regular Tasks

  • Run test suite before any changes
  • Update PHPStan analysis configuration as needed
  • Review and update screen identifier documentation
  • Monitor for new screen types in Yoti platform updates

Version Compatibility

  • Compatible with PHP 7.4, 8.0, 8.1+
  • No breaking changes to existing API
  • Follows semantic versioning principles

Related Documentation


Quick Reference

Key Files Modified

src/DocScan/Session/Create/SdkConfig.php
src/DocScan/Session/Create/SdkConfigBuilder.php
src/DocScan/Session/Retrieve/Configuration/SessionConfigurationResponse.php
tests/DocScan/Session/Create/SdkConfigBuilderTest.php
tests/DocScan/Session/Retrieve/Configuration/SessionConfigurationResponseTest.php

Key Methods Added

// SdkConfig
public function getSuppressedScreens(): ?array

// SdkConfigBuilder
public function withSuppressedScreens(array $suppressedScreens): self
public function withSuppressedScreen(string $screenIdentifier): self

// SessionConfigurationResponse
public function getSdkConfig(): ?SdkConfig
public function getSuppressedScreens(): ?array

Testing Commands

composer test -- tests/DocScan/
vendor/bin/phpstan analyse src/DocScan/

This implementation enables flexible IDV flow customization while maintaining the high code quality and backward compatibility standards of the Yoti PHP SDK.

</details>



---

💡 <a href="/getyoti/yoti-php-sdk/new/master/.github/instructions?filename=*.instructions.md" class="Link--inTextBlock" target="_blank" rel="noopener noreferrer">Add Copilot custom instructions</a> for smarter, more guided reviews. <a href="https://docs.github.com/en/copilot/customizing-copilot/adding-repository-custom-instructions-for-github-copilot" class="Link--inTextBlock" target="_blank" rel="noopener noreferrer">Learn how to get started</a>.

Copy link
Contributor

@nikhilPank nikhilPank left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM - AuthenticationTokenGenerator works now
DocScanClient::builder works with both the legacy signed requests and new auth tokens

@mehmet-yoti mehmet-yoti merged commit a2433ef into development Feb 25, 2026
12 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants