Skip to content

Commit

Permalink
feat(firebase_auth): add custom auth domain setter to Firebase Auth (#…
Browse files Browse the repository at this point in the history
…12218)

Co-authored-by: russellwheatley <russellwheatley85@gmail.com>
  • Loading branch information
Andrey-K-Calven and russellwheatley committed Mar 26, 2024
1 parent b12d850 commit e129780
Show file tree
Hide file tree
Showing 15 changed files with 161 additions and 9 deletions.
Expand Up @@ -142,6 +142,11 @@ static FirebaseAuth getAuthFromPigeon(
auth.setCustomAuthDomain(customDomain);
}

// Auth's `getCustomAuthDomain` supersedes value from `customAuthDomain` map set by `initializeApp`
if (pigeonApp.getCustomAuthDomain() != null) {
auth.setCustomAuthDomain(pigeonApp.getCustomAuthDomain());
}

return auth;
}

Expand Down
Expand Up @@ -363,6 +363,16 @@ public void setTenantId(@Nullable String setterArg) {
this.tenantId = setterArg;
}

private @Nullable String customAuthDomain;

public @Nullable String getCustomAuthDomain() {
return customAuthDomain;
}

public void setCustomAuthDomain(@Nullable String setterArg) {
this.customAuthDomain = setterArg;
}

/** Constructor is non-public to enforce null safety; use Builder. */
AuthPigeonFirebaseApp() {}

Expand All @@ -382,19 +392,28 @@ public static final class Builder {
return this;
}

private @Nullable String customAuthDomain;

public @NonNull Builder setCustomAuthDomain(@Nullable String setterArg) {
this.customAuthDomain = setterArg;
return this;
}

public @NonNull AuthPigeonFirebaseApp build() {
AuthPigeonFirebaseApp pigeonReturn = new AuthPigeonFirebaseApp();
pigeonReturn.setAppName(appName);
pigeonReturn.setTenantId(tenantId);
pigeonReturn.setCustomAuthDomain(customAuthDomain);
return pigeonReturn;
}
}

@NonNull
ArrayList<Object> toList() {
ArrayList<Object> toListResult = new ArrayList<Object>(2);
ArrayList<Object> toListResult = new ArrayList<Object>(3);
toListResult.add(appName);
toListResult.add(tenantId);
toListResult.add(customAuthDomain);
return toListResult;
}

Expand All @@ -404,6 +423,8 @@ ArrayList<Object> toList() {
pigeonResult.setAppName((String) appName);
Object tenantId = list.get(1);
pigeonResult.setTenantId((String) tenantId);
Object customAuthDomain = list.get(2);
pigeonResult.setCustomAuthDomain((String) customAuthDomain);
return pigeonResult;
}
}
Expand Down
Expand Up @@ -635,6 +635,10 @@ - (FIRAuth *_Nullable)getFIRAuthFromAppNameFromPigeon:(AuthPigeonFirebaseApp *)p

auth.tenantID = pigeonApp.tenantId;
auth.customAuthDomain = [FLTFirebaseCorePlugin getCustomDomain:app.name];
// Auth's `customAuthDomain` supersedes value from `getCustomDomain` set by `initializeApp`
if (pigeonApp.customAuthDomain != nil) {
auth.customAuthDomain = pigeonApp.customAuthDomain;
}

return auth;
}
Expand Down
Expand Up @@ -91,9 +91,12 @@ typedef NS_ENUM(NSUInteger, ActionCodeInfoOperation) {
@interface AuthPigeonFirebaseApp : NSObject
/// `init` unavailable to enforce nonnull fields, see the `make` class method.
- (instancetype)init NS_UNAVAILABLE;
+ (instancetype)makeWithAppName:(NSString *)appName tenantId:(nullable NSString *)tenantId;
+ (instancetype)makeWithAppName:(NSString *)appName
tenantId:(nullable NSString *)tenantId
customAuthDomain:(nullable NSString *)customAuthDomain;
@property(nonatomic, copy) NSString *appName;
@property(nonatomic, copy, nullable) NSString *tenantId;
@property(nonatomic, copy, nullable) NSString *customAuthDomain;
@end

@interface PigeonActionCodeInfoData : NSObject
Expand Down
Expand Up @@ -238,17 +238,21 @@ - (NSArray *)toList {
@end

@implementation AuthPigeonFirebaseApp
+ (instancetype)makeWithAppName:(NSString *)appName tenantId:(nullable NSString *)tenantId {
+ (instancetype)makeWithAppName:(NSString *)appName
tenantId:(nullable NSString *)tenantId
customAuthDomain:(nullable NSString *)customAuthDomain {
AuthPigeonFirebaseApp *pigeonResult = [[AuthPigeonFirebaseApp alloc] init];
pigeonResult.appName = appName;
pigeonResult.tenantId = tenantId;
pigeonResult.customAuthDomain = customAuthDomain;
return pigeonResult;
}
+ (AuthPigeonFirebaseApp *)fromList:(NSArray *)list {
AuthPigeonFirebaseApp *pigeonResult = [[AuthPigeonFirebaseApp alloc] init];
pigeonResult.appName = GetNullableObjectAtIndex(list, 0);
NSAssert(pigeonResult.appName != nil, @"");
pigeonResult.tenantId = GetNullableObjectAtIndex(list, 1);
pigeonResult.customAuthDomain = GetNullableObjectAtIndex(list, 2);
return pigeonResult;
}
+ (nullable AuthPigeonFirebaseApp *)nullableFromList:(NSArray *)list {
Expand All @@ -258,6 +262,7 @@ - (NSArray *)toList {
return @[
(self.appName ?: [NSNull null]),
(self.tenantId ?: [NSNull null]),
(self.customAuthDomain ?: [NSNull null]),
];
}
@end
Expand Down
29 changes: 29 additions & 0 deletions packages/firebase_auth/firebase_auth/lib/src/firebase_auth.dart
Expand Up @@ -157,6 +157,35 @@ class FirebaseAuth extends FirebasePluginPlatform {
_delegate.tenantId = tenantId;
}

/// The current Auth instance's custom auth domain.
/// The auth domain used to handle redirects from OAuth provides, for example
/// "my-awesome-app.firebaseapp.com". By default, this is set to `null` and
/// default auth domain is used.
///
/// If not `null`, this value will supersede `authDomain` property set in `initializeApp`.
String? get customAuthDomain {
return _delegate.customAuthDomain;
}

/// Set the current Auth instance's auth domain for apple and android platforms.
/// The auth domain used to handle redirects from OAuth provides, for example
/// "my-awesome-app.firebaseapp.com". By default, this is set to `null` and
/// default auth domain is used.
///
/// If not `null`, this value will supersede `authDomain` property set in `initializeApp`.
set customAuthDomain(String? customAuthDomain) {
// Web and windows do not support setting custom auth domains on the auth instance
if (defaultTargetPlatform == TargetPlatform.windows || kIsWeb) {
final message = defaultTargetPlatform == TargetPlatform.windows
? 'Cannot set custom auth domain on a FirebaseAuth instance for windows platform'
: 'Cannot set custom auth domain on a FirebaseAuth instance. Set the custom auth domain on `FirebaseOptions.authDomain` instance and pass into `Firebase.initializeApp()` instead.';
throw UnimplementedError(
message,
);
}
_delegate.customAuthDomain = customAuthDomain;
}

/// Applies a verification code sent to the user by email or other out-of-band
/// mechanism.
///
Expand Down
Expand Up @@ -10,6 +10,7 @@ import 'package:firebase_auth/firebase_auth.dart';
import 'package:firebase_auth_platform_interface/firebase_auth_platform_interface.dart';
import 'package:firebase_auth_platform_interface/src/method_channel/method_channel_firebase_auth.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:mockito/mockito.dart';
import 'package:plugin_platform_interface/plugin_platform_interface.dart';
Expand Down Expand Up @@ -230,6 +231,37 @@ void main() {
});
});

group('customAuthDomain', () {
test('set customAuthDomain should call delegate method', () async {
// Each test uses a unique FirebaseApp instance to avoid sharing state
final app = await Firebase.initializeApp(
name: 'customAuthDomainTest',
options: const FirebaseOptions(
apiKey: 'apiKey',
appId: 'appId',
messagingSenderId: 'messagingSenderId',
projectId: 'projectId'));

FirebaseAuthPlatform.instance =
FakeFirebaseAuthPlatform(customAuthDomain: 'foo');
auth = FirebaseAuth.instanceFor(app: app);

expect(auth.customAuthDomain, 'foo');
if (defaultTargetPlatform == TargetPlatform.windows || kIsWeb) {
try {
auth.customAuthDomain = 'bar';
} on UnimplementedError catch (e) {
expect(e.message, contains('Cannot set auth domain'));
}
} else {
auth.customAuthDomain = 'bar';

expect(auth.customAuthDomain, 'bar');
expect(FirebaseAuthPlatform.instance.customAuthDomain, 'bar');
}
});
});

group('languageCode', () {
test('.languageCode should call delegate method', () {
auth.languageCode;
Expand Down Expand Up @@ -1026,11 +1058,14 @@ class MockFirebaseAuth extends Mock
class FakeFirebaseAuthPlatform extends Fake
with MockPlatformInterfaceMixin
implements FirebaseAuthPlatform {
FakeFirebaseAuthPlatform({this.tenantId});
FakeFirebaseAuthPlatform({this.tenantId, this.customAuthDomain});

@override
String? tenantId;

@override
String? customAuthDomain;

@override
FirebaseAuthPlatform delegateFor(
{required FirebaseApp app, Persistence? persistence}) {
Expand Down
33 changes: 29 additions & 4 deletions packages/firebase_auth/firebase_auth/windows/messages.g.cpp
Expand Up @@ -200,11 +200,15 @@ PigeonMultiFactorInfo PigeonMultiFactorInfo::FromEncodableList(
AuthPigeonFirebaseApp::AuthPigeonFirebaseApp(const std::string& app_name)
: app_name_(app_name) {}

AuthPigeonFirebaseApp::AuthPigeonFirebaseApp(const std::string& app_name,
const std::string* tenant_id)
AuthPigeonFirebaseApp::AuthPigeonFirebaseApp(
const std::string& app_name, const std::string* tenant_id,
const std::string* custom_auth_domain)
: app_name_(app_name),
tenant_id_(tenant_id ? std::optional<std::string>(*tenant_id)
: std::nullopt) {}
: std::nullopt),
custom_auth_domain_(custom_auth_domain
? std::optional<std::string>(*custom_auth_domain)
: std::nullopt) {}

const std::string& AuthPigeonFirebaseApp::app_name() const { return app_name_; }

Expand All @@ -225,11 +229,27 @@ void AuthPigeonFirebaseApp::set_tenant_id(std::string_view value_arg) {
tenant_id_ = value_arg;
}

const std::string* AuthPigeonFirebaseApp::custom_auth_domain() const {
return custom_auth_domain_ ? &(*custom_auth_domain_) : nullptr;
}

void AuthPigeonFirebaseApp::set_custom_auth_domain(
const std::string_view* value_arg) {
custom_auth_domain_ =
value_arg ? std::optional<std::string>(*value_arg) : std::nullopt;
}

void AuthPigeonFirebaseApp::set_custom_auth_domain(std::string_view value_arg) {
custom_auth_domain_ = value_arg;
}

EncodableList AuthPigeonFirebaseApp::ToEncodableList() const {
EncodableList list;
list.reserve(2);
list.reserve(3);
list.push_back(EncodableValue(app_name_));
list.push_back(tenant_id_ ? EncodableValue(*tenant_id_) : EncodableValue());
list.push_back(custom_auth_domain_ ? EncodableValue(*custom_auth_domain_)
: EncodableValue());
return list;
}

Expand All @@ -240,6 +260,11 @@ AuthPigeonFirebaseApp AuthPigeonFirebaseApp::FromEncodableList(
if (!encodable_tenant_id.IsNull()) {
decoded.set_tenant_id(std::get<std::string>(encodable_tenant_id));
}
auto& encodable_custom_auth_domain = list[2];
if (!encodable_custom_auth_domain.IsNull()) {
decoded.set_custom_auth_domain(
std::get<std::string>(encodable_custom_auth_domain));
}
return decoded;
}

Expand Down
9 changes: 8 additions & 1 deletion packages/firebase_auth/firebase_auth/windows/messages.g.h
Expand Up @@ -213,7 +213,8 @@ class AuthPigeonFirebaseApp {

// Constructs an object setting all fields.
explicit AuthPigeonFirebaseApp(const std::string& app_name,
const std::string* tenant_id);
const std::string* tenant_id,
const std::string* custom_auth_domain);

const std::string& app_name() const;
void set_app_name(std::string_view value_arg);
Expand All @@ -222,6 +223,10 @@ class AuthPigeonFirebaseApp {
void set_tenant_id(const std::string_view* value_arg);
void set_tenant_id(std::string_view value_arg);

const std::string* custom_auth_domain() const;
void set_custom_auth_domain(const std::string_view* value_arg);
void set_custom_auth_domain(std::string_view value_arg);

private:
static AuthPigeonFirebaseApp FromEncodableList(
const flutter::EncodableList& list);
Expand All @@ -242,6 +247,7 @@ class AuthPigeonFirebaseApp {
friend class GenerateInterfacesCodecSerializer;
std::string app_name_;
std::optional<std::string> tenant_id_;
std::optional<std::string> custom_auth_domain_;
};

// Generated class from Pigeon that represents data sent in messages.
Expand Down Expand Up @@ -535,6 +541,7 @@ class PigeonUserDetails {

const flutter::EncodableList& provider_data() const;
void set_provider_data(const flutter::EncodableList& value_arg);

static PigeonUserDetails FromEncodableList(
const flutter::EncodableList& list);
flutter::EncodableList ToEncodableList() const;
Expand Down
Expand Up @@ -62,6 +62,7 @@ class MethodChannelFirebaseAuth extends FirebaseAuthPlatform {
return AuthPigeonFirebaseApp(
appName: app.name,
tenantId: tenantId,
customAuthDomain: customAuthDomain,
);
}

Expand Down
Expand Up @@ -19,6 +19,7 @@ class MethodChannelMultiFactor extends MultiFactorPlatform {
return AuthPigeonFirebaseApp(
appName: auth.app.name,
tenantId: auth.tenantId,
customAuthDomain: auth.customAuthDomain,
);
}

Expand Down
Expand Up @@ -25,6 +25,7 @@ class MethodChannelUser extends UserPlatform {
return AuthPigeonFirebaseApp(
appName: auth.app.name,
tenantId: auth.tenantId,
customAuthDomain: auth.customAuthDomain,
);
}

Expand Down
Expand Up @@ -128,16 +128,20 @@ class AuthPigeonFirebaseApp {
AuthPigeonFirebaseApp({
required this.appName,
this.tenantId,
this.customAuthDomain,
});

String appName;

String? tenantId;

String? customAuthDomain;

Object encode() {
return <Object?>[
appName,
tenantId,
customAuthDomain,
];
}

Expand All @@ -146,6 +150,7 @@ class AuthPigeonFirebaseApp {
return AuthPigeonFirebaseApp(
appName: result[0]! as String,
tenantId: result[1] as String?,
customAuthDomain: result[2] as String?,
);
}
}
Expand Down
Expand Up @@ -33,6 +33,14 @@ abstract class FirebaseAuthPlatform extends PlatformInterface {
/// parent project. By default, this is set to `null`.
String? tenantId;

/// Set the current Auth instance's auth domain for apple and android platforms.
/// The auth domain used to handle redirects from OAuth provides, for example
/// "my-awesome-app.firebaseapp.com". By default, this is set to `null` and
/// default auth domain is used.
///
/// If not `null`, this value will supersede `authDomain` property set in `initializeApp`.
String? customAuthDomain;

/// Create an instance using [app]
FirebaseAuthPlatform({this.appInstance}) : super(token: _token);

Expand Down
Expand Up @@ -66,10 +66,12 @@ class AuthPigeonFirebaseApp {
const AuthPigeonFirebaseApp({
required this.appName,
required this.tenantId,
required this.customAuthDomain,
});

final String appName;
final String? tenantId;
final String? customAuthDomain;
}

/// The type of operation that generated the action code from calling
Expand Down

0 comments on commit e129780

Please sign in to comment.