Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 8 additions & 7 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
# Changelog

## Next

### Fixes
## 2.0.0

- Bump `adm-zip`, the Config Plugin's only runtime dependency, to 0.6.1. This clears GHSA-xcpc-8h2w-3j85 (high) and GHSA-vwc7-r8mq-g2x9 (moderate) from consumers' `npm audit`. Neither was reachable: the plugin never extracts to disk, and it only parses the AAR it downloaded after checking its SHA-256, or its own cached copy under `node_modules`.
- **Android**: the Config Plugin matches the maven repository URL exactly when deciding whether `android/build.gradle` already declares it, and ignores declarations inside `//` and `/* */` comments. The substring check it replaces skipped adding the repository when the URL appeared only in a comment or as the prefix of a longer URL. It was also the CodeQL `js/incomplete-url-substring-sanitization` alert.
Upgrading from 1.x: every failure now rejects with a `DocuSignError`. Check any code that branches on `error.code`, matches message text, handles `status: 'error'` from `presentCaptiveSigning*`, or reads `errorCode` in an `addSigningErrorListener` callback. Codes are the lowercase codes the README documents, on both platforms, where iOS previously emitted `ERR_`-prefixed variants and Android rejected most failures as `signing_failed`. The new Android `launchStrategy` is opt-in. Both native SDKs also move forward, see [Native SDKs](#native-sdks) for what that changes in your app.

## 2.0.0
### Native SDKs

Upgrading from 1.x: every failure now rejects with a `DocuSignError`. Check any code that branches on `error.code`, matches message text, handles `status: 'error'` from `presentCaptiveSigning*`, or reads `errorCode` in an `addSigningErrorListener` callback. Codes are the lowercase codes the README documents, on both platforms, where iOS previously emitted `ERR_`-prefixed variants and Android rejected most failures as `signing_failed`. The new Android `launchStrategy` is opt-in.
- **Android**: DocuSign Android SDK 2.1.4 to 2.1.7, the version DocuSign's install guide documents. Its own dependencies moved to Glide 5.0.4, OkHttp 4.12.0, RxJava 3, Room 2.7.2 and kotlin-stdlib 2.2.10, and it no longer pulls in RxJava 2, slf4j or commons-codec. Gradle resolves these across the whole app, so an app on an older OkHttp or Room gets the newer one. Compiling the module against it needs Kotlin 2.1 or newer, which Expo SDK 55 already ships. The config plugin still strips the Glide class from `sdk-pdf`, now pinned to the 2.1.7 hash, so run `npx expo prebuild` after upgrading to fetch the new AAR.
- **iOS**: DocuSign iOS SDK 4.1.1 to 4.3.0. DocuSign 4.3.0 fixes a double JavaScript invocation that caused signing backend errors. The captive signing screen gains Print, Download and Download Separate PDFs menu options (added in DocuSign 4.2.0), and the SDK has no setup key to hide them. DocuSign also inserted a new error code in the middle of its `DSMErrorCode` enum, so for DocuSign's own errors from 1022 up, `native.code` is one higher than on 4.1.1. A project with a committed `Podfile.lock` needs `pod update DocuSign`, because `pod install` alone keeps the locked 4.1.1 and fails to resolve.

### Breaking changes

Expand All @@ -34,6 +32,9 @@ Upgrading from 1.x: every failure now rejects with a `DocuSignError`. Check any

### Fixes

- Bump `adm-zip`, the Config Plugin's only runtime dependency, to 0.6.1. This clears GHSA-xcpc-8h2w-3j85 (high) and GHSA-vwc7-r8mq-g2x9 (moderate) from consumers' `npm audit`. Neither was reachable: the plugin never extracts to disk, and it only parses the AAR it downloaded after checking its SHA-256, or its own cached copy under `node_modules`.
- **Android**: the Config Plugin matches the maven repository URL exactly when deciding whether `android/build.gradle` already declares it, and ignores declarations inside `//` and `/* */` comments. The substring check it replaces skipped adding the repository when the URL appeared only in a comment or as the prefix of a longer URL. It was also the CodeQL `js/incomplete-url-substring-sanitization` alert.
- **iOS**: the module compiles without warnings under the Swift 6 compiler. Its exception classes restate the `@unchecked Sendable` conformance they inherit from Expo's `Exception`, and a `??` fallback on `DSMManager.defaultConfigurations()`, which never returns nil, is gone.
- **iOS**: the view controller to present from is looked up on the main thread. The lookup read `UIApplication.shared` on the background queue the JS call arrived on.
- **iOS**: a missing view controller settles the promise once. It previously completed the pending signing slot with a failure and also threw, rejecting the same call twice.
- **iOS**: `endSigningSession` no longer calls `DSMManager` off the main thread. Expo dispatches a synchronous `AsyncFunction` body on a serial background queue, so `clearAllWebCookies()` and `logout()` were reached off-main on every call, including the one `useDocuSignSigning`'s `reset()` makes between flows. The guard now lives in `clearWebCookiesAsync`, the only method touching `DSMManager` and `WKWebsiteDataStore` directly, so it covers every caller. Thanks to @virajpsimformsolutions for finding and fixing this.
Expand Down
25 changes: 14 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -94,25 +94,25 @@ The React Native layer never renders any of the signing UI. It only triggers the

| Platform | Minimum OS | SDK version | Language |
| -------- | -------------------- | -------------------------- | ----------- |
| iOS | 15.1 | DocuSign iOS SDK 4.1.1 | Swift 5.9 |
| Android | API 24 (Android 7.0) | DocuSign Android SDK 2.1.4 | Kotlin 1.8+ |
| iOS | 15.1 | DocuSign iOS SDK 4.3.0 | Swift 5.9 |
| Android | API 24 (Android 7.0) | DocuSign Android SDK 2.1.7 | Kotlin 2.1+ |

**Runtime requirements:**

- Expo SDK 55 or newer, OR bare React Native 0.74+
- Expo SDK 55 or newer, OR bare React Native 0.74+ (Android needs Kotlin 2.1 or newer)
- React Native New Architecture (Fabric + Hermes): supported, no additional configuration
- Expo Go: NOT supported (custom native modules require a development build)

## SDK bundling policy

The DocuSign native SDKs are **NOT bundled** inside this npm package. They are declared as external dependencies and resolved at consumer build time:

- **iOS**: `pod 'DocuSign-iOS-SDK', '~> 4.1.1'` from the public CocoaPods trunk
- **Android**: `com.docusign:androidsdk:2.1.4` from DocuSign's Maven repository (the config plugin adds the repo automatically)
- **iOS**: the `DocuSign` pod (`~> 4.3.0`) from the public CocoaPods trunk, declared by this package's podspec
- **Android**: `com.docusign:androidsdk:2.1.7` from DocuSign's Maven repository (the config plugin adds the repo automatically)

### Android Glide collision workaround

DocuSign's `com.docusign:sdk-pdf:2.1.4` AAR ships a pre-generated `com.bumptech.glide.GeneratedAppGlideModuleImpl.class` that collides at dex time with any other Glide-based library in the host app (notably `expo-image`, `react-native-fast-image`, and similar). To avoid this without redistributing DocuSign's binary, the Expo Config Plugin downloads `sdk-pdf-2.1.4.aar` directly from DocuSign's public Maven during `expo prebuild`, verifies its SHA-256 against a pinned hash, removes the offending class from the AAR's `classes.jar` in-memory, and writes the stripped artifact to `node_modules/react-native-docusign/android/libs/`. The existing flatDir injection picks it up at consumer build time. The download is cached after the first run; corrupted or partial caches are detected and regenerated. SHA mismatch or fetch failure aborts `expo prebuild` with an actionable error rather than silently letting the Android build fail later at the dex step.
DocuSign's `com.docusign:sdk-pdf:2.1.7` AAR ships a pre-generated `com.bumptech.glide.GeneratedAppGlideModuleImpl.class` that collides at dex time with any other Glide-based library in the host app (notably `expo-image`, `react-native-fast-image`, and similar). To avoid this without redistributing DocuSign's binary, the Expo Config Plugin downloads `sdk-pdf-2.1.7.aar` directly from DocuSign's public Maven during `expo prebuild`, verifies its SHA-256 against a pinned hash, removes the offending class from the AAR's `classes.jar` in-memory, and writes the stripped artifact to `node_modules/react-native-docusign/android/libs/`. The existing flatDir injection picks it up at consumer build time. The download is cached after the first run; corrupted or partial caches are detected and regenerated. SHA mismatch or fetch failure aborts `expo prebuild` with an actionable error rather than silently letting the Android build fail later at the dex step.

### What ships inside this package

Expand Down Expand Up @@ -182,7 +182,7 @@ npx expo run:android

If you are not using Expo prebuild, you must manually:

- **iOS**: add `pod 'DocuSign-iOS-SDK', '~> 4.1.1'` to your Podfile and run `pod install`
- **iOS**: run `pod install`. This package's podspec already depends on the `DocuSign` pod, so your Podfile needs no entry for it
- **Android**: add the Maven repo and the DocuSign dependency to your `android/build.gradle` + `android/app/build.gradle`
- **iOS**: add the `NSCameraUsageDescription` and `NSPhotoLibraryUsageDescription` keys to your `Info.plist`
- **Android**: add the required permissions to your `AndroidManifest.xml`
Expand All @@ -209,7 +209,7 @@ type DocuSignPluginProps = {

/**
* URL of the Android Maven repository that serves the DocuSign Android SDK.
* Defaults to 'https://maven.docusign.com/'.
* Defaults to 'https://docucdn-a.akamaihd.net/prod/docusignandroidsdk'.
* Override only if DocuSign moves their repo.
*/
androidMavenRepo?: string;
Expand Down Expand Up @@ -899,24 +899,26 @@ The config plugin writes these keys automatically, or you can configure them man

### iOS build fails with "No such module 'DocuSignSDK'"

Ensure `pod install` completed successfully inside `ios/`. The podspec declares a dependency on `DocuSign-iOS-SDK`; if CocoaPods trunk is unreachable, the pod cannot be installed. Check your network, proxy, and CocoaPods version (`pod --version` should be 1.14+).
Ensure `pod install` completed successfully inside `ios/`. The podspec declares a dependency on the `DocuSign` pod; if CocoaPods trunk is unreachable, the pod cannot be installed. Check your network, proxy, and CocoaPods version (`pod --version` should be 1.14+).

### iOS build fails with "Undefined symbols" for DSMManager

The DocuSign SDK must be set to `use_frameworks!` mode. Most Expo projects use frameworks by default. If you recently switched to static libraries, add `use_frameworks! :linkage => :static` to your Podfile and rebuild.

### Android build fails with "Could not find com.docusign:androidsdk:2.1.4"
### Android build fails with "Could not find com.docusign:androidsdk:2.1.7"

The DocuSign Android SDK is not on Maven Central; it is hosted on DocuSign's own Maven repository. Ensure the config plugin has added the repo to your project-level `build.gradle`. If running a bare project, manually add:

```groovy
allprojects {
repositories {
maven { url "https://maven.docusign.com/" }
maven { url "https://docucdn-a.akamaihd.net/prod/docusignandroidsdk" }
}
}
```

This is the URL DocuSign's own [Android SDK install guide](https://github.com/docusign/mobile-android-sdk) uses. It is a static file host with no index page, so opening it in a browser returns 404 even though Gradle resolves artifacts under it, such as `com/docusign/androidsdk/2.1.7/androidsdk-2.1.7.pom`. It also serves no `maven-metadata.xml`, so declare exact versions: a dynamic version such as `2.+` does not resolve.

### "not_logged_in" error when calling `presentCaptiveSigning`

The SDK login state is in-memory and does not survive app restarts. Always call `loginWithAccessToken()` before `presentCaptiveSigning()` within the same app session.
Expand Down Expand Up @@ -953,6 +955,7 @@ The module uses `appContext.activityProvider.currentActivity` to get the current
| This package version | Expo SDK | React Native | iOS SDK | Android SDK |
| -------------------- | -------- | ------------ | ------------------ | ---------------------- |
| 1.0.x | 55.x | 0.82.x | DocuSign iOS 4.1.1 | DocuSign Android 2.1.4 |
| 2.0.x | 55.x+ | 0.85.x+ | DocuSign iOS 4.3.0 | DocuSign Android 2.1.7 |

## Limitations

Expand Down
10 changes: 5 additions & 5 deletions android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -70,14 +70,14 @@ dependencies {
// collides with expo-image (and any other Glide-based library) at dex time.
// We exclude the upstream sdk-pdf and link a locally-stripped copy from
// libs/ that has the generated Glide classes removed.
implementation('com.docusign:androidsdk:2.1.4') {
implementation('com.docusign:androidsdk:2.1.7') {
exclude group: 'com.docusign', module: 'sdk-pdf'
}
implementation 'com.docusign:sdk-common:2.1.4'
implementation 'com.docusign:sdk-common:2.1.7'
// Direct file reference instead of flatDir (name:..., ext:...). Gradle 9.0 no
// longer honors subproject-scoped flatDir when resolving across project
// boundaries, so the stripped AAR (placed at libs/sdk-pdf-2.1.4-stripped.aar
// boundaries, so the stripped AAR (placed at libs/sdk-pdf-2.1.7-stripped.aar
// by the config plugin) wasn't found and the build failed with
// "Could not find :sdk-pdf-2.1.4-stripped:".
implementation files("$projectDir/libs/sdk-pdf-2.1.4-stripped.aar")
// "Could not find :sdk-pdf-2.1.7-stripped:".
implementation files("$projectDir/libs/sdk-pdf-2.1.7-stripped.aar")
}
12 changes: 6 additions & 6 deletions ios/DocuSignError.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import ExpoModulesCore
// when none is set, which would surface NotInitializedException to JS as ERR_NOT_INITIALIZED, not
// the not_initialized documented in the README error table and emitted by the Android module.

internal class InitializeFailedException: GenericException<String> {
internal class InitializeFailedException: GenericException<String>, @unchecked Sendable {
override var code: String {
"initialize_failed"
}
Expand All @@ -15,7 +15,7 @@ internal class InitializeFailedException: GenericException<String> {
}
}

internal class SigningInProgressException: Exception {
internal class SigningInProgressException: Exception, @unchecked Sendable {
override var code: String {
"signing_in_progress"
}
Expand All @@ -25,7 +25,7 @@ internal class SigningInProgressException: Exception {
}
}

internal class InvalidSigningUrlException: Exception {
internal class InvalidSigningUrlException: Exception, @unchecked Sendable {
override var code: String {
"invalid_signing_url"
}
Expand All @@ -35,7 +35,7 @@ internal class InvalidSigningUrlException: Exception {
}
}

internal class NotInitializedException: Exception {
internal class NotInitializedException: Exception, @unchecked Sendable {
override var code: String {
"not_initialized"
}
Expand All @@ -45,7 +45,7 @@ internal class NotInitializedException: Exception {
}
}

internal class NotLoggedInException: Exception {
internal class NotLoggedInException: Exception, @unchecked Sendable {
override var code: String {
"not_logged_in"
}
Expand All @@ -55,7 +55,7 @@ internal class NotLoggedInException: Exception {
}
}

internal class PresentationException: GenericException<String> {
internal class PresentationException: GenericException<String>, @unchecked Sendable {
override var code: String {
"presentation_failed"
}
Expand Down
2 changes: 1 addition & 1 deletion ios/DocuSignManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ internal final class DocuSignManager: NSObject {
self.environment = environment

let dispatchSetup: () -> Void = {
var configurations = DSMManager.defaultConfigurations() ?? [:]
var configurations = DSMManager.defaultConfigurations()
if options.disablePoweredByBranding {
configurations[DSM_SETUP_POWERED_BY_DOCUSIGN_ENABLED] = DSM_SETUP_FALSE_VALUE
}
Expand Down
2 changes: 1 addition & 1 deletion ios/ReactNativeDocuSign.podspec
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ Pod::Spec.new do |s|
s.static_framework = true

s.dependency 'ExpoModulesCore'
s.dependency 'DocuSign', '~> 4.1.1'
s.dependency 'DocuSign', '~> 4.3.0'

s.pod_target_xcconfig = {
'DEFINES_MODULE' => 'YES',
Expand Down
8 changes: 4 additions & 4 deletions plugin/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,16 @@ const DEFAULT_CAMERA_PERMISSION =
const DEFAULT_PHOTO_PERMISSION =
'Allows selecting photos to attach to DocuSign documents.';

const SDK_PDF_VERSION = '2.1.4';
const SDK_PDF_VERSION = '2.1.7';
const SDK_PDF_UPSTREAM_URL = `https://docucdn-a.akamaihd.net/prod/docusignandroidsdk/com/docusign/sdk-pdf/${SDK_PDF_VERSION}/sdk-pdf-${SDK_PDF_VERSION}.aar`;
// SHA-256 of the upstream `sdk-pdf-2.1.4.aar` as published on DocuSign's CDN.
// SHA-256 of the upstream `sdk-pdf-2.1.7.aar` as published on DocuSign's CDN.
// Verified at the time this version of the plugin was cut. Any deviation
// (CDN tampering, DNS hijack, partial download) causes the plugin to throw
// rather than write a binary we cannot vouch for. To bump the upstream SDK
// version, recompute this hash locally:
// curl -sSL <SDK_PDF_UPSTREAM_URL> | shasum -a 256
const SDK_PDF_SHA256 =
'26eb53effd74d117397fbfd77e46a94786bbbd05fb9318fdbbff389c1a4dcb0a';
'3f9be5ffcde3405ae422f7fe926d6ea716336b7cfc8ff9185d7cfb1d6203ab4a';
const STRIPPED_AAR_FILENAME = `sdk-pdf-${SDK_PDF_VERSION}-stripped.aar`;
const GLIDE_GENERATED_CLASS =
'com/bumptech/glide/GeneratedAppGlideModuleImpl.class';
Expand Down Expand Up @@ -172,7 +172,7 @@ const withDocuSignAndroidStrippedAarFlatDir: ConfigPlugin = (config) =>
});

/**
* Downloads the upstream `com.docusign:sdk-pdf:2.1.4` AAR from DocuSign's
* Downloads the upstream `com.docusign:sdk-pdf:2.1.7` AAR from DocuSign's
* public Maven repository and strips the pre-generated
* `com.bumptech.glide.GeneratedAppGlideModuleImpl` class from its
* `classes.jar`. The stripped artifact is written to
Expand Down
Loading