Skip to content

Commit 4fa2918

Browse files
authored
feat(auth) Update ActionCodeSettings to support link_domain and deprecate dynamic_link_domain (#884)
* Add link_domain to ActionCodeSettings; update encode_action_code_settings to handle link_domain * Add handling for InvalidHostingLinkDomainError * Add deprecation warning for dynamic_link_domain * Update error message for InvalidHostingLinkDomainError * Fix lint * Add type hints to ActionCodeSettings * Fix f-string lint
1 parent 2c8a34a commit 4fa2918

File tree

4 files changed

+63
-4
lines changed

4 files changed

+63
-4
lines changed

firebase_admin/_auth_utils.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -324,6 +324,17 @@ def __init__(self, message, cause, http_response):
324324
exceptions.InvalidArgumentError.__init__(self, message, cause, http_response)
325325

326326

327+
class InvalidHostingLinkDomainError(exceptions.InvalidArgumentError):
328+
"""The provided hosting link domain is not configured in Firebase Hosting
329+
or is not owned by the current project."""
330+
331+
default_message = ('The provided hosting link domain is not configured in Firebase '
332+
'Hosting or is not owned by the current project')
333+
334+
def __init__(self, message, cause, http_response):
335+
exceptions.InvalidArgumentError.__init__(self, message, cause, http_response)
336+
337+
327338
class InvalidIdTokenError(exceptions.InvalidArgumentError):
328339
"""The provided ID token is not a valid Firebase ID token."""
329340

@@ -423,6 +434,7 @@ def __init__(self, message, cause=None, http_response=None):
423434
'EMAIL_NOT_FOUND': EmailNotFoundError,
424435
'INSUFFICIENT_PERMISSION': InsufficientPermissionError,
425436
'INVALID_DYNAMIC_LINK_DOMAIN': InvalidDynamicLinkDomainError,
437+
'INVALID_HOSTING_LINK_DOMAIN': InvalidHostingLinkDomainError,
426438
'INVALID_ID_TOKEN': InvalidIdTokenError,
427439
'PHONE_NUMBER_EXISTS': PhoneNumberAlreadyExistsError,
428440
'TENANT_NOT_FOUND': TenantNotFoundError,

firebase_admin/_user_mgt.py

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,9 @@
1717
import base64
1818
from collections import defaultdict
1919
import json
20+
from typing import Optional
2021
from urllib import parse
22+
import warnings
2123

2224
import requests
2325

@@ -489,15 +491,30 @@ class ActionCodeSettings:
489491
Used when invoking the email action link generation APIs.
490492
"""
491493

492-
def __init__(self, url, handle_code_in_app=None, dynamic_link_domain=None, ios_bundle_id=None,
493-
android_package_name=None, android_install_app=None, android_minimum_version=None):
494+
def __init__(
495+
self,
496+
url: str,
497+
handle_code_in_app: Optional[bool] = None,
498+
dynamic_link_domain: Optional[str] = None,
499+
ios_bundle_id: Optional[str] = None,
500+
android_package_name: Optional[str] = None,
501+
android_install_app: Optional[str] = None,
502+
android_minimum_version: Optional[str] = None,
503+
link_domain: Optional[str] = None,
504+
):
505+
if dynamic_link_domain is not None:
506+
warnings.warn(
507+
'dynamic_link_domain is deprecated, use link_domain instead',
508+
DeprecationWarning
509+
)
494510
self.url = url
495511
self.handle_code_in_app = handle_code_in_app
496512
self.dynamic_link_domain = dynamic_link_domain
497513
self.ios_bundle_id = ios_bundle_id
498514
self.android_package_name = android_package_name
499515
self.android_install_app = android_install_app
500516
self.android_minimum_version = android_minimum_version
517+
self.link_domain = link_domain
501518

502519

503520
def encode_action_code_settings(settings):
@@ -535,6 +552,13 @@ def encode_action_code_settings(settings):
535552
f'Invalid value provided for dynamic_link_domain: {settings.dynamic_link_domain}')
536553
parameters['dynamicLinkDomain'] = settings.dynamic_link_domain
537554

555+
# link_domain
556+
if settings.link_domain is not None:
557+
if not isinstance(settings.link_domain, str):
558+
raise ValueError(
559+
f'Invalid value provided for link_domain: {settings.link_domain}')
560+
parameters['linkDomain'] = settings.link_domain
561+
538562
# ios_bundle_id
539563
if settings.ios_bundle_id is not None:
540564
if not isinstance(settings.ios_bundle_id, str):

firebase_admin/auth.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@
4949
'ImportUserRecord',
5050
'InsufficientPermissionError',
5151
'InvalidDynamicLinkDomainError',
52+
'InvalidHostingLinkDomainError',
5253
'InvalidIdTokenError',
5354
'InvalidSessionCookieError',
5455
'ListProviderConfigsPage',
@@ -125,6 +126,7 @@
125126
ImportUserRecord = _user_import.ImportUserRecord
126127
InsufficientPermissionError = _auth_utils.InsufficientPermissionError
127128
InvalidDynamicLinkDomainError = _auth_utils.InvalidDynamicLinkDomainError
129+
InvalidHostingLinkDomainError = _auth_utils.InvalidHostingLinkDomainError
128130
InvalidIdTokenError = _auth_utils.InvalidIdTokenError
129131
InvalidSessionCookieError = _token_gen.InvalidSessionCookieError
130132
ListProviderConfigsPage = _auth_providers.ListProviderConfigsPage

tests/test_user_mgt.py

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,8 @@
4343
MOCK_ACTION_CODE_DATA = {
4444
'url': 'http://localhost',
4545
'handle_code_in_app': True,
46-
'dynamic_link_domain': 'http://testly',
46+
'dynamic_link_domain': 'http://dynamic-link-domain',
47+
'link_domain': 'http://link-domain',
4748
'ios_bundle_id': 'test.bundle',
4849
'android_package_name': 'test.bundle',
4950
'android_minimum_version': '7',
@@ -1363,7 +1364,8 @@ def test_valid_data(self):
13631364
data = {
13641365
'url': 'http://localhost',
13651366
'handle_code_in_app': True,
1366-
'dynamic_link_domain': 'http://testly',
1367+
'dynamic_link_domain': 'http://dynamic-link-domain',
1368+
'link_domain': 'http://link-domain',
13671369
'ios_bundle_id': 'test.bundle',
13681370
'android_package_name': 'test.bundle',
13691371
'android_minimum_version': '7',
@@ -1374,6 +1376,7 @@ def test_valid_data(self):
13741376
assert parameters['continueUrl'] == data['url']
13751377
assert parameters['canHandleCodeInApp'] == data['handle_code_in_app']
13761378
assert parameters['dynamicLinkDomain'] == data['dynamic_link_domain']
1379+
assert parameters['linkDomain'] == data['link_domain']
13771380
assert parameters['iOSBundleId'] == data['ios_bundle_id']
13781381
assert parameters['androidPackageName'] == data['android_package_name']
13791382
assert parameters['androidMinimumVersion'] == data['android_minimum_version']
@@ -1496,6 +1499,23 @@ def test_invalid_dynamic_link(self, user_mgt_app, func):
14961499
assert excinfo.value.http_response is not None
14971500
assert excinfo.value.cause is not None
14981501

1502+
@pytest.mark.parametrize('func', [
1503+
auth.generate_sign_in_with_email_link,
1504+
auth.generate_email_verification_link,
1505+
auth.generate_password_reset_link,
1506+
])
1507+
def test_invalid_hosting_link(self, user_mgt_app, func):
1508+
resp = '{"error":{"message": "INVALID_HOSTING_LINK_DOMAIN: Because of this reason."}}'
1509+
_instrument_user_manager(user_mgt_app, 500, resp)
1510+
with pytest.raises(auth.InvalidHostingLinkDomainError) as excinfo:
1511+
func('[email protected]', MOCK_ACTION_CODE_SETTINGS, app=user_mgt_app)
1512+
assert isinstance(excinfo.value, exceptions.InvalidArgumentError)
1513+
assert str(excinfo.value) == ('The provided hosting link domain is not configured in '
1514+
'Firebase Hosting or is not owned by the current project '
1515+
'(INVALID_HOSTING_LINK_DOMAIN). Because of this reason.')
1516+
assert excinfo.value.http_response is not None
1517+
assert excinfo.value.cause is not None
1518+
14991519
@pytest.mark.parametrize('func', [
15001520
auth.generate_sign_in_with_email_link,
15011521
auth.generate_email_verification_link,
@@ -1534,6 +1554,7 @@ def _validate_request(self, request, settings=None):
15341554
assert request['continueUrl'] == settings.url
15351555
assert request['canHandleCodeInApp'] == settings.handle_code_in_app
15361556
assert request['dynamicLinkDomain'] == settings.dynamic_link_domain
1557+
assert request['linkDomain'] == settings.link_domain
15371558
assert request['iOSBundleId'] == settings.ios_bundle_id
15381559
assert request['androidPackageName'] == settings.android_package_name
15391560
assert request['androidMinimumVersion'] == settings.android_minimum_version

0 commit comments

Comments
 (0)