Skip to content

Update ActionCodeSettings to support link_domain #884

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions firebase_admin/_auth_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -328,6 +328,15 @@ def __init__(self, message, cause, http_response):
exceptions.InvalidArgumentError.__init__(self, message, cause, http_response)


class InvalidHostingLinkDomainError(exceptions.InvalidArgumentError):
"""Hosting link domain in ActionCodeSettings is not authorized."""

default_message = 'Hosting link domain specified in ActionCodeSettings is not authorized'

def __init__(self, message, cause, http_response):
exceptions.InvalidArgumentError.__init__(self, message, cause, http_response)


class InvalidIdTokenError(exceptions.InvalidArgumentError):
"""The provided ID token is not a valid Firebase ID token."""

Expand Down Expand Up @@ -427,6 +436,7 @@ def __init__(self, message, cause=None, http_response=None):
'EMAIL_NOT_FOUND': EmailNotFoundError,
'INSUFFICIENT_PERMISSION': InsufficientPermissionError,
'INVALID_DYNAMIC_LINK_DOMAIN': InvalidDynamicLinkDomainError,
'INVALID_HOSTING_LINK_DOMAIN': InvalidHostingLinkDomainError,
'INVALID_ID_TOKEN': InvalidIdTokenError,
'PHONE_NUMBER_EXISTS': PhoneNumberAlreadyExistsError,
'TENANT_NOT_FOUND': TenantNotFoundError,
Expand Down
14 changes: 13 additions & 1 deletion firebase_admin/_user_mgt.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
from collections import defaultdict
import json
from urllib import parse
import warnings

import requests

Expand Down Expand Up @@ -490,14 +491,18 @@ class ActionCodeSettings:
"""

def __init__(self, url, handle_code_in_app=None, dynamic_link_domain=None, ios_bundle_id=None,
android_package_name=None, android_install_app=None, android_minimum_version=None):
android_package_name=None, android_install_app=None, android_minimum_version=None,
link_domain=None):
if dynamic_link_domain is not None:
warnings.warn('dynamic_link_domain is deprecated, use link_domain instead', DeprecationWarning)
self.url = url
self.handle_code_in_app = handle_code_in_app
self.dynamic_link_domain = dynamic_link_domain
self.ios_bundle_id = ios_bundle_id
self.android_package_name = android_package_name
self.android_install_app = android_install_app
self.android_minimum_version = android_minimum_version
self.link_domain = link_domain


def encode_action_code_settings(settings):
Expand Down Expand Up @@ -535,6 +540,13 @@ def encode_action_code_settings(settings):
.format(settings.dynamic_link_domain))
parameters['dynamicLinkDomain'] = settings.dynamic_link_domain

# link_domain
if settings.link_domain is not None:
if not isinstance(settings.link_domain, str):
raise ValueError('Invalid value provided for link_domain: {0}'
.format(settings.link_domain))
parameters['linkDomain'] = settings.link_domain

# ios_bundle_id
if settings.ios_bundle_id is not None:
if not isinstance(settings.ios_bundle_id, str):
Expand Down
2 changes: 2 additions & 0 deletions firebase_admin/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
'ImportUserRecord',
'InsufficientPermissionError',
'InvalidDynamicLinkDomainError',
'InvalidHostingLinkDomainError',
'InvalidIdTokenError',
'InvalidSessionCookieError',
'ListProviderConfigsPage',
Expand Down Expand Up @@ -125,6 +126,7 @@
ImportUserRecord = _user_import.ImportUserRecord
InsufficientPermissionError = _auth_utils.InsufficientPermissionError
InvalidDynamicLinkDomainError = _auth_utils.InvalidDynamicLinkDomainError
InvalidHostingLinkDomainError = _auth_utils.InvalidHostingLinkDomainError
InvalidIdTokenError = _auth_utils.InvalidIdTokenError
InvalidSessionCookieError = _token_gen.InvalidSessionCookieError
ListProviderConfigsPage = _auth_providers.ListProviderConfigsPage
Expand Down
25 changes: 23 additions & 2 deletions tests/test_user_mgt.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,8 @@
MOCK_ACTION_CODE_DATA = {
'url': 'http://localhost',
'handle_code_in_app': True,
'dynamic_link_domain': 'http://testly',
'dynamic_link_domain': 'http://dynamic-link-domain',
'link_domain': 'http://link-domain',
'ios_bundle_id': 'test.bundle',
'android_package_name': 'test.bundle',
'android_minimum_version': '7',
Expand Down Expand Up @@ -1364,7 +1365,8 @@ def test_valid_data(self):
data = {
'url': 'http://localhost',
'handle_code_in_app': True,
'dynamic_link_domain': 'http://testly',
'dynamic_link_domain': 'http://dynamic-link-domain',
'link_domain': 'http://link-domain',
'ios_bundle_id': 'test.bundle',
'android_package_name': 'test.bundle',
'android_minimum_version': '7',
Expand All @@ -1375,6 +1377,7 @@ def test_valid_data(self):
assert parameters['continueUrl'] == data['url']
assert parameters['canHandleCodeInApp'] == data['handle_code_in_app']
assert parameters['dynamicLinkDomain'] == data['dynamic_link_domain']
assert parameters['linkDomain'] == data['link_domain']
assert parameters['iOSBundleId'] == data['ios_bundle_id']
assert parameters['androidPackageName'] == data['android_package_name']
assert parameters['androidMinimumVersion'] == data['android_minimum_version']
Expand Down Expand Up @@ -1497,6 +1500,23 @@ def test_invalid_dynamic_link(self, user_mgt_app, func):
assert excinfo.value.http_response is not None
assert excinfo.value.cause is not None

@pytest.mark.parametrize('func', [
auth.generate_sign_in_with_email_link,
auth.generate_email_verification_link,
auth.generate_password_reset_link,
])
def test_invalid_hosting_link(self, user_mgt_app, func):
resp = '{"error":{"message": "INVALID_HOSTING_LINK_DOMAIN: Because of this reason."}}'
_instrument_user_manager(user_mgt_app, 500, resp)
with pytest.raises(auth.InvalidHostingLinkDomainError) as excinfo:
func('[email protected]', MOCK_ACTION_CODE_SETTINGS, app=user_mgt_app)
assert isinstance(excinfo.value, exceptions.InvalidArgumentError)
assert str(excinfo.value) == ('Hosting link domain specified in ActionCodeSettings is '
'not authorized (INVALID_HOSTING_LINK_DOMAIN). Because '
'of this reason.')
assert excinfo.value.http_response is not None
assert excinfo.value.cause is not None

@pytest.mark.parametrize('func', [
auth.generate_sign_in_with_email_link,
auth.generate_email_verification_link,
Expand Down Expand Up @@ -1535,6 +1555,7 @@ def _validate_request(self, request, settings=None):
assert request['continueUrl'] == settings.url
assert request['canHandleCodeInApp'] == settings.handle_code_in_app
assert request['dynamicLinkDomain'] == settings.dynamic_link_domain
assert request['linkDomain'] == settings.link_domain
assert request['iOSBundleId'] == settings.ios_bundle_id
assert request['androidPackageName'] == settings.android_package_name
assert request['androidMinimumVersion'] == settings.android_minimum_version
Expand Down