.. SPDX-License-Identifier: GPL-2.0-or-later .. SPDX-FileCopyrightText: Netresearch DTT GmbH .. include:: /Includes.rst.txt .. _usage-extension-settings: ============================================= Example: SaaS API keys in extension settings ============================================= Many TYPO3 extensions integrate with SaaS services (DeepL, Personio, Stepstone, Stripe, etc.) and store API keys in extension settings. This page shows how to secure these credentials with nr-vault. .. contents:: Table of contents :local: :depth: 2 .. _extension-settings-challenge: The challenge ============= Extension settings defined in :file:`ext_conf_template.txt` are stored in TYPO3's local system configuration files. The filename depends on the TYPO3 version and project layout. These settings do not use TCA tables. This means: - The ``renderType: 'vaultSecret'`` approach doesn't work directly - API keys are stored as plaintext in the filesystem - Keys may end up in version control or backups .. _extension-settings-approaches: Approaches ========== There are three approaches to secure extension settings with vault: .. _extension-settings-approach-1: Approach 1: Store vault identifier in settings (recommended) ------------------------------------------------------------ Store the secret's identifier in extension settings, resolve at runtime. **Advantages:** - Works with existing extension settings UI - No schema changes required - Secrets properly encrypted in vault - Uses the identifier directly with the vault HTTP client **Extension settings template:** .. code-block:: text :caption: EXT:my_deepl_extension/ext_conf_template.txt # cat=api; type=string; label=DeepL API Key (Vault Identifier): Enter the identifier of your stored secret deeplApiKey = The admin enters the identifier ``deepl_api_key`` (not the actual key). The HTTP client resolves this identifier at use time. The optional ``VaultReference`` helper described in ADR-009 is not implemented; use its direct-identifier pattern. See :ref:`adr-009-extension-configuration-secrets` for the design rationale. **Service implementation:** .. literalinclude:: _DeepLServiceVault.php :language: php :caption: EXT:my_deepl_extension/Classes/Service/DeepLService.php Register the service in the consuming extension's dependency-injection configuration. TYPO3 13 and 14 provide the ``RequestFactoryInterface`` alias through Core's HTTP request factory; the container injects it alongside the vault service and extension configuration. .. literalinclude:: _DeepLServices.yaml :language: yaml :caption: EXT:my_deepl_extension/Configuration/Services.yaml **Setup steps:** 1. Store the actual DeepL API key in vault: **Via backend (recommended):** a. Go to :guilabel:`Admin Tools > Vault > Secrets` b. Click :guilabel:`+ Create new` c. Enter identifier: ``deepl_api_key`` d. Paste your DeepL API key into the secret field e. Optionally set owner and allowed groups f. Click :guilabel:`Save` **Via CLI (alternative):** .. code-block:: bash :caption: Store API key via command line ./vendor/bin/typo3 vault:store deepl_api_key --value="your-deepl-api-key-here" 2. Configure the extension setting with the identifier: **Via backend:** a. Go to :guilabel:`Admin Tools > Settings > Extension Configuration` b. Find your extension and expand it c. Enter the identifier: ``deepl_api_key`` d. Click :guilabel:`Save` 3. The service passes the identifier to the HTTP client, which resolves the secret at use time. .. tip:: No CLI or file access required. Admins can manage everything through the TYPO3 backend: - Create secrets in :guilabel:`Admin Tools > Vault` - Reference them in :guilabel:`Extension Configuration` with their identifier .. _extension-settings-approach-2: Approach 2: Environment variable reference ------------------------------------------ For containerized deployments, reference environment variables. **Extension settings template:** .. code-block:: text :caption: EXT:my_deepl_extension/ext_conf_template.txt # cat=api; type=string; label=DeepL API Key (env var): Environment variable name containing the API key deeplApiKeyEnvVar = DEEPL_API_KEY **Service implementation:** .. literalinclude:: _DeepLServiceEnv.php :language: php :caption: EXT:my_deepl_extension/Classes/Service/DeepLService.php .. warning:: This approach stores the API key in memory for the request lifetime. No automatic memory cleanup. Consider Approach 1 for sensitive keys. .. _extension-settings-approach-3: Approach 3: Configuration record with TCA vault field ------------------------------------------------------ For maximum security and a proper backend UI, create a configuration record. **TCA definition:** .. literalinclude:: _tca-deepl-config.php :language: php :caption: EXT:my_deepl_extension/Configuration/TCA/tx_mydeeplext_config.php :lines: 3- **Repository to load configuration:** .. literalinclude:: _ConfigRepository.php :language: php :caption: EXT:my_deepl_extension/Classes/Domain/Repository/ConfigRepository.php **DTO:** .. literalinclude:: _DeepLConfig.php :language: php :caption: EXT:my_deepl_extension/Classes/Domain/Dto/DeepLConfig.php **Missing configuration exception:** The service reports a missing active configuration with this application-specific exception before delegating any HTTP request. .. literalinclude:: _DeepLNotConfiguredException.php :language: php :caption: EXT:my_deepl_extension/Classes/Exception/DeepLNotConfiguredException.php **Service using config record:** The TCA enables soft deletion because the repository selects records with ``deleted = 0``. The service delegates the stored vault UUID to the HTTP client and selects the ``Authorization: DeepL-Auth-Key`` scheme required by DeepL. .. literalinclude:: _DeepLServiceTca.php :language: php :caption: EXT:my_deepl_extension/Classes/Service/DeepLService.php **Advantages of Approach 3:** - Full vault UI with masked input and reveal — plus copy, outside the hardened profile, which disables it deliberately - Proper access control (owner, groups) - Audit logging of configuration access - Multiple configurations possible (e.g., per site) .. _extension-settings-comparison: Comparison ========== .. list-table:: :header-rows: 1 :widths: 25 25 25 25 * - Aspect - Approach 1 (Vault ID) - Approach 2 (Env var) - Approach 3 (TCA record) * - Setup complexity - Low - Low - Medium * - Security - High - Medium - High * - Memory safety - Yes (sodium_memzero) - No - Yes (sodium_memzero) * - Audit trail - Yes - No - Yes * - Backend UI - Text field - Text field - Vault secret field * - Multi-config - Manual - Manual - Native **Recommendation:** Use Approach 1 for simple single-key integrations, Approach 3 for complex integrations requiring multiple configurations or strict access control.