@smileid/usesmileid 12.0.0-beta01 → 12.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +11 -0
- package/README.md +701 -89
- package/dist/camera/camera_defaults.d.ts +14 -9
- package/dist/camera/camera_defaults.d.ts.map +1 -1
- package/dist/camera/camera_defaults.js +11 -9
- package/dist/camera/camera_defaults.js.map +1 -1
- package/dist/camera/orientation_degrees.d.ts +5 -6
- package/dist/camera/orientation_degrees.d.ts.map +1 -1
- package/dist/camera/orientation_degrees.js +5 -6
- package/dist/camera/orientation_degrees.js.map +1 -1
- package/dist/core/models/document_capture_mode.d.ts +8 -0
- package/dist/core/models/document_capture_mode.d.ts.map +1 -1
- package/dist/core/models/document_capture_mode.js +24 -0
- package/dist/core/models/document_capture_mode.js.map +1 -1
- package/dist/core/models/document_type.d.ts +2 -2
- package/dist/core/models/document_type.d.ts.map +1 -1
- package/dist/core/models/document_type.js +6 -4
- package/dist/core/models/document_type.js.map +1 -1
- package/dist/core/models/result.d.ts +9 -8
- package/dist/core/models/result.d.ts.map +1 -1
- package/dist/core/models/result.js +5 -6
- package/dist/core/models/result.js.map +1 -1
- package/dist/data/api/interceptors/smile_auth_interceptor.js +1 -1
- package/dist/data/api/interceptors/smile_auth_interceptor.js.map +1 -1
- package/dist/data/api/interceptors/smile_integrity_interceptor.d.ts +11 -0
- package/dist/data/api/interceptors/smile_integrity_interceptor.d.ts.map +1 -1
- package/dist/data/api/interceptors/smile_integrity_interceptor.js +13 -0
- package/dist/data/api/interceptors/smile_integrity_interceptor.js.map +1 -1
- package/dist/data/api/interceptors/smile_request_mac_interceptor.d.ts +8 -0
- package/dist/data/api/interceptors/smile_request_mac_interceptor.d.ts.map +1 -1
- package/dist/data/api/interceptors/smile_request_mac_interceptor.js +11 -0
- package/dist/data/api/interceptors/smile_request_mac_interceptor.js.map +1 -1
- package/dist/data/compression/compressible_asset.d.ts +41 -0
- package/dist/data/compression/compressible_asset.d.ts.map +1 -0
- package/dist/data/compression/compressible_asset.js +34 -0
- package/dist/data/compression/compressible_asset.js.map +1 -0
- package/dist/data/compression/payload_compression_config.d.ts +36 -0
- package/dist/data/compression/payload_compression_config.d.ts.map +1 -0
- package/dist/data/compression/payload_compression_config.js +52 -0
- package/dist/data/compression/payload_compression_config.js.map +1 -0
- package/dist/data/compression/payload_compression_runner.d.ts +29 -0
- package/dist/data/compression/payload_compression_runner.d.ts.map +1 -0
- package/dist/data/compression/payload_compression_runner.js +55 -0
- package/dist/data/compression/payload_compression_runner.js.map +1 -0
- package/dist/data/compression/payload_compressor.d.ts +40 -0
- package/dist/data/compression/payload_compressor.d.ts.map +1 -0
- package/dist/data/compression/payload_compressor.js +126 -0
- package/dist/data/compression/payload_compressor.js.map +1 -0
- package/dist/data/compression/uri_compressible_asset.d.ts +23 -0
- package/dist/data/compression/uri_compressible_asset.d.ts.map +1 -0
- package/dist/data/compression/uri_compressible_asset.js +77 -0
- package/dist/data/compression/uri_compressible_asset.js.map +1 -0
- package/dist/data/dsl/builder/cache_config_builder.d.ts +2 -1
- package/dist/data/dsl/builder/cache_config_builder.d.ts.map +1 -1
- package/dist/data/dsl/builder/cache_config_builder.js +2 -1
- package/dist/data/dsl/builder/cache_config_builder.js.map +1 -1
- package/dist/data/dsl/builder/config_builder.d.ts +40 -14
- package/dist/data/dsl/builder/config_builder.d.ts.map +1 -1
- package/dist/data/dsl/builder/config_builder.js +40 -14
- package/dist/data/dsl/builder/config_builder.js.map +1 -1
- package/dist/data/dsl/builder/interceptor_config_builder.d.ts +9 -7
- package/dist/data/dsl/builder/interceptor_config_builder.d.ts.map +1 -1
- package/dist/data/dsl/builder/interceptor_config_builder.js +9 -7
- package/dist/data/dsl/builder/interceptor_config_builder.js.map +1 -1
- package/dist/data/dsl/builder/retry_config_builder.d.ts +16 -1
- package/dist/data/dsl/builder/retry_config_builder.d.ts.map +1 -1
- package/dist/data/dsl/builder/retry_config_builder.js +16 -1
- package/dist/data/dsl/builder/retry_config_builder.js.map +1 -1
- package/dist/data/dsl/builder/timeout_config_builder.d.ts +4 -0
- package/dist/data/dsl/builder/timeout_config_builder.d.ts.map +1 -1
- package/dist/data/dsl/builder/timeout_config_builder.js +4 -0
- package/dist/data/dsl/builder/timeout_config_builder.js.map +1 -1
- package/dist/data/dsl/use_smile_id_network_builder.d.ts +17 -0
- package/dist/data/dsl/use_smile_id_network_builder.d.ts.map +1 -1
- package/dist/data/dsl/use_smile_id_network_builder.js +17 -0
- package/dist/data/dsl/use_smile_id_network_builder.js.map +1 -1
- package/dist/data/factory/networking_factory.d.ts.map +1 -1
- package/dist/data/factory/networking_factory.js +11 -1
- package/dist/data/factory/networking_factory.js.map +1 -1
- package/dist/data/repository/use_smile_id_repository_impl.d.ts +6 -0
- package/dist/data/repository/use_smile_id_repository_impl.d.ts.map +1 -1
- package/dist/data/repository/use_smile_id_repository_impl.js +82 -8
- package/dist/data/repository/use_smile_id_repository_impl.js.map +1 -1
- package/dist/data/security/request_canonicalizer.d.ts.map +1 -1
- package/dist/data/security/request_canonicalizer.js +14 -2
- package/dist/data/security/request_canonicalizer.js.map +1 -1
- package/dist/data/security/security_policy.d.ts +43 -0
- package/dist/data/security/security_policy.d.ts.map +1 -0
- package/dist/data/security/security_policy.js +61 -0
- package/dist/data/security/security_policy.js.map +1 -0
- package/dist/data/util/retry_util.d.ts +1 -1
- package/dist/data/util/retry_util.d.ts.map +1 -1
- package/dist/data/util/retry_util.js +2 -1
- package/dist/data/util/retry_util.js.map +1 -1
- package/dist/data/util/use_smile_id_token_decoder.d.ts +22 -0
- package/dist/data/util/use_smile_id_token_decoder.d.ts.map +1 -1
- package/dist/data/util/use_smile_id_token_decoder.js +58 -24
- package/dist/data/util/use_smile_id_token_decoder.js.map +1 -1
- package/dist/l10n/strings_en.d.ts +2 -1
- package/dist/l10n/strings_en.d.ts.map +1 -1
- package/dist/l10n/strings_en.js +2 -1
- package/dist/l10n/strings_en.js.map +1 -1
- package/dist/l10n/use_smile_id_localizations.d.ts +1 -0
- package/dist/l10n/use_smile_id_localizations.d.ts.map +1 -1
- package/dist/l10n/use_smile_id_localizations.js +3 -0
- package/dist/l10n/use_smile_id_localizations.js.map +1 -1
- package/dist/metadata/builder_configuration.d.ts +39 -0
- package/dist/metadata/builder_configuration.d.ts.map +1 -0
- package/dist/metadata/builder_configuration.js +34 -0
- package/dist/metadata/builder_configuration.js.map +1 -0
- package/dist/metadata/captured_image_properties.d.ts +11 -0
- package/dist/metadata/captured_image_properties.d.ts.map +1 -0
- package/dist/metadata/captured_image_properties.js +11 -0
- package/dist/metadata/captured_image_properties.js.map +1 -0
- package/dist/metadata/device_orientation_collector.d.ts +17 -0
- package/dist/metadata/device_orientation_collector.d.ts.map +1 -0
- package/dist/metadata/device_orientation_collector.js +66 -0
- package/dist/metadata/device_orientation_collector.js.map +1 -0
- package/dist/metadata/geolocation.d.ts +20 -0
- package/dist/metadata/geolocation.d.ts.map +1 -0
- package/dist/metadata/geolocation.js +40 -0
- package/dist/metadata/geolocation.js.map +1 -0
- package/dist/metadata/metadata_configuration_mappers.d.ts +48 -0
- package/dist/metadata/metadata_configuration_mappers.d.ts.map +1 -0
- package/dist/metadata/metadata_configuration_mappers.js +171 -0
- package/dist/metadata/metadata_configuration_mappers.js.map +1 -0
- package/dist/metadata/model_configuration.d.ts +41 -0
- package/dist/metadata/model_configuration.d.ts.map +1 -0
- package/dist/metadata/model_configuration.js +44 -0
- package/dist/metadata/model_configuration.js.map +1 -0
- package/dist/metadata/optional_native_modules.d.ts +79 -3
- package/dist/metadata/optional_native_modules.d.ts.map +1 -1
- package/dist/metadata/optional_native_modules.js +108 -3
- package/dist/metadata/optional_native_modules.js.map +1 -1
- package/dist/metadata/use_smile_id_metadata.d.ts +45 -7
- package/dist/metadata/use_smile_id_metadata.d.ts.map +1 -1
- package/dist/metadata/use_smile_id_metadata.js +85 -13
- package/dist/metadata/use_smile_id_metadata.js.map +1 -1
- package/dist/metadata/use_smile_id_metadata_factory.d.ts +37 -0
- package/dist/metadata/use_smile_id_metadata_factory.d.ts.map +1 -1
- package/dist/metadata/use_smile_id_metadata_factory.js +184 -12
- package/dist/metadata/use_smile_id_metadata_factory.js.map +1 -1
- package/dist/presentation/assets/svg/document_shimmer_strings.d.ts +14 -0
- package/dist/presentation/assets/svg/document_shimmer_strings.d.ts.map +1 -0
- package/dist/presentation/assets/svg/document_shimmer_strings.js +14 -0
- package/dist/presentation/assets/svg/document_shimmer_strings.js.map +1 -0
- package/dist/presentation/assets/svg/instruction_svg_strings.d.ts +12 -4
- package/dist/presentation/assets/svg/instruction_svg_strings.d.ts.map +1 -1
- package/dist/presentation/assets/svg/instruction_svg_strings.js +19 -8
- package/dist/presentation/assets/svg/instruction_svg_strings.js.map +1 -1
- package/dist/presentation/components/helpers/enhanced_smart_selfie_animation_frames.d.ts +0 -5
- package/dist/presentation/components/helpers/enhanced_smart_selfie_animation_frames.d.ts.map +1 -1
- package/dist/presentation/components/helpers/enhanced_smart_selfie_animation_frames.js +0 -5
- package/dist/presentation/components/helpers/enhanced_smart_selfie_animation_frames.js.map +1 -1
- package/dist/presentation/components/icons/index.d.ts +24 -8
- package/dist/presentation/components/icons/index.d.ts.map +1 -1
- package/dist/presentation/components/icons/index.js +31 -11
- package/dist/presentation/components/icons/index.js.map +1 -1
- package/dist/presentation/components/use_smile_id_attribution.d.ts +1 -1
- package/dist/presentation/components/use_smile_id_button.d.ts +2 -2
- package/dist/presentation/components/use_smile_id_button.js +4 -4
- package/dist/presentation/components/use_smile_id_button.js.map +1 -1
- package/dist/presentation/components/use_smile_id_capture_content.d.ts +13 -0
- package/dist/presentation/components/use_smile_id_capture_content.d.ts.map +1 -1
- package/dist/presentation/components/use_smile_id_capture_content.js +15 -9
- package/dist/presentation/components/use_smile_id_capture_content.js.map +1 -1
- package/dist/presentation/components/use_smile_id_captured_document_image.d.ts +2 -3
- package/dist/presentation/components/use_smile_id_captured_document_image.d.ts.map +1 -1
- package/dist/presentation/components/use_smile_id_captured_document_image.js +1 -3
- package/dist/presentation/components/use_smile_id_captured_document_image.js.map +1 -1
- package/dist/presentation/components/use_smile_id_document_capture_controls.d.ts +12 -6
- package/dist/presentation/components/use_smile_id_document_capture_controls.d.ts.map +1 -1
- package/dist/presentation/components/use_smile_id_document_capture_controls.js +59 -16
- package/dist/presentation/components/use_smile_id_document_capture_controls.js.map +1 -1
- package/dist/presentation/components/use_smile_id_document_overlay.d.ts +22 -5
- package/dist/presentation/components/use_smile_id_document_overlay.d.ts.map +1 -1
- package/dist/presentation/components/use_smile_id_document_overlay.js +80 -40
- package/dist/presentation/components/use_smile_id_document_overlay.js.map +1 -1
- package/dist/presentation/components/use_smile_id_document_shimmer.d.ts +15 -0
- package/dist/presentation/components/use_smile_id_document_shimmer.d.ts.map +1 -0
- package/dist/presentation/components/use_smile_id_document_shimmer.js +23 -0
- package/dist/presentation/components/use_smile_id_document_shimmer.js.map +1 -0
- package/dist/presentation/components/use_smile_id_enhanced_smart_selfie_clip.d.ts.map +1 -1
- package/dist/presentation/components/use_smile_id_enhanced_smart_selfie_clip.js +1 -3
- package/dist/presentation/components/use_smile_id_enhanced_smart_selfie_clip.js.map +1 -1
- package/dist/presentation/components/use_smile_id_guideline_thumbnail.d.ts.map +1 -1
- package/dist/presentation/components/use_smile_id_guideline_thumbnail.js +84 -1
- package/dist/presentation/components/use_smile_id_guideline_thumbnail.js.map +1 -1
- package/dist/presentation/components/use_smile_id_instructions_hero.d.ts +3 -1
- package/dist/presentation/components/use_smile_id_instructions_hero.d.ts.map +1 -1
- package/dist/presentation/components/use_smile_id_instructions_hero.js +18 -3
- package/dist/presentation/components/use_smile_id_instructions_hero.js.map +1 -1
- package/dist/presentation/components/use_smile_id_oval_border_palette.d.ts.map +1 -1
- package/dist/presentation/components/use_smile_id_oval_border_palette.js +7 -3
- package/dist/presentation/components/use_smile_id_oval_border_palette.js.map +1 -1
- package/dist/presentation/flow/dsl/builder/config_builder.d.ts +12 -0
- package/dist/presentation/flow/dsl/builder/config_builder.d.ts.map +1 -1
- package/dist/presentation/flow/dsl/builder/config_builder.js +12 -0
- package/dist/presentation/flow/dsl/builder/config_builder.js.map +1 -1
- package/dist/presentation/flow/dsl/builder/ml_builder.d.ts +13 -4
- package/dist/presentation/flow/dsl/builder/ml_builder.d.ts.map +1 -1
- package/dist/presentation/flow/dsl/builder/ml_builder.js +3 -2
- package/dist/presentation/flow/dsl/builder/ml_builder.js.map +1 -1
- package/dist/presentation/flow/dsl/builder/screens_builder.d.ts +111 -10
- package/dist/presentation/flow/dsl/builder/screens_builder.d.ts.map +1 -1
- package/dist/presentation/flow/dsl/builder/screens_builder.js +111 -10
- package/dist/presentation/flow/dsl/builder/screens_builder.js.map +1 -1
- package/dist/presentation/flow/dsl/builder/theme_builder.d.ts +15 -2
- package/dist/presentation/flow/dsl/builder/theme_builder.d.ts.map +1 -1
- package/dist/presentation/flow/dsl/builder/theme_builder.js +15 -2
- package/dist/presentation/flow/dsl/builder/theme_builder.js.map +1 -1
- package/dist/presentation/flow/dsl/use_smile_id_builder.d.ts +6 -4
- package/dist/presentation/flow/dsl/use_smile_id_builder.d.ts.map +1 -1
- package/dist/presentation/flow/dsl/use_smile_id_builder.js.map +1 -1
- package/dist/presentation/flow/dsl/use_smile_id_flow_builder.d.ts +62 -36
- package/dist/presentation/flow/dsl/use_smile_id_flow_builder.d.ts.map +1 -1
- package/dist/presentation/flow/dsl/use_smile_id_flow_builder.js +62 -36
- package/dist/presentation/flow/dsl/use_smile_id_flow_builder.js.map +1 -1
- package/dist/presentation/flow/flow_navigation_manager.d.ts +39 -4
- package/dist/presentation/flow/flow_navigation_manager.d.ts.map +1 -1
- package/dist/presentation/flow/flow_navigation_manager.js +141 -10
- package/dist/presentation/flow/flow_navigation_manager.js.map +1 -1
- package/dist/presentation/flow/model/instruction_guideline.d.ts +9 -0
- package/dist/presentation/flow/model/instruction_guideline.d.ts.map +1 -1
- package/dist/presentation/flow/model/instructions_defaults.d.ts +11 -2
- package/dist/presentation/flow/model/instructions_defaults.d.ts.map +1 -1
- package/dist/presentation/flow/model/instructions_defaults.js +73 -25
- package/dist/presentation/flow/model/instructions_defaults.js.map +1 -1
- package/dist/presentation/flow/screen/orchestrated_capture_screen.d.ts.map +1 -1
- package/dist/presentation/flow/screen/orchestrated_capture_screen.js +36 -12
- package/dist/presentation/flow/screen/orchestrated_capture_screen.js.map +1 -1
- package/dist/presentation/flow/screen/orchestrated_instructions_screen.js +1 -1
- package/dist/presentation/flow/screen/orchestrated_instructions_screen.js.map +1 -1
- package/dist/presentation/flow/screen/orchestrated_preview_screen.d.ts.map +1 -1
- package/dist/presentation/flow/screen/orchestrated_preview_screen.js +3 -1
- package/dist/presentation/flow/screen/orchestrated_preview_screen.js.map +1 -1
- package/dist/presentation/flow/screen/orchestrated_processing_screen.js +3 -3
- package/dist/presentation/flow/screen/orchestrated_processing_screen.js.map +1 -1
- package/dist/presentation/flow/validation/job_type_validator.d.ts.map +1 -1
- package/dist/presentation/flow/validation/job_type_validator.js +14 -0
- package/dist/presentation/flow/validation/job_type_validator.js.map +1 -1
- package/dist/presentation/screen/helpers/capture_screen_helpers.d.ts.map +1 -1
- package/dist/presentation/screen/helpers/capture_screen_helpers.js +1 -0
- package/dist/presentation/screen/helpers/capture_screen_helpers.js.map +1 -1
- package/dist/presentation/screen/helpers/instructions_screen_helpers.d.ts +1 -0
- package/dist/presentation/screen/helpers/instructions_screen_helpers.d.ts.map +1 -1
- package/dist/presentation/screen/helpers/instructions_screen_helpers.js +6 -2
- package/dist/presentation/screen/helpers/instructions_screen_helpers.js.map +1 -1
- package/dist/presentation/screen/helpers/scan_state_to_overlay_state.js +3 -0
- package/dist/presentation/screen/helpers/scan_state_to_overlay_state.js.map +1 -1
- package/dist/presentation/screen/use_smile_id_capture_screen.d.ts +17 -1
- package/dist/presentation/screen/use_smile_id_capture_screen.d.ts.map +1 -1
- package/dist/presentation/screen/use_smile_id_capture_screen.js +153 -18
- package/dist/presentation/screen/use_smile_id_capture_screen.js.map +1 -1
- package/dist/presentation/screen/use_smile_id_document_preview_screen.d.ts +2 -3
- package/dist/presentation/screen/use_smile_id_document_preview_screen.d.ts.map +1 -1
- package/dist/presentation/screen/use_smile_id_document_preview_screen.js +16 -11
- package/dist/presentation/screen/use_smile_id_document_preview_screen.js.map +1 -1
- package/dist/presentation/screen/use_smile_id_instructions_screen.d.ts +2 -0
- package/dist/presentation/screen/use_smile_id_instructions_screen.d.ts.map +1 -1
- package/dist/presentation/screen/use_smile_id_instructions_screen.js +2 -1
- package/dist/presentation/screen/use_smile_id_instructions_screen.js.map +1 -1
- package/dist/presentation/screen/use_smile_id_preview_screen.d.ts +7 -6
- package/dist/presentation/screen/use_smile_id_preview_screen.d.ts.map +1 -1
- package/dist/presentation/screen/use_smile_id_preview_screen.js +9 -8
- package/dist/presentation/screen/use_smile_id_preview_screen.js.map +1 -1
- package/dist/presentation/screen/use_smile_id_processing_screen.d.ts +2 -3
- package/dist/presentation/screen/use_smile_id_processing_screen.d.ts.map +1 -1
- package/dist/presentation/screen/use_smile_id_processing_screen.js +3 -2
- package/dist/presentation/screen/use_smile_id_processing_screen.js.map +1 -1
- package/dist/presentation/theme/theme.d.ts +1 -1
- package/dist/presentation/theme/theme.d.ts.map +1 -1
- package/dist/presentation/theme/theme.js +20 -18
- package/dist/presentation/theme/theme.js.map +1 -1
- package/dist/presentation/utils/force_max_brightness.d.ts +4 -4
- package/package.json +26 -6
- package/src/__tests__/react_native_mock.tsx +8 -0
- package/src/camera/camera_defaults.ts +11 -9
- package/src/camera/orientation_degrees.ts +5 -6
- package/src/core/models/__tests__/document_type.test.ts +6 -4
- package/src/core/models/document_capture_mode.ts +27 -0
- package/src/core/models/document_type.ts +6 -4
- package/src/core/models/result.ts +9 -8
- package/src/data/api/interceptors/__tests__/smile_integrity_interceptor.test.ts +37 -1
- package/src/data/api/interceptors/__tests__/smile_request_mac_interceptor.test.ts +59 -1
- package/src/data/api/interceptors/smile_auth_interceptor.ts +1 -1
- package/src/data/api/interceptors/smile_integrity_interceptor.ts +20 -0
- package/src/data/api/interceptors/smile_request_mac_interceptor.ts +13 -0
- package/src/data/compression/__tests__/payload_compressor.test.ts +200 -0
- package/src/data/compression/__tests__/payload_compressor_real_jpeg.test.ts +222 -0
- package/src/data/compression/compressible_asset.ts +62 -0
- package/src/data/compression/payload_compression_config.ts +53 -0
- package/src/data/compression/payload_compression_runner.ts +102 -0
- package/src/data/compression/payload_compressor.ts +164 -0
- package/src/data/compression/uri_compressible_asset.ts +85 -0
- package/src/data/dsl/builder/cache_config_builder.ts +3 -1
- package/src/data/dsl/builder/config_builder.ts +45 -14
- package/src/data/dsl/builder/interceptor_config_builder.ts +9 -7
- package/src/data/dsl/builder/retry_config_builder.ts +22 -1
- package/src/data/dsl/builder/timeout_config_builder.ts +7 -0
- package/src/data/dsl/use_smile_id_network_builder.ts +17 -0
- package/src/data/factory/networking_factory.ts +14 -1
- package/src/data/repository/__tests__/network_retries.test.ts +117 -0
- package/src/data/repository/use_smile_id_repository_impl.ts +96 -9
- package/src/data/security/__tests__/security_policy.test.ts +96 -0
- package/src/data/security/__tests__/smile_id_crypto_manager.test.ts +18 -0
- package/src/data/security/request_canonicalizer.ts +15 -2
- package/src/data/security/security_policy.ts +70 -0
- package/src/data/util/__tests__/use_smile_id_token_decoder.test.ts +57 -1
- package/src/data/util/retry_util.ts +3 -2
- package/src/data/util/use_smile_id_token_decoder.ts +76 -28
- package/src/l10n/strings_en.ts +2 -1
- package/src/l10n/use_smile_id_localizations.ts +3 -0
- package/src/metadata/__tests__/device_orientation_collector.test.ts +15 -0
- package/src/metadata/__tests__/metadata_configuration_mappers.test.ts +215 -0
- package/src/metadata/__tests__/metadata_configuration_objects.test.ts +85 -0
- package/src/metadata/__tests__/metadata_device_context.test.ts +147 -0
- package/src/metadata/__tests__/metadata_golden_fixture.json +13 -0
- package/src/metadata/__tests__/metadata_payload_golden.test.ts +193 -0
- package/src/metadata/__tests__/use_smile_id_metadata.test.ts +18 -24
- package/src/metadata/builder_configuration.ts +63 -0
- package/src/metadata/captured_image_properties.ts +10 -0
- package/src/metadata/device_orientation_collector.ts +75 -0
- package/src/metadata/geolocation.ts +46 -0
- package/src/metadata/metadata_configuration_mappers.ts +234 -0
- package/src/metadata/model_configuration.ts +68 -0
- package/src/metadata/optional_native_modules.ts +157 -3
- package/src/metadata/use_smile_id_metadata.ts +140 -17
- package/src/metadata/use_smile_id_metadata_factory.ts +195 -11
- package/src/presentation/assets/si_ess_animation_all_directions.json +1300 -286
- package/src/presentation/assets/svg/document_shimmer_strings.ts +17 -0
- package/src/presentation/assets/svg/instruction_svg_strings.ts +34 -12
- package/src/presentation/components/__tests__/enhanced_smart_selfie_animation_frames.test.ts +1 -3
- package/src/presentation/components/__tests__/use_smile_id_enhanced_smart_selfie_clip.test.tsx +3 -4
- package/src/presentation/components/__tests__/use_smile_id_guideline_thumbnail.test.tsx +34 -1
- package/src/presentation/components/__tests__/use_smile_id_instructions_hero.test.tsx +27 -0
- package/src/presentation/components/__tests__/use_smile_id_oval_border_palette.test.ts +7 -5
- package/src/presentation/components/helpers/enhanced_smart_selfie_animation_frames.ts +0 -6
- package/src/presentation/components/icons/index.tsx +65 -18
- package/src/presentation/components/use_smile_id_attribution.tsx +1 -1
- package/src/presentation/components/use_smile_id_button.tsx +4 -4
- package/src/presentation/components/use_smile_id_capture_content.tsx +36 -12
- package/src/presentation/components/use_smile_id_captured_document_image.tsx +3 -8
- package/src/presentation/components/use_smile_id_document_capture_controls.tsx +103 -39
- package/src/presentation/components/use_smile_id_document_overlay.tsx +134 -44
- package/src/presentation/components/use_smile_id_document_shimmer.tsx +51 -0
- package/src/presentation/components/use_smile_id_enhanced_smart_selfie_clip.tsx +0 -3
- package/src/presentation/components/use_smile_id_guideline_thumbnail.tsx +131 -2
- package/src/presentation/components/use_smile_id_instructions_hero.tsx +100 -59
- package/src/presentation/components/use_smile_id_oval_border_palette.ts +7 -3
- package/src/presentation/flow/__tests__/document_flow_navigation.test.ts +200 -1
- package/src/presentation/flow/dsl/builder/config_builder.ts +14 -0
- package/src/presentation/flow/dsl/builder/ml_builder.ts +13 -4
- package/src/presentation/flow/dsl/builder/screens_builder.ts +111 -10
- package/src/presentation/flow/dsl/builder/theme_builder.ts +19 -2
- package/src/presentation/flow/dsl/use_smile_id_builder.tsx +6 -4
- package/src/presentation/flow/dsl/use_smile_id_flow_builder.ts +62 -36
- package/src/presentation/flow/flow_navigation_manager.ts +179 -13
- package/src/presentation/flow/model/instruction_guideline.ts +6 -0
- package/src/presentation/flow/model/instructions_defaults.ts +103 -25
- package/src/presentation/flow/screen/orchestrated_capture_screen.tsx +43 -10
- package/src/presentation/flow/screen/orchestrated_instructions_screen.tsx +1 -0
- package/src/presentation/flow/screen/orchestrated_preview_screen.tsx +4 -2
- package/src/presentation/flow/screen/orchestrated_processing_screen.tsx +3 -3
- package/src/presentation/flow/validation/__tests__/job_type_validator.test.ts +64 -0
- package/src/presentation/flow/validation/job_type_validator.ts +24 -0
- package/src/presentation/screen/__tests__/instructions_screen_helpers.test.ts +15 -3
- package/src/presentation/screen/__tests__/use_smile_id_capture_screen.test.tsx +6 -4
- package/src/presentation/screen/helpers/capture_screen_helpers.ts +1 -0
- package/src/presentation/screen/helpers/instructions_screen_helpers.ts +7 -2
- package/src/presentation/screen/helpers/scan_state_to_overlay_state.ts +3 -0
- package/src/presentation/screen/use_smile_id_capture_screen.tsx +234 -36
- package/src/presentation/screen/use_smile_id_document_preview_screen.tsx +32 -22
- package/src/presentation/screen/use_smile_id_instructions_screen.tsx +4 -0
- package/src/presentation/screen/use_smile_id_preview_screen.tsx +9 -9
- package/src/presentation/screen/use_smile_id_processing_screen.tsx +7 -7
- package/src/presentation/theme/theme.ts +21 -19
- package/src/presentation/utils/force_max_brightness.tsx +4 -4
- package/dist/camera/frame_processor_plugin.d.ts +0 -28
- package/dist/camera/frame_processor_plugin.d.ts.map +0 -1
- package/dist/camera/frame_processor_plugin.js +0 -21
- package/dist/camera/frame_processor_plugin.js.map +0 -1
- package/dist/camera/use_smile_id_frame_processor.d.ts +0 -38
- package/dist/camera/use_smile_id_frame_processor.d.ts.map +0 -1
- package/dist/camera/use_smile_id_frame_processor.js +0 -2
- package/dist/camera/use_smile_id_frame_processor.js.map +0 -1
- package/dist/presentation/components/use_smile_id_document_frame_glyph.d.ts +0 -17
- package/dist/presentation/components/use_smile_id_document_frame_glyph.d.ts.map +0 -1
- package/dist/presentation/components/use_smile_id_document_frame_glyph.js +0 -17
- package/dist/presentation/components/use_smile_id_document_frame_glyph.js.map +0 -1
- package/dist/presentation/components/use_smile_id_enhanced_smart_selfie_animation.d.ts +0 -24
- package/dist/presentation/components/use_smile_id_enhanced_smart_selfie_animation.d.ts.map +0 -1
- package/dist/presentation/components/use_smile_id_enhanced_smart_selfie_animation.js +0 -91
- package/dist/presentation/components/use_smile_id_enhanced_smart_selfie_animation.js.map +0 -1
- package/dist/presentation/components/use_smile_id_instruction_card.d.ts +0 -31
- package/dist/presentation/components/use_smile_id_instruction_card.d.ts.map +0 -1
- package/dist/presentation/components/use_smile_id_instruction_card.js +0 -43
- package/dist/presentation/components/use_smile_id_instruction_card.js.map +0 -1
- package/dist/presentation/screen/helpers/use_document_orientation_lock.d.ts +0 -24
- package/dist/presentation/screen/helpers/use_document_orientation_lock.d.ts.map +0 -1
- package/dist/presentation/screen/helpers/use_document_orientation_lock.js +0 -47
- package/dist/presentation/screen/helpers/use_document_orientation_lock.js.map +0 -1
- package/src/presentation/components/use_smile_id_document_frame_glyph.tsx +0 -162
|
@@ -99,6 +99,14 @@ export const I18nManager = { isRTL: false };
|
|
|
99
99
|
|
|
100
100
|
export const useColorScheme = (): "light" | "dark" | null => "light";
|
|
101
101
|
|
|
102
|
+
// Portrait window by default — the capture screen reads this for its
|
|
103
|
+
// iOS preview-remount key (`cameraOrientationKey`); tests don't assert on
|
|
104
|
+
// the key, so a stable portrait size just lets the component render.
|
|
105
|
+
export const useWindowDimensions = (): { width: number; height: number } => ({
|
|
106
|
+
width: 390,
|
|
107
|
+
height: 844,
|
|
108
|
+
});
|
|
109
|
+
|
|
102
110
|
export const Platform = {
|
|
103
111
|
OS: "ios" as const,
|
|
104
112
|
select<T>(spec: { ios?: T; android?: T; default?: T }): T | undefined {
|
|
@@ -1,23 +1,25 @@
|
|
|
1
1
|
/** Static camera tuning defaults. */
|
|
2
2
|
export const CameraDefaults = {
|
|
3
|
-
/** Focus running indicator visibility duration
|
|
3
|
+
/** Focus running indicator visibility duration (ms). */
|
|
4
4
|
focusRunningDurationMs: 600,
|
|
5
|
-
/** Focus settled indicator visibility duration
|
|
5
|
+
/** Focus settled indicator visibility duration (ms). */
|
|
6
6
|
focusSettledDurationMs: 500,
|
|
7
7
|
|
|
8
|
-
/** Pinned analysis-stream frame rate
|
|
8
|
+
/** Pinned analysis-stream frame rate; 30 fps keeps motion smooth at low ISO. */
|
|
9
9
|
analysisTargetFps: 30,
|
|
10
|
-
/**
|
|
10
|
+
/** Selfie analysis resolution (4:3 — the face/smile heuristics are calibrated on it). */
|
|
11
11
|
analysisTargetResolution: { width: 640, height: 480 } as const,
|
|
12
|
+
/** Document analysis resolution (16:9, matched to the visible preview so the guide-rect crop stays aligned). */
|
|
13
|
+
documentAnalysisResolution: { width: 1280, height: 720 } as const,
|
|
12
14
|
|
|
13
|
-
/** Longest-edge ceiling (px) for an imported document image; downscale-only
|
|
15
|
+
/** Longest-edge ceiling (px) for an imported document image; downscale-only. */
|
|
14
16
|
documentMaxDimension: 1600,
|
|
15
|
-
/** JPEG
|
|
17
|
+
/** JPEG quality (0–1) for imported document images. */
|
|
16
18
|
documentJpegQuality: 0.85,
|
|
17
|
-
/** Minimum shortest-edge (px) for a gallery document image; smaller is rejected
|
|
19
|
+
/** Minimum shortest-edge (px) for a gallery document image; smaller is rejected. */
|
|
18
20
|
minGalleryDocumentDimensionPx: 600,
|
|
19
|
-
/** Default selfie JPEG width (px)
|
|
21
|
+
/** Default selfie JPEG width (px); also the `image_width` metadata fallback. */
|
|
20
22
|
selfieJpegTargetWidth: 480,
|
|
21
|
-
/** Default selfie JPEG height (px)
|
|
23
|
+
/** Default selfie JPEG height (px); also the `image_height` metadata fallback. */
|
|
22
24
|
selfieJpegTargetHeight: 640,
|
|
23
25
|
} as const;
|
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
import type { Orientation } from "react-native-vision-camera";
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* Map VisionCamera
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* `
|
|
8
|
-
*
|
|
9
|
-
* not how the user is holding the phone.
|
|
4
|
+
* Map a VisionCamera {@link Orientation} to clockwise degrees (`0` = portrait).
|
|
5
|
+
* Feeds two callers: `onOutputOrientationChanged` (gravity tilt → the upright
|
|
6
|
+
* validator) and `onPreviewOrientationChanged` (locked viewport axis → the
|
|
7
|
+
* document `viewportOrientation` the frame processor uprights the capture to).
|
|
8
|
+
* Never use `frame.orientation` — that is the fixed sensor mounting.
|
|
10
9
|
*/
|
|
11
10
|
export function orientationToDegrees(orientation: Orientation): number {
|
|
12
11
|
switch (orientation) {
|
|
@@ -5,21 +5,23 @@ import { DocumentType } from "../document_type";
|
|
|
5
5
|
// / orientation drive both copy and capture behaviour). Pin them exactly so a
|
|
6
6
|
// change forces a deliberate decision here and in the sibling SDKs.
|
|
7
7
|
describe("DocumentType presets", () => {
|
|
8
|
-
it("SouthAfricaGreenBook is a portrait,
|
|
8
|
+
it("SouthAfricaGreenBook is a portrait, single-sided document", () => {
|
|
9
9
|
expect(DocumentType.SouthAfricaGreenBook).toEqual({
|
|
10
10
|
kind: "southAfricaGreenBook",
|
|
11
11
|
displayName: "Green Book",
|
|
12
|
-
hasBackSide:
|
|
12
|
+
hasBackSide: false,
|
|
13
13
|
orientation: "Portrait",
|
|
14
|
+
knownAspectRatio: 320 / 428,
|
|
14
15
|
});
|
|
15
16
|
});
|
|
16
17
|
|
|
17
|
-
it("Passport is a
|
|
18
|
+
it("Passport is a landscape, single-sided document", () => {
|
|
18
19
|
expect(DocumentType.Passport).toEqual({
|
|
19
20
|
kind: "passport",
|
|
20
21
|
displayName: "Passport",
|
|
21
22
|
hasBackSide: false,
|
|
22
|
-
orientation: "
|
|
23
|
+
orientation: "Landscape",
|
|
24
|
+
knownAspectRatio: 356 / 272,
|
|
23
25
|
});
|
|
24
26
|
});
|
|
25
27
|
|
|
@@ -44,3 +44,30 @@ export const DocumentCaptureMode = {
|
|
|
44
44
|
});
|
|
45
45
|
},
|
|
46
46
|
} as const;
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* The wire token the server expects for the `auto_capture_enabled` builder-configuration field,
|
|
50
|
+
* keyed off the mode's `kind`. Lives with {@link DocumentCaptureMode} — its single source of
|
|
51
|
+
* truth — so the metadata layer maps the partner's typed choice straight onto the wire without a
|
|
52
|
+
* parallel enum. These exact strings are the server contract; changing them is a wire-breaking
|
|
53
|
+
* change. Not part of the public package surface (not re-exported from `index.ts`).
|
|
54
|
+
*/
|
|
55
|
+
export function autoCaptureWireValue(
|
|
56
|
+
mode: DocumentCaptureMode | undefined,
|
|
57
|
+
): string {
|
|
58
|
+
switch (mode?.kind) {
|
|
59
|
+
case "autoCapture":
|
|
60
|
+
return "auto_capture_only";
|
|
61
|
+
case "manualCapture":
|
|
62
|
+
return "manual";
|
|
63
|
+
case "autoCaptureWithManualFallback":
|
|
64
|
+
case undefined:
|
|
65
|
+
return "autocapture_default";
|
|
66
|
+
default: {
|
|
67
|
+
// Exhaustiveness guard: a new DocumentCaptureMode kind fails to compile here
|
|
68
|
+
// until it's handled above (`mode` narrows to `never` once all kinds are covered).
|
|
69
|
+
const exhaustive: never = mode;
|
|
70
|
+
return exhaustive;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
@@ -26,15 +26,17 @@ export type DocumentType =
|
|
|
26
26
|
const SOUTH_AFRICA_GREEN_BOOK: DocumentType = Object.freeze({
|
|
27
27
|
kind: "southAfricaGreenBook",
|
|
28
28
|
displayName: "Green Book",
|
|
29
|
-
hasBackSide:
|
|
29
|
+
hasBackSide: false,
|
|
30
30
|
orientation: "Portrait",
|
|
31
|
+
knownAspectRatio: 320 / 428,
|
|
31
32
|
});
|
|
32
33
|
|
|
33
34
|
const PASSPORT: DocumentType = Object.freeze({
|
|
34
35
|
kind: "passport",
|
|
35
36
|
displayName: "Passport",
|
|
36
37
|
hasBackSide: false,
|
|
37
|
-
orientation: "
|
|
38
|
+
orientation: "Landscape",
|
|
39
|
+
knownAspectRatio: 356 / 272,
|
|
38
40
|
});
|
|
39
41
|
|
|
40
42
|
/** Configurable fields for {@link DocumentType.GenericDocument}. */
|
|
@@ -47,9 +49,9 @@ export type GenericDocumentArgs = {
|
|
|
47
49
|
|
|
48
50
|
/** Preset values and the `GenericDocument` factory for {@link DocumentType}. */
|
|
49
51
|
export const DocumentType = {
|
|
50
|
-
/** South Africa Green Book — booklet-style,
|
|
52
|
+
/** South Africa Green Book — booklet-style, single-side, portrait. */
|
|
51
53
|
SouthAfricaGreenBook: SOUTH_AFRICA_GREEN_BOOK,
|
|
52
|
-
/** Passport — single data page, no back side,
|
|
54
|
+
/** Passport — single data page, no back side, landscape. */
|
|
53
55
|
Passport: PASSPORT,
|
|
54
56
|
/** Catch-all without a preset; defaults to a landscape, two-sided "Document". */
|
|
55
57
|
GenericDocument(args: GenericDocumentArgs = {}): DocumentType {
|
|
@@ -1,31 +1,32 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* The outcome of an SDK operation: success with a value, failure with an
|
|
3
|
+
* error, or cancellation when the user exits the flow. Surfaced to
|
|
4
|
+
* partners through `onResult`.
|
|
3
5
|
*
|
|
4
6
|
* Partners never need to unwrap a `UseSmileIDResult` directly — all
|
|
5
7
|
* errors are automatically surfaced through the `onResult` callback.
|
|
6
8
|
* The discriminated union lets partner code `switch (result.status)`
|
|
7
|
-
* and exhaust
|
|
9
|
+
* and exhaust the branches without try/catch ceremony.
|
|
8
10
|
*/
|
|
9
11
|
export type UseSmileIDResult<T> =
|
|
10
12
|
| { readonly status: "success"; readonly value: T }
|
|
11
13
|
| { readonly status: "failure"; readonly error: unknown }
|
|
12
14
|
| { readonly status: "cancelled" };
|
|
13
15
|
|
|
14
|
-
/**
|
|
16
|
+
/** A successful outcome carrying the resulting value. */
|
|
15
17
|
export function success<T>(value: T): UseSmileIDResult<T> {
|
|
16
18
|
return { status: "success", value };
|
|
17
19
|
}
|
|
18
20
|
|
|
19
|
-
/**
|
|
21
|
+
/** A failed outcome carrying the causing error. */
|
|
20
22
|
export function failure<T>(error: unknown): UseSmileIDResult<T> {
|
|
21
23
|
return { status: "failure", error };
|
|
22
24
|
}
|
|
23
25
|
|
|
24
26
|
/**
|
|
25
|
-
*
|
|
26
|
-
* (e.g. back-pressed out of the first screen)
|
|
27
|
-
*
|
|
28
|
-
* cancellation apart from an error.
|
|
27
|
+
* The user deliberately exited the flow without completing or failing it
|
|
28
|
+
* (e.g. back-pressed out of the first screen) — distinct from a failure
|
|
29
|
+
* so cancellation can be handled separately.
|
|
29
30
|
*/
|
|
30
31
|
export function cancelled<T>(): UseSmileIDResult<T> {
|
|
31
32
|
return { status: "cancelled" };
|
|
@@ -20,6 +20,7 @@ import {
|
|
|
20
20
|
ATTESTATION_OPT_IN_KEY,
|
|
21
21
|
} from "../smile_integrity_interceptor";
|
|
22
22
|
import { REQUEST_MAC_HEADER } from "../smile_request_mac_interceptor";
|
|
23
|
+
import { SecurityPolicy } from "../../../security/security_policy";
|
|
23
24
|
|
|
24
25
|
/**
|
|
25
26
|
* Pins down the integrity interceptor's wire shape:
|
|
@@ -51,7 +52,10 @@ describe("SmileIntegrityInterceptor", () => {
|
|
|
51
52
|
|
|
52
53
|
beforeEach(() => {
|
|
53
54
|
client = axios.create();
|
|
54
|
-
|
|
55
|
+
// undefined bit-code is fail-secure: the attestation check is active, matching the pre-policy default.
|
|
56
|
+
interceptor = new SmileIntegrityInterceptor({
|
|
57
|
+
securityPolicy: SecurityPolicy.fromBitCode(undefined),
|
|
58
|
+
});
|
|
55
59
|
interceptor.attach(client);
|
|
56
60
|
warmUpAttestation = vi.fn();
|
|
57
61
|
requestAttestationToken = vi.fn();
|
|
@@ -153,4 +157,36 @@ describe("SmileIntegrityInterceptor", () => {
|
|
|
153
157
|
expect(ex.message).toBe("hello world");
|
|
154
158
|
expect(encodeURIComponent(ex.message)).toBe("hello%20world");
|
|
155
159
|
});
|
|
160
|
+
|
|
161
|
+
it("passes through without a nonce when policy disables attestation", async () => {
|
|
162
|
+
// bit-code 1: only payload signing (bit 0) set; the attestation bit (3) is off, so the token
|
|
163
|
+
// must not be requested even though the MAC header is present.
|
|
164
|
+
const localClient = axios.create();
|
|
165
|
+
new SmileIntegrityInterceptor({
|
|
166
|
+
securityPolicy: SecurityPolicy.fromBitCode(1),
|
|
167
|
+
}).attach(localClient);
|
|
168
|
+
const config = makeConfig({
|
|
169
|
+
[ATTESTATION_OPT_IN_KEY]: true,
|
|
170
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
171
|
+
} as any);
|
|
172
|
+
config.headers.set(REQUEST_MAC_HEADER, "mac-abc");
|
|
173
|
+
|
|
174
|
+
const handlers = (
|
|
175
|
+
localClient.interceptors.request as unknown as {
|
|
176
|
+
handlers: {
|
|
177
|
+
fulfilled?: (cfg: InternalAxiosRequestConfig) => unknown;
|
|
178
|
+
}[];
|
|
179
|
+
}
|
|
180
|
+
).handlers;
|
|
181
|
+
let current = config;
|
|
182
|
+
for (let i = handlers.length - 1; i >= 0; i--) {
|
|
183
|
+
const handler = handlers[i]?.fulfilled;
|
|
184
|
+
if (typeof handler === "function") {
|
|
185
|
+
current = (await handler(current)) as InternalAxiosRequestConfig;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
expect(current.headers.get(DEVICE_NONCE_HEADER)).toBeUndefined();
|
|
190
|
+
expect(requestAttestationToken).not.toHaveBeenCalled();
|
|
191
|
+
});
|
|
156
192
|
});
|
|
@@ -2,6 +2,7 @@ import { describe, expect, it, beforeEach } from "vitest";
|
|
|
2
2
|
import axios, { AxiosHeaders, type InternalAxiosRequestConfig } from "axios";
|
|
3
3
|
|
|
4
4
|
import { SmileIDCryptoManager } from "../../../security/smile_id_crypto_manager";
|
|
5
|
+
import { SecurityPolicy } from "../../../security/security_policy";
|
|
5
6
|
import {
|
|
6
7
|
SmileRequestMacInterceptor,
|
|
7
8
|
REQUEST_MAC_HEADER,
|
|
@@ -26,7 +27,11 @@ describe("SmileRequestMacInterceptor", () => {
|
|
|
26
27
|
|
|
27
28
|
beforeEach(() => {
|
|
28
29
|
client = axios.create();
|
|
29
|
-
|
|
30
|
+
// undefined bit-code is fail-secure: payload signing is active, matching the pre-policy default.
|
|
31
|
+
interceptor = new SmileRequestMacInterceptor({
|
|
32
|
+
cryptoManager,
|
|
33
|
+
securityPolicy: SecurityPolicy.fromBitCode(undefined),
|
|
34
|
+
});
|
|
30
35
|
interceptor.attach(client);
|
|
31
36
|
});
|
|
32
37
|
|
|
@@ -95,4 +100,57 @@ describe("SmileRequestMacInterceptor", () => {
|
|
|
95
100
|
);
|
|
96
101
|
expect(result.headers.get(REQUEST_MAC_HEADER)).toBeUndefined();
|
|
97
102
|
});
|
|
103
|
+
|
|
104
|
+
/** Drive a one-interceptor client built with a specific policy. */
|
|
105
|
+
async function runWith(
|
|
106
|
+
policy: SecurityPolicy,
|
|
107
|
+
config: InternalAxiosRequestConfig,
|
|
108
|
+
): Promise<InternalAxiosRequestConfig> {
|
|
109
|
+
const localClient = axios.create();
|
|
110
|
+
new SmileRequestMacInterceptor({
|
|
111
|
+
cryptoManager,
|
|
112
|
+
securityPolicy: policy,
|
|
113
|
+
}).attach(localClient);
|
|
114
|
+
const handlers = (
|
|
115
|
+
localClient.interceptors.request as unknown as {
|
|
116
|
+
handlers: {
|
|
117
|
+
fulfilled?: (cfg: InternalAxiosRequestConfig) => unknown;
|
|
118
|
+
}[];
|
|
119
|
+
}
|
|
120
|
+
).handlers;
|
|
121
|
+
let current = config;
|
|
122
|
+
for (let i = handlers.length - 1; i >= 0; i--) {
|
|
123
|
+
const handler = handlers[i]?.fulfilled;
|
|
124
|
+
if (typeof handler === "function") {
|
|
125
|
+
current = (await handler(current)) as InternalAxiosRequestConfig;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
return current;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
it("leaves the request unsigned when the policy disables payload signing", async () => {
|
|
132
|
+
// bit-code 0: no bits set, so payload signing is off for this session.
|
|
133
|
+
const result = await runWith(
|
|
134
|
+
SecurityPolicy.fromBitCode(0),
|
|
135
|
+
makeConfig({
|
|
136
|
+
data: '{"hello":"world"}',
|
|
137
|
+
headers: new AxiosHeaders({ [REQUEST_TIMESTAMP_HEADER]: TIMESTAMP }),
|
|
138
|
+
[REQUEST_MAC_OPT_IN_KEY]: true,
|
|
139
|
+
} as Partial<InternalAxiosRequestConfig>),
|
|
140
|
+
);
|
|
141
|
+
expect(result.headers.get(REQUEST_MAC_HEADER)).toBeUndefined();
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
it("signs when the policy enables only payload signing", async () => {
|
|
145
|
+
// bit-code 1: bit 0 (payload signing) set; other behaviours off.
|
|
146
|
+
const result = await runWith(
|
|
147
|
+
SecurityPolicy.fromBitCode(1),
|
|
148
|
+
makeConfig({
|
|
149
|
+
data: '{"hello":"world"}',
|
|
150
|
+
headers: new AxiosHeaders({ [REQUEST_TIMESTAMP_HEADER]: TIMESTAMP }),
|
|
151
|
+
[REQUEST_MAC_OPT_IN_KEY]: true,
|
|
152
|
+
} as Partial<InternalAxiosRequestConfig>),
|
|
153
|
+
);
|
|
154
|
+
expect(result.headers.get(REQUEST_MAC_HEADER)).toBeDefined();
|
|
155
|
+
});
|
|
98
156
|
});
|
|
@@ -26,7 +26,7 @@ import type { TokenHolder } from "../auth/token_holder";
|
|
|
26
26
|
* by the next outgoing request.
|
|
27
27
|
*/
|
|
28
28
|
|
|
29
|
-
const SDK_VERSION = "12.0.0
|
|
29
|
+
const SDK_VERSION = "12.0.0";
|
|
30
30
|
|
|
31
31
|
/**
|
|
32
32
|
* Source-SDK identifier, suffixed with the runtime platform so the server can
|
|
@@ -2,6 +2,7 @@ import type { AxiosInstance, InternalAxiosRequestConfig } from "axios";
|
|
|
2
2
|
|
|
3
3
|
import { Attestation, AttestationException } from "@smileid/usesmileid_bridge";
|
|
4
4
|
|
|
5
|
+
import type { SecurityPolicy } from "../../security/security_policy";
|
|
5
6
|
import { REQUEST_MAC_HEADER } from "./smile_request_mac_interceptor";
|
|
6
7
|
|
|
7
8
|
/**
|
|
@@ -31,6 +32,11 @@ import { REQUEST_MAC_HEADER } from "./smile_request_mac_interceptor";
|
|
|
31
32
|
* Warm-up lives outside the interceptor. Call `Attestation.warmUp()`
|
|
32
33
|
* once at flow mount so the per-request `requestToken` call doesn't
|
|
33
34
|
* pay the multi-second warm-up cost on every submission.
|
|
35
|
+
*
|
|
36
|
+
* Attestation is additionally gated by the server-driven {@link SecurityPolicy}:
|
|
37
|
+
* when the session's policy disables the device-attestation check, an opted-in
|
|
38
|
+
* request ships without a device nonce. The policy is fail-secure, so a token
|
|
39
|
+
* that omits the claim attests as before.
|
|
34
40
|
*/
|
|
35
41
|
|
|
36
42
|
export const DEVICE_NONCE_HEADER = "smileid-device-nonce";
|
|
@@ -38,7 +44,17 @@ export const DEVICE_NONCE_HEADER = "smileid-device-nonce";
|
|
|
38
44
|
/** Axios config key that opts a request into attestation stamping. */
|
|
39
45
|
export const ATTESTATION_OPT_IN_KEY = "_useSmileIDAttestation";
|
|
40
46
|
|
|
47
|
+
export type SmileIntegrityInterceptorOptions = {
|
|
48
|
+
readonly securityPolicy: SecurityPolicy;
|
|
49
|
+
};
|
|
50
|
+
|
|
41
51
|
export class SmileIntegrityInterceptor {
|
|
52
|
+
private readonly _securityPolicy: SecurityPolicy;
|
|
53
|
+
|
|
54
|
+
constructor(options: SmileIntegrityInterceptorOptions) {
|
|
55
|
+
this._securityPolicy = options.securityPolicy;
|
|
56
|
+
}
|
|
57
|
+
|
|
42
58
|
attach(client: AxiosInstance): { request: number } {
|
|
43
59
|
const requestId = client.interceptors.request.use(async (config) => {
|
|
44
60
|
return this._onRequest(config);
|
|
@@ -55,6 +71,10 @@ export class SmileIntegrityInterceptor {
|
|
|
55
71
|
] === true;
|
|
56
72
|
if (!optedIn) return config;
|
|
57
73
|
|
|
74
|
+
// Server-driven policy can switch device attestation off for this session; when it is off,
|
|
75
|
+
// ship the opted-in request without requesting a token.
|
|
76
|
+
if (!this._securityPolicy.isPlayIntegrityCheckActive()) return config;
|
|
77
|
+
|
|
58
78
|
const macHeader = config.headers.get(REQUEST_MAC_HEADER);
|
|
59
79
|
if (typeof macHeader !== "string") return config;
|
|
60
80
|
|
|
@@ -2,6 +2,7 @@ import type { AxiosInstance, InternalAxiosRequestConfig } from "axios";
|
|
|
2
2
|
|
|
3
3
|
import { SmileIDCryptoError } from "../../security/smile_id_crypto_error";
|
|
4
4
|
import type { SmileIDCryptoManager } from "../../security/smile_id_crypto_manager";
|
|
5
|
+
import type { SecurityPolicy } from "../../security/security_policy";
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
8
|
* Stamps the `smileid-request-mac` header on every outbound request whose axios
|
|
@@ -18,6 +19,11 @@ import type { SmileIDCryptoManager } from "../../security/smile_id_crypto_manage
|
|
|
18
19
|
* rather than signed against an invented timestamp.
|
|
19
20
|
*
|
|
20
21
|
* Requests without the opt-in flag pass through unchanged.
|
|
22
|
+
*
|
|
23
|
+
* Signing is additionally gated by the server-driven {@link SecurityPolicy}: when the session's
|
|
24
|
+
* policy disables payload signing, an opted-in request still ships, just unsigned — the same wire
|
|
25
|
+
* shape as an opt-out request. The policy is fail-secure, so a token that omits the claim signs as
|
|
26
|
+
* before.
|
|
21
27
|
*/
|
|
22
28
|
|
|
23
29
|
export const REQUEST_MAC_HEADER = "smileid-request-mac";
|
|
@@ -28,13 +34,16 @@ export const REQUEST_MAC_OPT_IN_KEY = "_useSmileIDRequestMac";
|
|
|
28
34
|
|
|
29
35
|
export type SmileRequestMacInterceptorOptions = {
|
|
30
36
|
readonly cryptoManager: SmileIDCryptoManager;
|
|
37
|
+
readonly securityPolicy: SecurityPolicy;
|
|
31
38
|
};
|
|
32
39
|
|
|
33
40
|
export class SmileRequestMacInterceptor {
|
|
34
41
|
private readonly _cryptoManager: SmileIDCryptoManager;
|
|
42
|
+
private readonly _securityPolicy: SecurityPolicy;
|
|
35
43
|
|
|
36
44
|
constructor(options: SmileRequestMacInterceptorOptions) {
|
|
37
45
|
this._cryptoManager = options.cryptoManager;
|
|
46
|
+
this._securityPolicy = options.securityPolicy;
|
|
38
47
|
}
|
|
39
48
|
|
|
40
49
|
attach(client: AxiosInstance): { request: number } {
|
|
@@ -53,6 +62,10 @@ export class SmileRequestMacInterceptor {
|
|
|
53
62
|
] === true;
|
|
54
63
|
if (!optedIn) return config;
|
|
55
64
|
|
|
65
|
+
// Server-driven policy can switch payload signing off for this session; when it is off, ship
|
|
66
|
+
// the opted-in request unsigned and let the server apply its policy.
|
|
67
|
+
if (!this._securityPolicy.isPayloadSigningActive()) return config;
|
|
68
|
+
|
|
56
69
|
const timestamp = config.headers.get(REQUEST_TIMESTAMP_HEADER);
|
|
57
70
|
if (typeof timestamp !== "string") return config;
|
|
58
71
|
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
import { ImageType, type ImageTypeId } from "@smileid/usesmileid_bridge";
|
|
2
|
+
import { describe, expect, it } from "vitest";
|
|
3
|
+
|
|
4
|
+
import {
|
|
5
|
+
type CompressibleAsset,
|
|
6
|
+
CompressibleAssets,
|
|
7
|
+
} from "../compressible_asset";
|
|
8
|
+
import {
|
|
9
|
+
base64Len,
|
|
10
|
+
PayloadCompressionConfig as C,
|
|
11
|
+
} from "../payload_compression_config";
|
|
12
|
+
import { PayloadCompressor } from "../payload_compressor";
|
|
13
|
+
|
|
14
|
+
const mib = C.bytesPerMB;
|
|
15
|
+
|
|
16
|
+
/** Encoded size scales linearly with quality: `size(q) = base * q`. */
|
|
17
|
+
class FakeAsset implements CompressibleAsset {
|
|
18
|
+
recompressCount = 0;
|
|
19
|
+
private q: number;
|
|
20
|
+
constructor(
|
|
21
|
+
readonly imageType: ImageTypeId,
|
|
22
|
+
private readonly base: number,
|
|
23
|
+
startQuality: number,
|
|
24
|
+
) {
|
|
25
|
+
this.q = startQuality;
|
|
26
|
+
}
|
|
27
|
+
encodedSize(): number {
|
|
28
|
+
return Math.trunc(this.base * this.q);
|
|
29
|
+
}
|
|
30
|
+
quality(): number {
|
|
31
|
+
return this.q;
|
|
32
|
+
}
|
|
33
|
+
async recompress(quality: number): Promise<void> {
|
|
34
|
+
this.q = quality;
|
|
35
|
+
this.recompressCount += 1;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
describe("PayloadCompressor", () => {
|
|
40
|
+
it("base64Len matches ceil(n / 3) * 4", () => {
|
|
41
|
+
expect(base64Len(0)).toBe(0);
|
|
42
|
+
expect(base64Len(1)).toBe(4);
|
|
43
|
+
expect(base64Len(3)).toBe(4);
|
|
44
|
+
expect(base64Len(4)).toBe(8);
|
|
45
|
+
expect(base64Len(6)).toBe(8);
|
|
46
|
+
expect(base64Len(3_000_000)).toBe(Math.ceil(3_000_000 / 3) * 4);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it("raw and projected sizes sum images plus metadata", () => {
|
|
50
|
+
const assets = new CompressibleAssets({
|
|
51
|
+
selfie: new FakeAsset(ImageType.Selfie, 300_000, 1.0),
|
|
52
|
+
liveness: [new FakeAsset(ImageType.Liveness, 100_000, 1.0)],
|
|
53
|
+
});
|
|
54
|
+
const metadata = 50_000;
|
|
55
|
+
expect(PayloadCompressor.rawBundleSize(assets, metadata)).toBe(
|
|
56
|
+
300_000 + 100_000 + 50_000,
|
|
57
|
+
);
|
|
58
|
+
expect(PayloadCompressor.projectedEncodedSize(assets, metadata)).toBe(
|
|
59
|
+
base64Len(300_000) +
|
|
60
|
+
base64Len(100_000) +
|
|
61
|
+
metadata +
|
|
62
|
+
C.structuralOverheadBytes,
|
|
63
|
+
);
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it("plan order Rule B - selfie over 1MB compresses faces first then back then front", () => {
|
|
67
|
+
const assets = new CompressibleAssets({
|
|
68
|
+
selfie: new FakeAsset(ImageType.Selfie, 2 * mib, 0.95),
|
|
69
|
+
liveness: [new FakeAsset(ImageType.Liveness, mib, 0.8)],
|
|
70
|
+
documentFront: new FakeAsset(ImageType.DocumentFront, mib, 0.85),
|
|
71
|
+
documentBack: new FakeAsset(ImageType.DocumentBack, mib, 0.85),
|
|
72
|
+
});
|
|
73
|
+
const plan = PayloadCompressor.buildCompressionPlan(assets);
|
|
74
|
+
expect(plan.map(([a]) => a.imageType)).toEqual([
|
|
75
|
+
ImageType.Selfie,
|
|
76
|
+
ImageType.Liveness,
|
|
77
|
+
ImageType.DocumentBack,
|
|
78
|
+
ImageType.DocumentFront,
|
|
79
|
+
]);
|
|
80
|
+
expect(plan[0][1]).toBe(C.faceMinQ);
|
|
81
|
+
expect(plan[3][1]).toBe(C.docMinQ);
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
it("plan order Rule A - documents over 4MB compresses back, front, then faces", () => {
|
|
85
|
+
const assets = new CompressibleAssets({
|
|
86
|
+
selfie: new FakeAsset(ImageType.Selfie, 500_000, 0.95),
|
|
87
|
+
documentFront: new FakeAsset(ImageType.DocumentFront, 3 * mib, 0.9),
|
|
88
|
+
documentBack: new FakeAsset(ImageType.DocumentBack, 3 * mib, 0.9),
|
|
89
|
+
});
|
|
90
|
+
const plan = PayloadCompressor.buildCompressionPlan(assets);
|
|
91
|
+
expect(plan.map(([a]) => a.imageType)).toEqual([
|
|
92
|
+
ImageType.DocumentBack,
|
|
93
|
+
ImageType.DocumentFront,
|
|
94
|
+
ImageType.Selfie,
|
|
95
|
+
]);
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
it("plan order generic - small selfie and docs uses faces then docs", () => {
|
|
99
|
+
const assets = new CompressibleAssets({
|
|
100
|
+
selfie: new FakeAsset(ImageType.Selfie, 400_000, 0.95),
|
|
101
|
+
documentFront: new FakeAsset(ImageType.DocumentFront, 400_000, 0.85),
|
|
102
|
+
});
|
|
103
|
+
const plan = PayloadCompressor.buildCompressionPlan(assets);
|
|
104
|
+
expect(plan.map(([a]) => a.imageType)).toEqual([
|
|
105
|
+
ImageType.Selfie,
|
|
106
|
+
ImageType.DocumentFront,
|
|
107
|
+
]);
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
it("already small bundle is left untouched", async () => {
|
|
111
|
+
const selfie = new FakeAsset(ImageType.Selfie, mib, 0.95);
|
|
112
|
+
await PayloadCompressor.prepareBundleForTransmission(
|
|
113
|
+
new CompressibleAssets({ selfie }),
|
|
114
|
+
50_000,
|
|
115
|
+
);
|
|
116
|
+
expect(selfie.recompressCount).toBe(0);
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
it("over-budget bundle compresses under target and never breaches floors", async () => {
|
|
120
|
+
const selfie = new FakeAsset(
|
|
121
|
+
ImageType.Selfie,
|
|
122
|
+
Math.trunc((22 * mib) / 10),
|
|
123
|
+
0.95,
|
|
124
|
+
);
|
|
125
|
+
const liveness = [
|
|
126
|
+
new FakeAsset(ImageType.Liveness, Math.trunc((12 * mib) / 10), 0.8),
|
|
127
|
+
new FakeAsset(ImageType.Liveness, Math.trunc((12 * mib) / 10), 0.8),
|
|
128
|
+
];
|
|
129
|
+
const assets = new CompressibleAssets({ selfie, liveness });
|
|
130
|
+
const metadata = Math.trunc((2 * mib) / 10);
|
|
131
|
+
await PayloadCompressor.prepareBundleForTransmission(assets, metadata);
|
|
132
|
+
|
|
133
|
+
expect(
|
|
134
|
+
PayloadCompressor.rawBundleSize(assets, metadata),
|
|
135
|
+
).toBeLessThanOrEqual(C.rawFallback);
|
|
136
|
+
expect(selfie.quality()).toBeGreaterThanOrEqual(C.faceMinQ);
|
|
137
|
+
for (const frame of liveness)
|
|
138
|
+
expect(frame.quality()).toBeGreaterThanOrEqual(C.faceMinQ);
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
it("document-heavy bundle compresses back first and never breaches the doc floor", async () => {
|
|
142
|
+
const front = new FakeAsset(
|
|
143
|
+
ImageType.DocumentFront,
|
|
144
|
+
Math.trunc((23 * mib) / 10),
|
|
145
|
+
0.9,
|
|
146
|
+
);
|
|
147
|
+
const back = new FakeAsset(
|
|
148
|
+
ImageType.DocumentBack,
|
|
149
|
+
Math.trunc((23 * mib) / 10),
|
|
150
|
+
0.9,
|
|
151
|
+
);
|
|
152
|
+
const selfie = new FakeAsset(ImageType.Selfie, 400_000, 0.95);
|
|
153
|
+
const assets = new CompressibleAssets({
|
|
154
|
+
selfie,
|
|
155
|
+
documentFront: front,
|
|
156
|
+
documentBack: back,
|
|
157
|
+
});
|
|
158
|
+
await PayloadCompressor.prepareBundleForTransmission(assets, 100_000);
|
|
159
|
+
expect(back.quality()).toBeLessThanOrEqual(front.quality());
|
|
160
|
+
expect(front.quality()).toBeGreaterThanOrEqual(C.docMinQ);
|
|
161
|
+
expect(back.quality()).toBeGreaterThanOrEqual(C.docMinQ);
|
|
162
|
+
expect(
|
|
163
|
+
PayloadCompressor.rawBundleSize(assets, 100_000),
|
|
164
|
+
).toBeLessThanOrEqual(C.rawTarget);
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
it("hard guarantee drops past the per-type floor to fit the relaxed cap", async () => {
|
|
168
|
+
// floor .45 -> 12MB*.45 = 5.4MB raw, still over the relaxed 4.5MB ceiling. The guarantee pass
|
|
169
|
+
// must step quality below faceMinQ until the bundle fits both caps.
|
|
170
|
+
const selfie = new FakeAsset(ImageType.Selfie, 12 * mib, 0.95);
|
|
171
|
+
const assets = new CompressibleAssets({ selfie });
|
|
172
|
+
await PayloadCompressor.prepareBundleForTransmission(assets, 100_000);
|
|
173
|
+
expect(
|
|
174
|
+
PayloadCompressor.rawBundleSize(assets, 100_000),
|
|
175
|
+
).toBeLessThanOrEqual(C.rawFallback);
|
|
176
|
+
expect(
|
|
177
|
+
PayloadCompressor.projectedEncodedSize(assets, 100_000),
|
|
178
|
+
).toBeLessThanOrEqual(C.hardEncodedCap);
|
|
179
|
+
expect(selfie.quality()).toBeLessThan(C.faceMinQ);
|
|
180
|
+
expect(selfie.quality()).toBeGreaterThanOrEqual(C.absoluteMinQ);
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
it("extreme bundle unreachable by quality drives to the absolute floor without throwing", async () => {
|
|
184
|
+
// So large that even absoluteMinQ leaves it over the cap (real JPEGs shrink far more than this
|
|
185
|
+
// linear fake). The guarantee pass drives to the absolute floor and ships — never loops forever.
|
|
186
|
+
const selfie = new FakeAsset(ImageType.Selfie, 80 * mib, 0.95);
|
|
187
|
+
const assets = new CompressibleAssets({ selfie });
|
|
188
|
+
await PayloadCompressor.prepareBundleForTransmission(assets, 100_000);
|
|
189
|
+
expect(selfie.quality()).toBe(C.absoluteMinQ);
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
it("bundle in fallback band with light metadata is accepted", async () => {
|
|
193
|
+
const selfie = new FakeAsset(ImageType.Selfie, 9 * mib, 0.95);
|
|
194
|
+
const assets = new CompressibleAssets({ selfie });
|
|
195
|
+
await PayloadCompressor.prepareBundleForTransmission(assets, 100_000);
|
|
196
|
+
const raw = PayloadCompressor.rawBundleSize(assets, 100_000);
|
|
197
|
+
expect(raw).toBeGreaterThan(C.rawTarget);
|
|
198
|
+
expect(raw).toBeLessThanOrEqual(C.rawFallback);
|
|
199
|
+
});
|
|
200
|
+
});
|