Skip to content

Commit 2c82e40

Browse files
Add push credential update (AUT-3560) (#101)
* Add push credential update (AUT-3560) Add push.updateCredential(pushToken) bridging the native iOS/Android SDKs to refresh a push credential's token. Bump 2.14.0 -> 2.15.0 and the native deps (iOS 2.11.0, Android 3.12.0). Add an example button. * Clarify push token source in example updateCredential * Align updateCredential return shape with native SDKs The iOS/Android SDKs now return the update response (userAuthenticatorId, userId, lastVerifiedAt, pushToken) instead of a faked AppCredential. Mirror that here: add UpdatedAppCredential type, remap both native bridges, update the example. Also tidy the Android bridge null-handling.
1 parent f52566a commit 2c82e40

10 files changed

Lines changed: 127 additions & 10 deletions

File tree

android/build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ dependencies {
6666

6767
implementation "androidx.browser:browser:1.2.0"
6868

69-
implementation("com.authsignal:authsignal-android:4.0.0")
69+
implementation("com.authsignal:authsignal-android:4.1.0")
7070

7171
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.8.1")
7272
}

android/src/main/java/com/authsignal/react/AuthsignalPushModule.kt

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,30 @@ class AuthsignalPushModule(private val reactContext: ReactApplicationContext) :
156156
}
157157
}
158158

159+
@ReactMethod
160+
override fun updateCredential(pushToken: String, promise: Promise) {
161+
launch(promise) {
162+
val response = it.updateCredential(pushToken)
163+
164+
val credential = response.data
165+
166+
if (response.error != null) {
167+
val errorCode = response.errorCode ?: defaultError
168+
169+
promise.reject(errorCode, response.error)
170+
} else if (credential != null) {
171+
val map = Arguments.createMap()
172+
map.putString("userAuthenticatorId", credential.userAuthenticatorId)
173+
map.putString("userId", credential.userId)
174+
map.putString("lastVerifiedAt", credential.lastVerifiedAt)
175+
map.putString("pushToken", credential.pushToken)
176+
promise.resolve(map)
177+
} else {
178+
promise.resolve(null)
179+
}
180+
}
181+
}
182+
159183
private fun launch(promise: Promise, fn: suspend (client: AuthsignalPush) -> Unit) {
160184
coroutineScope.launch {
161185
authsignal?.let {

example/src/screens/HomeScreen.tsx

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -712,6 +712,29 @@ export function HomeScreen() {
712712
}
713713
};
714714

715+
const updatePushCredential = async () => {
716+
const authsignal = authsignalRef.current;
717+
if (!authsignal) return;
718+
719+
try {
720+
// Pass the device's current FCM (Android) / APNs (iOS) push token, e.g. from
721+
// messaging().getToken(). This example uses a placeholder to demonstrate the call.
722+
const pushToken = `example-push-token-${Date.now()}`;
723+
724+
addOutput('Updating push credential...');
725+
const response = await authsignal.push.updateCredential(pushToken);
726+
727+
if (response.data) {
728+
addOutput('Push credential updated');
729+
addOutput(` Credential ID: ${response.data.userAuthenticatorId}`);
730+
} else {
731+
addOutput(`Failed to update push credential: ${response.error}`);
732+
}
733+
} catch (e) {
734+
addOutput(`Error: ${e}`);
735+
}
736+
};
737+
715738
const ActionButton = ({
716739
title,
717740
onPress,
@@ -831,6 +854,11 @@ export function HomeScreen() {
831854
onPress={() => updatePushChallenge(false)}
832855
disabled={!isInitialized || !lastPushChallengeId}
833856
/>
857+
<ActionButton
858+
title="Update Credential"
859+
onPress={updatePushCredential}
860+
disabled={!isInitialized}
861+
/>
834862
<ActionButton
835863
title="Remove"
836864
onPress={removePushCredential}

ios/AuthsignalPushModule.m

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,4 +31,8 @@ @interface RCT_EXTERN_MODULE(AuthsignalPushModule, NSObject)
3131
resolve:(RCTPromiseResolveBlock)resolve
3232
reject:(RCTPromiseRejectBlock)reject)
3333

34+
RCT_EXTERN_METHOD(updateCredential:(NSString)pushToken
35+
resolve:(RCTPromiseResolveBlock)resolve
36+
reject:(RCTPromiseRejectBlock)reject)
37+
3438
@end

ios/AuthsignalPushModule.swift

Lines changed: 39 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -82,19 +82,21 @@ class AuthsignalPushModule: NSObject {
8282

8383
if let error = response.error {
8484
reject(response.errorCode ?? "unexpected_error", error, nil)
85-
} else {
85+
} else if let data = response.data {
8686
let credential: [String: String?] = [
87-
"credentialId": response.data!.credentialId,
88-
"createdAt": response.data!.createdAt,
89-
"userId": response.data!.userId,
90-
"lastAuthenticatedAt": response.data!.lastAuthenticatedAt,
87+
"credentialId": data.credentialId,
88+
"createdAt": data.createdAt,
89+
"userId": data.userId,
90+
"lastAuthenticatedAt": data.lastAuthenticatedAt,
9191
]
92-
92+
9393
resolve(credential)
94+
} else {
95+
resolve(nil)
9496
}
9597
}
9698
}
97-
99+
98100
@objc func removeCredential(
99101
_ resolve: @escaping RCTPromiseResolveBlock,
100102
reject: @escaping RCTPromiseRejectBlock
@@ -179,6 +181,36 @@ class AuthsignalPushModule: NSObject {
179181
}
180182
}
181183

184+
@objc func updateCredential(
185+
_ pushToken: NSString,
186+
resolve: @escaping RCTPromiseResolveBlock,
187+
reject: @escaping RCTPromiseRejectBlock
188+
) -> Void {
189+
guard let authsignal = authsignal else {
190+
resolve(nil)
191+
return
192+
}
193+
194+
Task.init {
195+
let response = await authsignal.updateCredential(pushToken: pushToken as String)
196+
197+
if let error = response.error {
198+
reject(response.errorCode ?? "unexpected_error", error, nil)
199+
} else if let data = response.data {
200+
let credential: [String: String?] = [
201+
"userAuthenticatorId": data.userAuthenticatorId,
202+
"userId": data.userId,
203+
"lastVerifiedAt": data.lastVerifiedAt,
204+
"pushToken": data.pushToken,
205+
]
206+
207+
resolve(credential)
208+
} else {
209+
resolve(nil)
210+
}
211+
}
212+
}
213+
182214
func getKeychainAccess(value: String?) -> KeychainAccess {
183215
switch value {
184216
case "afterFirstUnlock":

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "react-native-authsignal",
3-
"version": "3.0.0",
3+
"version": "3.1.0",
44
"description": "The official Authsignal React Native library.",
55
"main": "lib/commonjs/index",
66
"module": "lib/module/index",

react-native-authsignal.podspec

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ Pod::Spec.new do |s|
1818
s.private_header_files = "ios/**/*.h"
1919

2020
s.dependency "React-Core"
21-
s.dependency 'Authsignal', '~> 2.10.0'
21+
s.dependency 'Authsignal', '~> 2.11.0'
2222

2323
# Use install_modules_dependencies helper to install the dependencies if React Native version >=0.71.0.
2424
# See https://github.com/facebook/react-native/blob/febf6b7f33fdb4904669f99d795eba4c0f95d7bf/scripts/cocoapods/new_architecture.rb#L79.

src/NativeAuthsignalPushModule.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ export interface Spec extends TurboModule {
1717
withApproval: boolean,
1818
withVerificationCode: string | null
1919
): Promise<boolean>;
20+
updateCredential(pushToken: string): Promise<Object | null>;
2021
}
2122

2223
export default TurboModuleRegistry.get<Spec>('AuthsignalPushModule');

src/push.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import type {
99
AppCredential,
1010
AuthsignalResponse,
1111
UpdateChallengeInput,
12+
UpdatedAppCredential,
1213
} from './types';
1314

1415
interface ConstructorArgs {
@@ -139,6 +140,26 @@ export class AuthsignalPush {
139140
}
140141
}
141142

143+
async updateCredential(
144+
pushToken: string
145+
): Promise<AuthsignalResponse<UpdatedAppCredential>> {
146+
await this.ensureModuleIsInitialized();
147+
148+
try {
149+
const data = (await AuthsignalPushModule.updateCredential(
150+
pushToken
151+
)) as UpdatedAppCredential;
152+
153+
return { data };
154+
} catch (ex) {
155+
if (this.enableLogging) {
156+
console.log(ex);
157+
}
158+
159+
return handleErrorCodes(ex);
160+
}
161+
}
162+
142163
private async ensureModuleIsInitialized() {
143164
if (this.initialized) {
144165
return;

src/types.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,13 @@ export interface AppCredential {
7575
lastAuthenticatedAt?: string;
7676
}
7777

78+
export interface UpdatedAppCredential {
79+
userAuthenticatorId: string;
80+
userId: string;
81+
lastVerifiedAt: string;
82+
pushToken: string;
83+
}
84+
7885
export interface ClaimChallengeInput {
7986
challengeId: string;
8087
}

0 commit comments

Comments
 (0)