react-native-credentials-manager 0.2.1 → 0.3.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/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  A React Native library that implements the [Credential Manager API](https://developer.android.com/identity/sign-in/credential-manager) for Android. This library allows you to manage passwords and passkeys in your React Native applications.
4
4
 
5
- > ⚠️ **Note**: This library is actively under development. iOS support using Authentication Services is coming soon. Support for the old React Native architecture is also in progress. Google Sign-In integration with Credential Manager will be added shortly.
5
+ > ⚠️ **Note**: This library is actively under development. iOS support using Authentication Services is coming soon. ~~Support for the old React Native architecture is also in progress.~~ Supports old architecture now. Google Sign-In integration with Credential Manager will be added shortly.
6
6
 
7
7
  ## Features
8
8
 
@@ -236,7 +236,7 @@ For more detailed information about the underlying APIs, refer to:
236
236
 
237
237
  ## Roadmap
238
238
 
239
- - Old Architecture Support
239
+ - ~~Old Architecture Support~~ ✅
240
240
  - iOS Support using [Authentication Services](https://developer.apple.com/documentation/authenticationservices?language=objc)
241
241
  - Additional Authentication Methods
242
242
  - Comprehensive Documentation
@@ -79,16 +79,26 @@ android {
79
79
  targetCompatibility JavaVersion.VERSION_1_8
80
80
  }
81
81
 
82
- sourceSets {
83
- main {
84
- if (isNewArchitectureEnabled()) {
85
- java.srcDirs += [
86
- "generated/java",
87
- "generated/jni"
88
- ]
89
- }
82
+ sourceSets {
83
+ main {
84
+ if (isNewArchitectureEnabled()) {
85
+ java.srcDirs += ['src/newarch',"generated/java",]
86
+ } else {
87
+ java.srcDirs += ['src/oldarch']
88
+ }
89
+ }
90
90
  }
91
- }
91
+
92
+ // sourceSets {
93
+ // main {
94
+ // if (isNewArchitectureEnabled()) {
95
+ // java.srcDirs += [
96
+ // "generated/java",
97
+ // "generated/jni"
98
+ // ]
99
+ // }
100
+ // }
101
+ // }
92
102
  }
93
103
 
94
104
  repositories {
@@ -0,0 +1,7 @@
1
+ package com.credentialsmanager
2
+
3
+ class CredentialsManagerModuleImpl {
4
+ companion object {
5
+ const val NAME = "CredentialsManager"
6
+ }
7
+ }
@@ -12,7 +12,7 @@ class CredentialsManagerPackage : BaseReactPackage() {
12
12
  name: String,
13
13
  reactContext: ReactApplicationContext,
14
14
  ): NativeModule? =
15
- if (name == CredentialsManagerModule.NAME) {
15
+ if (name == CredentialsManagerModuleImpl.NAME) {
16
16
  CredentialsManagerModule(reactContext)
17
17
  } else {
18
18
  null
@@ -21,14 +21,15 @@ class CredentialsManagerPackage : BaseReactPackage() {
21
21
  override fun getReactModuleInfoProvider(): ReactModuleInfoProvider =
22
22
  ReactModuleInfoProvider {
23
23
  val moduleInfos: MutableMap<String, ReactModuleInfo> = HashMap()
24
- moduleInfos[CredentialsManagerModule.NAME] =
24
+ val isTurboModule = BuildConfig.IS_NEW_ARCHITECTURE_ENABLED
25
+ moduleInfos[CredentialsManagerModuleImpl.NAME] =
25
26
  ReactModuleInfo(
26
- CredentialsManagerModule.NAME,
27
- CredentialsManagerModule.NAME,
27
+ CredentialsManagerModuleImpl.NAME,
28
+ CredentialsManagerModuleImpl.NAME,
28
29
  false, // canOverrideExistingModule
29
30
  false, // needsEagerInit
30
31
  false, // isCxxModule
31
- true, // isTurboModule
32
+ isTurboModule, // isTurboModule
32
33
  )
33
34
  moduleInfos
34
35
  }
@@ -0,0 +1,91 @@
1
+ package com.credentialsmanager.handlers
2
+
3
+ import android.content.Context
4
+ import androidx.credentials.CreatePasswordRequest
5
+ import androidx.credentials.CreatePublicKeyCredentialRequest
6
+ import androidx.credentials.CreatePublicKeyCredentialResponse
7
+ import androidx.credentials.CredentialManager
8
+ import androidx.credentials.GetCredentialRequest
9
+ import androidx.credentials.GetPasswordOption
10
+ import androidx.credentials.GetPublicKeyCredentialOption
11
+ import androidx.credentials.PasswordCredential
12
+ import androidx.credentials.PublicKeyCredential
13
+ import com.facebook.react.bridge.Arguments
14
+ import com.facebook.react.bridge.ReadableMap
15
+ import org.json.JSONObject
16
+
17
+ class CredentialHandler(
18
+ private val context: Context,
19
+ ) {
20
+ private val credentialManager = CredentialManager.create(context)
21
+
22
+ suspend fun createPasskey(
23
+ jsonString: String,
24
+ preferImmediatelyAvailableCredentials: Boolean,
25
+ ): ReadableMap? {
26
+ val request =
27
+ CreatePublicKeyCredentialRequest(
28
+ requestJson = jsonString,
29
+ preferImmediatelyAvailableCredentials = preferImmediatelyAvailableCredentials,
30
+ )
31
+
32
+ val response =
33
+ credentialManager.createCredential(
34
+ context,
35
+ request,
36
+ ) as CreatePublicKeyCredentialResponse
37
+
38
+ return response.data.getString("androidx.credentials.BUNDLE_KEY_REGISTRATION_RESPONSE_JSON")?.let { json ->
39
+ val jsonObject = Arguments.createMap()
40
+ val parsedObject = JSONObject(json)
41
+
42
+ parsedObject.keys().forEach { key ->
43
+ jsonObject.putString(key, parsedObject.getString(key))
44
+ }
45
+ jsonObject
46
+ }
47
+ }
48
+
49
+ suspend fun createPassword(
50
+ username: String,
51
+ password: String,
52
+ ) {
53
+ val createPasswordRequest = CreatePasswordRequest(id = username, password = password)
54
+ credentialManager.createCredential(context, createPasswordRequest)
55
+ }
56
+
57
+ suspend fun getSavedCredentials(jsonString: String): ReadableMap? {
58
+ val getPublicKeyCredentialOption = GetPublicKeyCredentialOption(jsonString, null)
59
+ val getPasswordOption = GetPasswordOption()
60
+
61
+ val result =
62
+ credentialManager.getCredential(
63
+ context,
64
+ GetCredentialRequest(
65
+ listOf(
66
+ getPublicKeyCredentialOption,
67
+ getPasswordOption,
68
+ ),
69
+ ),
70
+ )
71
+
72
+ return when (result.credential) {
73
+ is PublicKeyCredential -> {
74
+ val cred = result.credential as PublicKeyCredential
75
+ Arguments.createMap().apply {
76
+ putString("type", "passkey")
77
+ putString("authenticationResponseJson", cred.authenticationResponseJson)
78
+ }
79
+ }
80
+ is PasswordCredential -> {
81
+ val cred = result.credential as PasswordCredential
82
+ Arguments.createMap().apply {
83
+ putString("type", "password")
84
+ putString("username", cred.id)
85
+ putString("password", cred.password)
86
+ }
87
+ }
88
+ else -> null
89
+ }
90
+ }
91
+ }
@@ -0,0 +1,36 @@
1
+ package com.credentialsmanager.handlers
2
+
3
+ import android.util.Log
4
+ import androidx.credentials.exceptions.CreateCredentialCancellationException
5
+ import androidx.credentials.exceptions.CreateCredentialCustomException
6
+ import androidx.credentials.exceptions.CreateCredentialException
7
+ import androidx.credentials.exceptions.CreateCredentialInterruptedException
8
+ import androidx.credentials.exceptions.CreateCredentialProviderConfigurationException
9
+ import androidx.credentials.exceptions.CreateCredentialUnknownException
10
+ import androidx.credentials.exceptions.publickeycredential.CreatePublicKeyCredentialDomException
11
+
12
+ object ErrorHandler {
13
+ fun handleCredentialError(e: CreateCredentialException) {
14
+ when (e) {
15
+ is CreatePublicKeyCredentialDomException -> {
16
+ Log.d("CredentialManager", "passkey DOM errors")
17
+ }
18
+ is CreateCredentialCancellationException -> {
19
+ Log.d("CredentialManager", "User cancelled")
20
+ }
21
+ is CreateCredentialInterruptedException -> {
22
+ Log.d("CredentialManager", "Retry process")
23
+ }
24
+ is CreateCredentialProviderConfigurationException -> {
25
+ Log.d("CredentialManager", "Missing provider configuration")
26
+ }
27
+ is CreateCredentialUnknownException -> {
28
+ Log.d("CredentialManager", "Unknown error")
29
+ }
30
+ is CreateCredentialCustomException -> {
31
+ Log.d("CredentialManager", "Custom credential error")
32
+ }
33
+ else -> Log.w("CredentialManager", "Unexpected exception type ${e::class.java.name}")
34
+ }
35
+ }
36
+ }
@@ -0,0 +1,71 @@
1
+ package com.credentialsmanager
2
+ import androidx.credentials.CredentialManager
3
+ import androidx.credentials.exceptions.CreateCredentialException
4
+ import com.credentialsmanager.handlers.CredentialHandler
5
+ import com.credentialsmanager.handlers.ErrorHandler
6
+ import com.facebook.react.bridge.Promise
7
+ import com.facebook.react.bridge.ReactApplicationContext
8
+ import com.facebook.react.bridge.ReadableMap
9
+ import kotlinx.coroutines.CoroutineScope
10
+ import kotlinx.coroutines.Dispatchers
11
+ import kotlinx.coroutines.launch
12
+
13
+ class CredentialsManagerModule(
14
+ reactContext: ReactApplicationContext,
15
+ ) : NativeCredentialsManagerSpec(reactContext) {
16
+ private val coroutineScope = CoroutineScope(Dispatchers.IO)
17
+ val credentialManager = CredentialManager.create(getReactApplicationContext())
18
+ private val credentialHandler = CredentialHandler(reactContext)
19
+
20
+ private var implementation: CredentialsManagerModuleImpl = CredentialsManagerModuleImpl()
21
+
22
+ override fun getName(): String = CredentialsManagerModuleImpl.NAME
23
+
24
+ override fun signUpWithPasskeys(
25
+ requestJson: ReadableMap,
26
+ preferImmediatelyAvailableCredentials: Boolean,
27
+ promise: Promise,
28
+ ) {
29
+ val jsonString = requestJson.toString()
30
+
31
+ coroutineScope.launch {
32
+ try {
33
+ val response =
34
+ credentialHandler.createPasskey(
35
+ jsonString,
36
+ preferImmediatelyAvailableCredentials,
37
+ )
38
+
39
+ response?.let {
40
+ promise.resolve(it)
41
+ } ?: promise.reject("ERROR", "No response received")
42
+ } catch (e: CreateCredentialException) {
43
+ ErrorHandler.handleCredentialError(e)
44
+ promise.reject("ERROR", e.message.toString())
45
+ }
46
+ }
47
+ }
48
+
49
+ override fun signUpWithPassword(credObject: ReadableMap) {
50
+ val username = credObject.getString("username") ?: ""
51
+ val password = credObject.getString("password") ?: ""
52
+ coroutineScope.launch {
53
+ try {
54
+ credentialHandler.createPassword(username, password)
55
+ } catch (e: CreateCredentialException) {
56
+ ErrorHandler.handleCredentialError(e)
57
+ }
58
+ }
59
+ }
60
+
61
+ override fun signInWithSavedCredentials(
62
+ requestJson: ReadableMap,
63
+ promise: Promise,
64
+ ) {
65
+ val jsonString = requestJson.toString()
66
+ coroutineScope.launch {
67
+ val data = credentialHandler.getSavedCredentials(jsonString)
68
+ promise.resolve(data)
69
+ }
70
+ }
71
+ }
@@ -0,0 +1,77 @@
1
+ package com.credentialsmanager
2
+ import androidx.credentials.CredentialManager
3
+ import androidx.credentials.exceptions.CreateCredentialException
4
+ import com.credentialsmanager.handlers.CredentialHandler
5
+ import com.credentialsmanager.handlers.ErrorHandler
6
+ import com.facebook.react.bridge.Promise
7
+ import com.facebook.react.bridge.ReactApplicationContext
8
+ import com.facebook.react.bridge.ReactContextBaseJavaModule
9
+ import com.facebook.react.bridge.ReadableMap
10
+ import com.facebook.react.module.annotations.ReactModule
11
+ import kotlinx.coroutines.CoroutineScope
12
+ import kotlinx.coroutines.Dispatchers
13
+ import kotlinx.coroutines.launch
14
+
15
+ @ReactModule(name = CredentialsManagerModuleImpl.NAME)
16
+ class CredentialsManagerModule(
17
+ reactContext: ReactApplicationContext,
18
+ ) : ReactContextBaseJavaModule(reactContext) {
19
+ private val coroutineScope = CoroutineScope(Dispatchers.IO)
20
+ val credentialManager = CredentialManager.create(getReactApplicationContext())
21
+ private val credentialHandler = CredentialHandler(reactContext)
22
+
23
+ private var implementation: CredentialsManagerModuleImpl = CredentialsManagerModuleImpl()
24
+
25
+ override fun getName(): String = CredentialsManagerModuleImpl.NAME
26
+
27
+ @ReactMethod
28
+ fun signUpWithPasskeys(
29
+ requestJson: ReadableMap,
30
+ preferImmediatelyAvailableCredentials: Boolean,
31
+ promise: Promise,
32
+ ) {
33
+ val jsonString = requestJson.toString()
34
+
35
+ coroutineScope.launch {
36
+ try {
37
+ val response =
38
+ credentialHandler.createPasskey(
39
+ jsonString,
40
+ preferImmediatelyAvailableCredentials,
41
+ )
42
+
43
+ response?.let {
44
+ promise.resolve(it)
45
+ } ?: promise.reject("ERROR", "No response received")
46
+ } catch (e: CreateCredentialException) {
47
+ ErrorHandler.handleCredentialError(e)
48
+ promise.reject("ERROR", e.message.toString())
49
+ }
50
+ }
51
+ }
52
+
53
+ @ReactMethod
54
+ fun signUpWithPassword(credObject: ReadableMap) {
55
+ val username = credObject.getString("username") ?: ""
56
+ val password = credObject.getString("password") ?: ""
57
+ coroutineScope.launch {
58
+ try {
59
+ credentialHandler.createPassword(username, password)
60
+ } catch (e: CreateCredentialException) {
61
+ ErrorHandler.handleCredentialError(e)
62
+ }
63
+ }
64
+ }
65
+
66
+ @ReactMethod
67
+ fun signInWithSavedCredentials(
68
+ requestJson: ReadableMap,
69
+ promise: Promise,
70
+ ) {
71
+ val jsonString = requestJson.toString()
72
+ coroutineScope.launch {
73
+ val data = credentialHandler.getSavedCredentials(jsonString)
74
+ promise.resolve(data)
75
+ }
76
+ }
77
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-credentials-manager",
3
- "version": "0.2.1",
3
+ "version": "0.3.0",
4
4
  "description": "A React Native library that implements the Credential Manager API for Android. This library allows you to manage passwords and passkeys in your React Native applications.",
5
5
  "source": "./src/index.tsx",
6
6
  "main": "./lib/commonjs/index.js",
@@ -1,212 +0,0 @@
1
- package com.credentialsmanager
2
-
3
- import android.util.Log
4
- import androidx.credentials.CreatePasswordRequest
5
- import androidx.credentials.CreatePublicKeyCredentialRequest
6
- import androidx.credentials.CreatePublicKeyCredentialResponse
7
- import androidx.credentials.CredentialManager
8
- import androidx.credentials.CustomCredential
9
- import androidx.credentials.GetCredentialRequest
10
- import androidx.credentials.GetPasswordOption
11
- import androidx.credentials.GetPublicKeyCredentialOption
12
- import androidx.credentials.PasswordCredential
13
- import androidx.credentials.PublicKeyCredential
14
- import androidx.credentials.exceptions.CreateCredentialCancellationException
15
- import androidx.credentials.exceptions.CreateCredentialCustomException
16
- import androidx.credentials.exceptions.CreateCredentialException
17
- import androidx.credentials.exceptions.CreateCredentialInterruptedException
18
- import androidx.credentials.exceptions.CreateCredentialProviderConfigurationException
19
- import androidx.credentials.exceptions.CreateCredentialUnknownException
20
- import androidx.credentials.exceptions.publickeycredential.CreatePublicKeyCredentialDomException
21
- import com.facebook.react.bridge.Arguments
22
- import com.facebook.react.bridge.Promise
23
- import com.facebook.react.bridge.ReactApplicationContext
24
- import com.facebook.react.bridge.ReadableMap
25
- import com.facebook.react.module.annotations.ReactModule
26
- import kotlinx.coroutines.CoroutineScope
27
- import kotlinx.coroutines.Dispatchers
28
- import kotlinx.coroutines.launch
29
- import org.json.JSONObject
30
-
31
- @ReactModule(name = CredentialsManagerModule.NAME)
32
- class CredentialsManagerModule(
33
- reactContext: ReactApplicationContext,
34
- ) : NativeCredentialsManagerSpec(reactContext) {
35
- private val coroutineScope = CoroutineScope(Dispatchers.IO)
36
- val credentialManager = CredentialManager.create(getReactApplicationContext())
37
-
38
- override fun getName(): String = NAME
39
-
40
- override fun signUpWithPasskeys(
41
- requestJson: ReadableMap,
42
- preferImmediatelyAvailableCredentials: Boolean,
43
- promise: Promise,
44
- ) {
45
- val jsonString = requestJson.toString()
46
-
47
- val request =
48
- CreatePublicKeyCredentialRequest(
49
- requestJson = jsonString,
50
- preferImmediatelyAvailableCredentials = preferImmediatelyAvailableCredentials,
51
- )
52
-
53
- coroutineScope.launch {
54
- try {
55
- val response =
56
- credentialManager.createCredential(
57
- getReactApplicationContext(),
58
- request,
59
- ) as CreatePublicKeyCredentialResponse
60
-
61
- response?.let {
62
- val dataBundle = it.data
63
- val responseJson = dataBundle.getString("androidx.credentials.BUNDLE_KEY_REGISTRATION_RESPONSE_JSON")
64
- responseJson?.let { json ->
65
-
66
- val jsonObject = Arguments.createMap()
67
- val parsedObject = JSONObject(json)
68
-
69
- parsedObject.keys().forEach { key ->
70
- jsonObject.putString(key, parsedObject.getString(key))
71
- }
72
-
73
- Log.d("CredentialManager", "Passkey creation result: $jsonObject")
74
- promise.resolve(jsonObject)
75
- } ?: run {
76
- promise.reject("ERROR", "Response JSON is null")
77
- }
78
- } ?: run {
79
- promise.reject("ERROR", "No response received")
80
- }
81
- } catch (e: CreateCredentialException) {
82
- handleFailure(e)
83
- promise.reject("ERROR", e.message.toString())
84
- }
85
- }
86
- }
87
-
88
- override fun signUpWithPassword(credObject: ReadableMap) {
89
- val username = credObject.getString("username") ?: ""
90
- val password = credObject.getString("password") ?: ""
91
- coroutineScope.launch {
92
- createPassword(username, password)
93
- }
94
- }
95
-
96
- override fun signInWithSavedCredentials(
97
- requestJson: ReadableMap,
98
- promise: Promise,
99
- ) {
100
- val jsonString = requestJson.toString()
101
- coroutineScope.launch {
102
- val data = getSavedCredentials(jsonString)
103
- promise.resolve(data)
104
- }
105
- }
106
-
107
- private suspend fun getSavedCredentials(jsonString: String): ReadableMap? {
108
- val getPublicKeyCredentialOption =
109
- GetPublicKeyCredentialOption(jsonString, null)
110
-
111
- val getPasswordOption = GetPasswordOption()
112
-
113
- val result =
114
- try {
115
- credentialManager.getCredential(
116
- getReactApplicationContext(),
117
- GetCredentialRequest(
118
- listOf(
119
- getPublicKeyCredentialOption,
120
- getPasswordOption,
121
- ),
122
- ),
123
- )
124
- } catch (e: Exception) {
125
- Log.e("CredentialManager", "getCredential failed with exception: " + e.message.toString())
126
-
127
- return null
128
- }
129
-
130
- val resultMap = Arguments.createMap()
131
- when (result.credential) {
132
- is PublicKeyCredential -> {
133
- val cred = result.credential as PublicKeyCredential
134
- resultMap.putString("type", "passkey")
135
- resultMap.putString("authenticationResponseJson", cred.authenticationResponseJson)
136
- Log.d("CredentialManager", "Passkey: ${cred.authenticationResponseJson}")
137
- }
138
- is PasswordCredential -> {
139
- val cred = result.credential as PasswordCredential
140
- resultMap.putString("type", "password")
141
- resultMap.putString("username", cred.id)
142
- resultMap.putString("password", cred.password)
143
- Log.d("CredentialManager", "Got Password - User:${cred.id} Password: ${cred.password}")
144
- }
145
- is CustomCredential -> {
146
- return null
147
- }
148
- else -> return null
149
- }
150
-
151
- return resultMap
152
- }
153
-
154
- private suspend fun createPassword(
155
- username: String,
156
- password: String,
157
- ) {
158
- val createPasswordRequest =
159
- CreatePasswordRequest(id = username, password = password)
160
-
161
- try {
162
- val result =
163
- credentialManager.createCredential(
164
- getReactApplicationContext(),
165
- createPasswordRequest,
166
- )
167
- } catch (e: CreateCredentialException) {
168
- handleFailure(e)
169
- }
170
- }
171
-
172
- private fun handleFailure(e: CreateCredentialException) {
173
- when (e) {
174
- is CreatePublicKeyCredentialDomException -> {
175
- // Handle the passkey DOM errors thrown according to the
176
- // WebAuthn spec.
177
- Log.d("CredentialManager", "passkey DOM errors")
178
- // handlePasskeyError(e.domError)
179
- }
180
- is CreateCredentialCancellationException -> {
181
- // The user intentionally canceled the operation and chose not
182
- // to register the credential.
183
- Log.d("CredentialManager", "User cancelled")
184
- }
185
- is CreateCredentialInterruptedException -> {
186
- // Retry-able error. Consider retrying the call.
187
- Log.d("CredentialManager", "Retry process")
188
- }
189
- is CreateCredentialProviderConfigurationException -> {
190
- // Your app is missing the provider configuration dependency.
191
- // Most likely, you're missing the
192
- // "credentials-play-services-auth" module.
193
- }
194
- is CreateCredentialUnknownException -> {
195
- Log.d("CredentialManager", "Unknown error")
196
- }
197
- is CreateCredentialCustomException -> {
198
- // You have encountered an error from a 3rd-party SDK. If you
199
- // make the API call with a request object that's a subclass of
200
- // CreateCustomCredentialRequest using a 3rd-party SDK, then you
201
- // should check for any custom exception type constants within
202
- // that SDK to match with e.type. Otherwise, drop or log the
203
- // exception.
204
- }
205
- else -> Log.w("CredentialManager", "Unexpected exception type ${e::class.java.name}")
206
- }
207
- }
208
-
209
- companion object {
210
- const val NAME = "CredentialsManager"
211
- }
212
- }