Skip to content

Commit b570287

Browse files
authored
[Auth] Alternative recaptcha approach (#14217)
1 parent d672490 commit b570287

File tree

3 files changed

+32
-93
lines changed

3 files changed

+32
-93
lines changed

FirebaseAuth/Sources/ObjC/FIRRecaptchaBridge.m

Lines changed: 12 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -18,83 +18,23 @@
1818
#import "FirebaseAuth/Sources/Public/FirebaseAuth/FIRRecaptchaBridge.h"
1919
#import "RecaptchaInterop/RecaptchaInterop.h"
2020

21-
// This is thread safe since it is only called by the AuthRecaptchaVerifier singleton.
22-
static id<RCARecaptchaClientProtocol> recaptchaClient;
23-
24-
static void retrieveToken(NSString *actionString,
25-
NSString *fakeToken,
26-
FIRAuthRecaptchaTokenCallback callback) {
27-
Class RecaptchaActionClass = NSClassFromString(@"RecaptchaEnterprise.RCAAction");
28-
SEL customActionSelector = NSSelectorFromString(@"initWithCustomAction:");
29-
if (!RecaptchaActionClass) {
30-
// Fall back to attempting to connect with pre-18.7.0 RecaptchaEnterprise.
31-
RecaptchaActionClass = NSClassFromString(@"RecaptchaAction");
32-
}
33-
34-
if (RecaptchaActionClass &&
35-
[RecaptchaActionClass instancesRespondToSelector:customActionSelector]) {
36-
// Initialize with a custom action
37-
id (*funcWithCustomAction)(id, SEL, NSString *) = (id(*)(
38-
id, SEL, NSString *))[RecaptchaActionClass instanceMethodForSelector:customActionSelector];
39-
40-
id<RCAActionProtocol> customAction =
41-
funcWithCustomAction([RecaptchaActionClass alloc], customActionSelector, actionString);
42-
if (customAction) {
43-
[recaptchaClient execute:customAction
44-
completion:^(NSString *_Nullable token, NSError *_Nullable error) {
45-
if (!error) {
46-
callback(token, nil, YES, YES);
47-
return;
48-
} else {
49-
callback(fakeToken, nil, YES, YES);
50-
}
51-
}];
52-
} else {
53-
// RecaptchaAction class creation failed.
54-
callback(@"", nil, YES, NO);
55-
}
56-
21+
Class<RCARecaptchaProtocol> _Nonnull __fir_castToRecaptchaProtocolFromClass(Class _Nonnull klass) {
22+
if ([klass conformsToProtocol:@protocol(RCARecaptchaProtocol)]) {
23+
NSLog(@"RCARecaptchaProtocol - true");
5724
} else {
58-
// RecaptchaEnterprise not linked.
59-
callback(@"", nil, NO, NO);
25+
NSLog(@"RCARecaptchaProtocol - false");
6026
}
27+
return (Class<RCARecaptchaProtocol>)klass;
6128
}
6229

63-
void FIRRecaptchaGetToken(NSString *siteKey,
64-
NSString *actionString,
65-
NSString *fakeToken,
66-
FIRAuthRecaptchaTokenCallback callback) {
67-
if (recaptchaClient != nil) {
68-
retrieveToken(actionString, fakeToken, callback);
69-
return;
70-
}
71-
72-
// Why not use `conformsToProtocol`?
73-
Class RecaptchaClass = NSClassFromString(@"RecaptchaEnterprise.RCARecaptcha");
74-
SEL selector = NSSelectorFromString(@"fetchClientWithSiteKey:completion:");
75-
if (!RecaptchaClass) {
76-
// Fall back to attempting to connect with pre-18.7.0 RecaptchaEnterprise.
77-
RecaptchaClass = NSClassFromString(@"Recaptcha");
78-
selector = NSSelectorFromString(@"getClientWithSiteKey:completion:");
79-
}
80-
81-
if (RecaptchaClass && [RecaptchaClass respondsToSelector:selector]) {
82-
void (*funcWithoutTimeout)(id, SEL, NSString *,
83-
void (^)(id<RCARecaptchaClientProtocol> _Nullable recaptchaClient,
84-
NSError *_Nullable error)) =
85-
(void *)[RecaptchaClass methodForSelector:selector];
86-
funcWithoutTimeout(RecaptchaClass, selector, siteKey,
87-
^(id<RCARecaptchaClientProtocol> _Nonnull client, NSError *_Nullable error) {
88-
if (error) {
89-
callback(@"", error, YES, YES);
90-
} else {
91-
recaptchaClient = client;
92-
retrieveToken(actionString, fakeToken, callback);
93-
}
94-
});
30+
Class<RCAActionProtocol> _Nonnull __fir_castToRecaptchaActionProtocolFromClass(
31+
Class _Nonnull klass) {
32+
if ([klass conformsToProtocol:@protocol(RCAActionProtocol)]) {
33+
NSLog(@"RCAActionProtocol - true");
9534
} else {
96-
// RecaptchaEnterprise not linked.
97-
callback(@"", nil, NO, NO);
35+
NSLog(@"RCAActionProtocol - false");
9836
}
37+
return (Class<RCAActionProtocol>)klass;
9938
}
39+
10040
#endif

FirebaseAuth/Sources/Public/FirebaseAuth/FIRRecaptchaBridge.h

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -17,17 +17,12 @@
1717

1818
#if TARGET_OS_IOS
1919

20-
typedef void (^FIRAuthRecaptchaTokenCallback)(NSString *_Nonnull token,
21-
NSError *_Nullable error,
22-
BOOL linked,
23-
BOOL recaptchaActionCreated);
20+
@protocol RCARecaptchaProtocol;
21+
@protocol RCAActionProtocol;
22+
23+
Class<RCARecaptchaProtocol> _Nonnull __fir_castToRecaptchaProtocolFromClass(Class _Nonnull klass);
24+
25+
Class<RCAActionProtocol> _Nonnull __fir_castToRecaptchaActionProtocolFromClass(
26+
Class _Nonnull klass);
2427

25-
// Provide a bridge to the Objective-C protocol provided by the optional Recaptcha Enterprise
26-
// dependency. Once the Recaptcha Enterprise provides a Swift interop protocol, this C and
27-
// Objective-C code can be converted to Swift. Casting to a Objective-C protocol does not seem
28-
// possible in Swift. The C API is a workaround for linkage problems with an Objective-C API.
29-
void FIRRecaptchaGetToken(NSString *_Nonnull siteKey,
30-
NSString *_Nonnull actionString,
31-
NSString *_Nonnull fakeToken,
32-
_Nonnull FIRAuthRecaptchaTokenCallback callback);
3328
#endif

FirebaseAuth/Sources/Swift/Utilities/AuthRecaptchaVerifier.swift

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,7 @@
177177
} else if let recaptcha = NSClassFromString("Recaptcha") {
178178
// Fall back to attempting to connect with pre-18.7.0 RecaptchaEnterprise.
179179
do {
180+
let recaptcha = __fir_castToRecaptchaProtocolFromClass(recaptcha)
180181
let client = try await recaptcha.getClient(withSiteKey: siteKey)
181182
recaptchaClient = client
182183
return await retrieveToken(actionString: actionString, fakeToken: fakeToken)
@@ -192,18 +193,21 @@
192193
private func retrieveToken(actionString: String,
193194
fakeToken: String) async -> (token: String, error: Error?,
194195
linked: Bool, actionCreated: Bool) {
195-
let recaptchaAction = (
196-
NSClassFromString("RecaptchaEnterprise.RCAAction") ?? NSClassFromString("RecaptchaAction")
197-
) as? RCAActionProtocol.Type
198-
199-
guard let recaptchaAction else {
196+
if let recaptchaAction =
197+
NSClassFromString("RecaptchaEnterprise.RCAAction") as? RCAActionProtocol.Type {
198+
let action = recaptchaAction.init(customAction: actionString)
199+
let token = try? await recaptchaClient!.execute(withAction: action)
200+
return (token ?? "NO_RECAPTCHA", nil, true, true)
201+
} else if let recaptchaAction = NSClassFromString("RecaptchaAction") {
202+
// Fall back to attempting to connect with pre-18.7.0 RecaptchaEnterprise.
203+
let recaptchaAction = __fir_castToRecaptchaActionProtocolFromClass(recaptchaAction)
204+
let action = recaptchaAction.init(customAction: actionString)
205+
let token = try? await recaptchaClient!.execute(withAction: action)
206+
return (token ?? "NO_RECAPTCHA", nil, true, true)
207+
} else {
200208
// RecaptchaEnterprise not linked.
201209
return ("", nil, false, false)
202210
}
203-
204-
let action = recaptchaAction.init(customAction: actionString)
205-
let token = try? await recaptchaClient!.execute(withAction: action)
206-
return (token ?? "NO_RECAPTCHA", nil, true, true)
207211
}
208212

209213
func retrieveRecaptchaConfig(forceRefresh: Bool) async throws {

0 commit comments

Comments
 (0)