Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cordova improvements - preparation for merge #220

Open
wants to merge 19 commits into
base: cordova
Choose a base branch
from
Open
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
34 changes: 0 additions & 34 deletions .github/workflows/test-ios.yml

This file was deleted.

5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,10 @@ The documentation is available at the [Wultra Developer Portal](https://develope

| Version | React-Native<sup>1</sup> | Cordova | Native SDK | Server version | Support Status |
|---------|--------------------------|-----------|--------------|----------------|-------------------|
| `2.5.x` | `0.73+` | `12.0.0+` | `1.7.x` | `0.24+` | Fully supported |
| `3.0.x` | `0.73+` | `12.0.0+` | `1.7.x` | `0.24+` | Fully supported |
| `2.5.x` | `0.73+` | - | `1.7.x` | `0.24+` | Security bugfixes |
| `2.4.x` | `0.71+` | - | `1.7.x` | `0.24+` | Security bugfixes |
| `2.3.x` | `0.64` - `0.70` | - | `1.7.x` | `0.24+` | Security bugfixes |
| `2.3.x` | `0.64` - `0.70` | - | `1.7.x` | `0.24+` | Not supported |
| `2.2.x` | | - | `1.6.x` | `0.24+` | Not supported |

<!-- begin box info -->
Expand Down
2 changes: 1 addition & 1 deletion android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ repositories {

dependencies {
implementation("org.jetbrains.kotlin:kotlin-stdlib:1.9.22")
api "com.wultra.android.powerauth:powerauth-sdk:1.7.9"
api "com.wultra.android.powerauth:powerauth-sdk:1.7.10"

if (project == rootProject) {
// The standalone build require to specify exact version of RN
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,6 @@
*/
package com.wultra.android.powerauth.js

import com.wultra.android.powerauth.bridge.Arguments
import com.wultra.android.powerauth.bridge.Dynamic
import com.wultra.android.powerauth.bridge.ReadableArray
import com.wultra.android.powerauth.bridge.ReadableMap
import com.wultra.android.powerauth.bridge.JsApiMethod
import com.wultra.android.powerauth.bridge.Promise
import com.wultra.android.powerauth.bridge.WritableMap


import android.annotation.SuppressLint
import android.app.Activity
Expand All @@ -31,25 +23,19 @@ import android.os.Build
import android.util.Base64
import android.util.Pair
import androidx.fragment.app.FragmentActivity
import io.getlime.security.powerauth.core.Password
import java.nio.charset.StandardCharsets

import io.getlime.security.powerauth.biometry.BiometricKeyData
import io.getlime.security.powerauth.biometry.BiometricAuthentication
import io.getlime.security.powerauth.biometry.BiometricStatus
import io.getlime.security.powerauth.biometry.BiometryType
import io.getlime.security.powerauth.biometry.IAddBiometryFactorListener
import io.getlime.security.powerauth.biometry.IBiometricAuthenticationCallback
import io.getlime.security.powerauth.biometry.ICommitActivationWithBiometryListener
import com.wultra.android.powerauth.bridge.*
import io.getlime.security.powerauth.biometry.*
import io.getlime.security.powerauth.core.*
import io.getlime.security.powerauth.exception.*
import io.getlime.security.powerauth.keychain.KeychainProtection
import io.getlime.security.powerauth.networking.interceptors.BasicHttpAuthenticationRequestInterceptor
import io.getlime.security.powerauth.sdk.*
import io.getlime.security.powerauth.networking.ssl.HttpClientSslNoValidationStrategy
import io.getlime.security.powerauth.networking.interceptors.CustomHeaderRequestInterceptor
import io.getlime.security.powerauth.networking.response.*
import io.getlime.security.powerauth.core.*
import io.getlime.security.powerauth.exception.*
import io.getlime.security.powerauth.networking.ssl.HttpClientSslNoValidationStrategy
import io.getlime.security.powerauth.sdk.*
import io.getlime.security.powerauth.sdk.impl.MainThreadExecutor
import java.nio.charset.StandardCharsets


class PowerAuthJsModule(
private val context: Context,
Expand Down Expand Up @@ -300,8 +286,8 @@ class PowerAuthJsModule(
fun commitActivation(instanceId: String, authMap: ReadableMap, promise: Promise) {
val context: Context = this.context
this.usePowerAuthOnMainThread(instanceId, promise, powerAuthBlock { sdk: PowerAuthSDK ->
val auth: PowerAuthAuthentication = constructAuthentication(authMap, true)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && authMap.getBoolean("isBiometry")) {
val auth: PowerAuthAuthentication = constructAuthentication(authMap, true, true)
val promptMap: ReadableMap? =
if (authMap.hasKey("biometricPrompt")) authMap.getMap("biometricPrompt") else null
val titleDesc = extractPromptStrings(promptMap)
Expand Down Expand Up @@ -339,6 +325,7 @@ class PowerAuthJsModule(
Errors.rejectPromise(promise, t)
}
} else {
val auth: PowerAuthAuthentication = constructAuthentication(authMap, true, false)
val result: Int = sdk.commitActivationWithAuthentication(context, auth)
if (result == PowerAuthErrorCodes.SUCCEED) {
promise.resolve(null)
Expand All @@ -359,7 +346,7 @@ class PowerAuthJsModule(
this.usePowerAuth(instanceId, promise, object : PowerAuthBlock {
@Throws(Exception::class)
override fun run(sdk: PowerAuthSDK) {
val auth: PowerAuthAuthentication = constructAuthentication(authMap, false)
val auth: PowerAuthAuthentication = constructAuthentication(authMap, false, false)
sdk.removeActivationWithAuthentication(
context,
auth,
Expand Down Expand Up @@ -403,7 +390,7 @@ class PowerAuthJsModule(
this.usePowerAuth(instanceId, promise, object : PowerAuthBlock {
@Throws(Exception::class)
override fun run(sdk: PowerAuthSDK) {
val auth: PowerAuthAuthentication = constructAuthentication(authMap, false)
val auth: PowerAuthAuthentication = constructAuthentication(authMap, false, false)
val paramMap = if (params == null) null else getStringMap(params)
val header: PowerAuthAuthorizationHttpHeader =
sdk.requestGetSignatureWithAuthentication(context, auth, uriId, paramMap)
Expand Down Expand Up @@ -434,7 +421,7 @@ class PowerAuthJsModule(
this.usePowerAuth(instanceId, promise, object : PowerAuthBlock {
@Throws(Exception::class)
override fun run(sdk: PowerAuthSDK) {
val auth: PowerAuthAuthentication = constructAuthentication(authMap, false)
val auth: PowerAuthAuthentication = constructAuthentication(authMap, false, false)
val decodedBody = body?.toByteArray(StandardCharsets.UTF_8)
val header: PowerAuthAuthorizationHttpHeader =
sdk.requestSignatureWithAuthentication(
Expand Down Expand Up @@ -472,7 +459,7 @@ class PowerAuthJsModule(
this.usePowerAuth(instanceId, promise, object : PowerAuthBlock {
@Throws(Exception::class)
override fun run(sdk: PowerAuthSDK) {
val auth: PowerAuthAuthentication = constructAuthentication(authMap, false)
val auth: PowerAuthAuthentication = constructAuthentication(authMap, false, false)
val decodedBody = body?.toByteArray(StandardCharsets.UTF_8)
val signature: String? =
sdk.offlineSignatureWithAuthentication(context, auth, uriId, decodedBody, nonce)
Expand Down Expand Up @@ -675,7 +662,7 @@ class PowerAuthJsModule(
this.usePowerAuth(instanceId, promise, object : PowerAuthBlock {
@Throws(Exception::class)
override fun run(sdk: PowerAuthSDK) {
val auth: PowerAuthAuthentication = constructAuthentication(authMap, false)
val auth: PowerAuthAuthentication = constructAuthentication(authMap, false, false)
sdk.fetchEncryptionKey(
context,
auth,
Expand Down Expand Up @@ -709,7 +696,7 @@ class PowerAuthJsModule(
this.usePowerAuth(instanceId, promise, object : PowerAuthBlock {
@Throws(Exception::class)
override fun run(sdk: PowerAuthSDK) {
val auth: PowerAuthAuthentication = constructAuthentication(authMap, false)
val auth: PowerAuthAuthentication = constructAuthentication(authMap, false, false)
sdk.signDataWithDevicePrivateKey(
context,
auth,
Expand Down Expand Up @@ -759,7 +746,7 @@ class PowerAuthJsModule(
this.usePowerAuth(instanceId, promise, object : PowerAuthBlock {
@Throws(Exception::class)
override fun run(sdk: PowerAuthSDK) {
val auth: PowerAuthAuthentication = constructAuthentication(authMap, false)
val auth: PowerAuthAuthentication = constructAuthentication(authMap, false, false)
sdk.getActivationRecoveryData(context, auth, object : IGetRecoveryDataListener {
override fun onGetRecoveryDataSucceeded(recoveryData: RecoveryData) {
val map: WritableMap = Arguments.createMap()
Expand Down Expand Up @@ -787,7 +774,7 @@ class PowerAuthJsModule(
this.usePowerAuth(instanceId, promise, object : PowerAuthBlock {
@Throws(Exception::class)
override fun run(sdk: PowerAuthSDK) {
val auth: PowerAuthAuthentication = constructAuthentication(authMap, false)
val auth: PowerAuthAuthentication = constructAuthentication(authMap, false, false)
sdk.confirmRecoveryCode(
context,
auth,
Expand Down Expand Up @@ -926,7 +913,7 @@ class PowerAuthJsModule(
this.usePowerAuth(instanceId, promise, object : PowerAuthBlock {
@Throws(Exception::class)
override fun run(sdk: PowerAuthSDK) {
val auth: PowerAuthAuthentication = constructAuthentication(authMap, false)
val auth: PowerAuthAuthentication = constructAuthentication(authMap, false, false)
sdk.tokenStore
.requestAccessToken(context, tokenName, auth, object : IGetTokenListener {
override fun onGetTokenSucceeded(token: PowerAuthToken) {
Expand Down Expand Up @@ -1110,7 +1097,8 @@ class PowerAuthJsModule(
@Throws(WrapperException::class)
private fun constructAuthentication(
map: ReadableMap,
forCommit: Boolean
forCommit: Boolean,
copyPassword: Boolean
): PowerAuthAuthentication {
val biometryKeyId: String? = map.getString("biometryKeyId")
val biometryKey: ByteArray?
Expand All @@ -1125,10 +1113,12 @@ class PowerAuthJsModule(
} else {
biometryKey = null
}
val password: Password? = if (map.hasKey("password")) {
passwordModule.usePassword(map.getDynamic("password"))
val password: Password?
if (map.hasKey("password")) {
val managedPassword = passwordModule.usePassword(map.getDynamic("password"))
password = if (copyPassword) managedPassword.copyToImmutable() else managedPassword
} else {
null
password = null
}
if (forCommit) {
// Authentication for activation commit
Expand All @@ -1154,7 +1144,7 @@ class PowerAuthJsModule(
return if (biometryKey != null) {
PowerAuthAuthentication.possessionWithBiometry(biometryKey)
} else if (password != null) {
PowerAuthAuthentication.commitWithPassword(password)
PowerAuthAuthentication.possessionWithPassword(password)
} else {
PowerAuthAuthentication.possession()
}
Expand Down Expand Up @@ -1401,6 +1391,8 @@ class PowerAuthJsModule(
if (biometryMap.hasKey("confirmBiometricAuthentication")) biometryMap.getBoolean("confirmBiometricAuthentication") else PowerAuthKeychainConfiguration.DEFAULT_CONFIRM_BIOMETRIC_AUTHENTICATION
val authenticateOnBiometricKeySetup: Boolean =
if (biometryMap.hasKey("authenticateOnBiometricKeySetup")) biometryMap.getBoolean("authenticateOnBiometricKeySetup") else PowerAuthKeychainConfiguration.DEFAULT_AUTHENTICATE_ON_BIOMETRIC_KEY_SETUP
val fallbackToSharedBiometryKey =
if (biometryMap.hasKey("fallbackToSharedBiometryKey")) biometryMap.getBoolean("fallbackToSharedBiometryKey") else PowerAuthKeychainConfiguration.DEFAULT_ENABLE_FALLBACK_TO_SHARED_BIOMETRY_KEY
// Keychain configuration
val minimalRequiredKeychainProtection =
getKeychainProtectionFromString(keychainMap.getString("minimalRequiredKeychainProtection"))
Expand All @@ -1409,6 +1401,7 @@ class PowerAuthJsModule(
.confirmBiometricAuthentication(confirmBiometricAuthentication)
.authenticateOnBiometricKeySetup(authenticateOnBiometricKeySetup)
.minimalRequiredKeychainProtection(minimalRequiredKeychainProtection)
.enableFallbackToSharedBiometryKey(fallbackToSharedBiometryKey)
.build()
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package com.wultra.android.powerauth.reactnative;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

import com.facebook.react.bridge.BaseJavaModule;
import com.facebook.react.bridge.Promise;
Expand Down Expand Up @@ -60,6 +61,13 @@ public void invalidate() {
objectRegisterJs.invalidate();
}

// Module Inter-Op

@Nullable
<T> T findObject(@NonNull String objectId, @NonNull Class<T> expectedClass) {
return objectRegisterJs.findObject(objectId, expectedClass);
}

// ---------------------------------------------------------------------------------------------
// JavaScript interface

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/*
* Copyright 2024 Wultra s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.wultra.android.powerauth.reactnative;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.facebook.react.bridge.ReactContext;
import io.getlime.security.powerauth.sdk.PowerAuthSDK;

/**
* PowerAuth React-Native Utilities.
* @noinspection unused
*/
public class PowerAuthUtils {
/**
* Function that lifts PowerAuthSDK instance from the React-Native module if present (configured). If the object is not available, returns nil.
* Might throw IllegalStateException from the RN layer.
*
* @param instanceId Id of the instance that was configured from the JS/TS layer.
* @param reactContext React context obtained from your ReactNative module or app.
* @return Native PowerAuthSDK or null if such instance is not configured.
*/
@Nullable
public static PowerAuthSDK liftPowerAuthSdk(@NonNull String instanceId, @NonNull ReactContext reactContext) {
ObjectRegister module = reactContext.getNativeModule(ObjectRegister.class);
if (module == null) {
return null;
}
return module.findObject(instanceId, PowerAuthSDK.class);
}
}
43 changes: 43 additions & 0 deletions docs/Accessing-Native-PowerAuthSDK.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# Accessing the Native PowerAuthSDK

<!-- begin box warning -->
__This feature is not available for Cordova.__
<!-- end -->

If you need to access the native PowerAuthSDK object (created in the JS/TS code) from Java/Kotlin or Objective-C/Swift, you can use the following snippets.

<!-- begin box info -->
Consider consultation with our technical support when accessing the native object. This technique should be used rarely and only for limited purposes as it might lead to unexpected behavior.
<!-- end -->

## iOS

To access the native `PowerAuthSDK` object on iOS, use the `LiftPowerAuthSdk` helper method.

```objc
#import "LiftPowerAuthSdk.h"
#import <PowerAuth2/PowerAuthSDK.h>

// The `bridge` (of type `RCTBridge`) availability depends on the place, where you're trying to access the PowerAuthSDK object.
// For example, you can access it in the AppDelegate that implements `RCTAppDelegate` or as property of an `RCTBridgeModule`.
PowerAuthSDK * sdk = LiftPowerAuthSdk(@"myPowerauthInstance", bridge);
if (sdk) {
// Do something with the native instance
}
```

## Android

To access the native `PowerAuthSDK` object on Android, use the `PowerAuthUtils.liftPowerAuthSdk` helper method.

```java
import com.wultra.android.powerauth.reactnative.PowerAuthUtils;
import io.getlime.security.powerauth.sdk.PowerAuthSDK;

// The `reactNativeContext` (of type ReactContext) availability depends on the place, where you're trying to access the PowerAuthSDK object.
// For example, you can pass it when creating our package when implementing `ReactPackage` interface.
PowerAuthSDK sdk = PowerAuthUtils.liftPowerAuthSdk("test", reactNativeContext);
if (sdk != null) {
// Do something with the native instance
}
```
5 changes: 5 additions & 0 deletions docs/Changelog.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Changelog

## 3.0.0 (TBA)

- Added Cordova Support
1 change: 1 addition & 0 deletions docs/Configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ In case that you need an advanced configuration, then you can import and use the
- `accessGroupName` - iOS specific, defines access group name used by the `PowerAuth` keychain instances. This is useful in situations, when your application is sharing data with another application or application's extension from the same vendor. The default value is `null`. See note<sup>2</sup> below.
- `userDefaultsSuiteName` - iOS specific, defines suite name used by the `UserDefaults` that check for Keychain data presence. This is useful in situations, when your application is sharing data with another application or application's extension from the same vendor. The default value is `null`. See note<sup>2</sup> below.
- `minimalRequiredKeychainProtection` - Android specific, defines minimal required keychain protection level that must be supported on the current device. The default value is `PowerAuthKeychainProtection.NONE`. See note<sup>3</sup> below.
- `fallbackToSharedBiometryKey` - Android specific, defines whether fallback to a shared, legacy biometry key is enabled. By default, this is enabled for compatibility reasons. If your application uses multiple `PowerAuth` instances, it's recommended to set this configuration to `false`.

- `PowerAuthSharingConfiguration` class or `PowerAuthSharingConfigurationType` interface - to configure an activation data sharing on iOS platform. You can alter the following parameters:
- `appGroup` - defines name of app group that allows you sharing data between multiple applications. Be aware that the value overrides `accessGroupName` property if it's provided in `PowerAuthKeychainConfiguration`.
Expand Down
Loading