Skip to content

Commit 89fb919

Browse files
committed
allows empty provider list
1 parent e9bb86f commit 89fb919

File tree

5 files changed

+368
-54
lines changed

5 files changed

+368
-54
lines changed

supertokens_python/recipe/thirdpartyemailpassword/recipe.py

Lines changed: 15 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,7 @@ def apis_override_email_password(_: EmailPasswordAPIInterface):
155155
)
156156

157157
if third_party_recipe is not None:
158-
self.third_party_recipe: Union[ThirdPartyRecipe, None] = third_party_recipe
158+
self.third_party_recipe = third_party_recipe
159159
else:
160160

161161
def func_override_third_party(
@@ -168,18 +168,16 @@ def apis_override_third_party(
168168
) -> ThirdPartyAPIInterface:
169169
return get_third_party_interface_impl(self.api_implementation)
170170

171-
self.third_party_recipe: Union[ThirdPartyRecipe, None] = None
171+
# No email delivery ingredient required for third party recipe
172+
# but we pass an object for future proofing
172173
tp_ingredients = ThirdPartyIngredients()
173-
if len(self.config.providers) != 0:
174-
self.third_party_recipe = ThirdPartyRecipe(
175-
recipe_id,
176-
app_info,
177-
SignInAndUpFeature(self.config.providers),
178-
tp_ingredients,
179-
TPOverrideConfig(
180-
func_override_third_party, apis_override_third_party
181-
),
182-
)
174+
self.third_party_recipe = ThirdPartyRecipe(
175+
recipe_id,
176+
app_info,
177+
SignInAndUpFeature(self.config.providers),
178+
tp_ingredients,
179+
TPOverrideConfig(func_override_third_party, apis_override_third_party),
180+
)
183181

184182
def is_error_from_this_recipe_based_on_instance(self, err: Exception) -> bool:
185183
return isinstance(err, SuperTokensError) and (
@@ -188,17 +186,13 @@ def is_error_from_this_recipe_based_on_instance(self, err: Exception) -> bool:
188186
err
189187
)
190188
or (
191-
self.third_party_recipe is not None
192-
and self.third_party_recipe.is_error_from_this_recipe_based_on_instance(
193-
err
194-
)
189+
self.third_party_recipe.is_error_from_this_recipe_based_on_instance(err)
195190
)
196191
)
197192

198193
def get_apis_handled(self) -> List[APIHandled]:
199194
apis_handled = self.email_password_recipe.get_apis_handled()
200-
if self.third_party_recipe is not None:
201-
apis_handled = apis_handled + self.third_party_recipe.get_apis_handled()
195+
apis_handled += self.third_party_recipe.get_apis_handled()
202196
return apis_handled
203197

204198
async def handle_api_request(
@@ -221,8 +215,7 @@ async def handle_api_request(
221215
request_id, tenant_id, request, path, method, response, user_context
222216
)
223217
if (
224-
self.third_party_recipe is not None
225-
and await self.third_party_recipe.return_api_id_if_can_handle_request(
218+
await self.third_party_recipe.return_api_id_if_can_handle_request(
226219
path, method, user_context
227220
)
228221
is not None
@@ -237,17 +230,13 @@ async def handle_error(
237230
) -> BaseResponse:
238231
if self.email_password_recipe.is_error_from_this_recipe_based_on_instance(err):
239232
return await self.email_password_recipe.handle_error(request, err, response)
240-
if (
241-
self.third_party_recipe is not None
242-
and self.third_party_recipe.is_error_from_this_recipe_based_on_instance(err)
243-
):
233+
if self.third_party_recipe.is_error_from_this_recipe_based_on_instance(err):
244234
return await self.third_party_recipe.handle_error(request, err, response)
245235
raise err
246236

247237
def get_all_cors_headers(self) -> List[str]:
248238
cors_headers = self.email_password_recipe.get_all_cors_headers()
249-
if self.third_party_recipe is not None:
250-
cors_headers = cors_headers + self.third_party_recipe.get_all_cors_headers()
239+
cors_headers += self.third_party_recipe.get_all_cors_headers()
251240
return cors_headers
252241

253242
@staticmethod

supertokens_python/recipe/thirdpartypasswordless/recipe.py

Lines changed: 17 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,7 @@ def apis_override_passwordless(
176176
)
177177

178178
if third_party_recipe is not None:
179-
self.third_party_recipe: Union[ThirdPartyRecipe, None] = third_party_recipe
179+
self.third_party_recipe = third_party_recipe
180180
else:
181181

182182
def func_override_third_party(
@@ -189,36 +189,30 @@ def apis_override_third_party(
189189
) -> ThirdPartyAPIInterface:
190190
return get_third_party_interface_impl(self.api_implementation)
191191

192-
self.third_party_recipe: Union[ThirdPartyRecipe, None] = None
193-
194-
if len(self.config.providers) != 0:
195-
tp_ingredients = ThirdPartyIngredients()
196-
self.third_party_recipe = ThirdPartyRecipe(
197-
recipe_id,
198-
app_info,
199-
SignInAndUpFeature(self.config.providers),
200-
tp_ingredients,
201-
TPOverrideConfig(
202-
func_override_third_party, apis_override_third_party
203-
),
204-
)
192+
# Thirdparty recipe doesn't need ingredients
193+
# as of now. But we are passing ingredients object
194+
# so that it's future-proof.
195+
tp_ingredients = ThirdPartyIngredients()
196+
self.third_party_recipe = ThirdPartyRecipe(
197+
recipe_id,
198+
app_info,
199+
SignInAndUpFeature(self.config.providers),
200+
tp_ingredients,
201+
TPOverrideConfig(func_override_third_party, apis_override_third_party),
202+
)
205203

206204
def is_error_from_this_recipe_based_on_instance(self, err: Exception) -> bool:
207205
return isinstance(err, SuperTokensError) and (
208206
isinstance(err, SupertokensThirdPartyPasswordlessError)
209207
or self.passwordless_recipe.is_error_from_this_recipe_based_on_instance(err)
210208
or (
211-
self.third_party_recipe is not None
212-
and self.third_party_recipe.is_error_from_this_recipe_based_on_instance(
213-
err
214-
)
209+
self.third_party_recipe.is_error_from_this_recipe_based_on_instance(err)
215210
)
216211
)
217212

218213
def get_apis_handled(self) -> List[APIHandled]:
219214
apis_handled = self.passwordless_recipe.get_apis_handled()
220-
if self.third_party_recipe is not None:
221-
apis_handled = apis_handled + self.third_party_recipe.get_apis_handled()
215+
apis_handled += self.third_party_recipe.get_apis_handled()
222216
return apis_handled
223217

224218
async def handle_api_request(
@@ -241,8 +235,7 @@ async def handle_api_request(
241235
request_id, tenant_id, request, path, method, response, user_context
242236
)
243237
if (
244-
self.third_party_recipe is not None
245-
and await self.third_party_recipe.return_api_id_if_can_handle_request(
238+
await self.third_party_recipe.return_api_id_if_can_handle_request(
246239
path, method, user_context
247240
)
248241
is not None
@@ -257,17 +250,13 @@ async def handle_error(
257250
) -> BaseResponse:
258251
if self.passwordless_recipe.is_error_from_this_recipe_based_on_instance(err):
259252
return await self.passwordless_recipe.handle_error(request, err, response)
260-
if (
261-
self.third_party_recipe is not None
262-
and self.third_party_recipe.is_error_from_this_recipe_based_on_instance(err)
263-
):
253+
if self.third_party_recipe.is_error_from_this_recipe_based_on_instance(err):
264254
return await self.third_party_recipe.handle_error(request, err, response)
265255
raise err
266256

267257
def get_all_cors_headers(self) -> List[str]:
268258
cors_headers = self.passwordless_recipe.get_all_cors_headers()
269-
if self.third_party_recipe is not None:
270-
cors_headers = cors_headers + self.third_party_recipe.get_all_cors_headers()
259+
cors_headers += self.third_party_recipe.get_all_cors_headers()
271260
return cors_headers
272261

273262
@staticmethod
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
2+
#
3+
# This software is licensed under the Apache License, Version 2.0 (the
4+
# "License") as published by the Apache Software Foundation.
5+
#
6+
# You may not use this file except in compliance with the License. You may
7+
# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11+
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12+
# License for the specific language governing permissions and limitations
13+
# under the License.
14+
import json
15+
16+
from fastapi import FastAPI
17+
from pytest import mark, fixture
18+
19+
from supertokens_python.framework.fastapi import get_middleware
20+
from fastapi.testclient import TestClient
21+
from supertokens_python.recipe import session, thirdparty
22+
from supertokens_python import init
23+
from supertokens_python.recipe.multitenancy.asyncio import (
24+
create_or_update_third_party_config,
25+
)
26+
from supertokens_python.recipe.thirdparty.provider import (
27+
ProviderConfig,
28+
ProviderClientConfig,
29+
)
30+
31+
from tests.utils import get_st_init_args
32+
from tests.utils import (
33+
setup_function,
34+
teardown_function,
35+
start_st,
36+
)
37+
38+
39+
_ = setup_function
40+
_ = teardown_function
41+
42+
pytestmark = mark.asyncio
43+
44+
45+
@fixture(scope="function")
46+
async def app():
47+
app = FastAPI()
48+
app.add_middleware(get_middleware())
49+
50+
return TestClient(app)
51+
52+
53+
async def test_calling_authorisation_url_api_with_empty_init(app: TestClient):
54+
args = get_st_init_args(
55+
[
56+
session.init(
57+
get_token_transfer_method=lambda _, __, ___: "cookie",
58+
anti_csrf="VIA_TOKEN",
59+
),
60+
thirdparty.init(),
61+
]
62+
)
63+
init(**args) # type: ignore
64+
start_st()
65+
66+
res = app.get(
67+
"/auth/authorisationurl?thirdPartyId=google&redirectURIOnProviderDashboard=redirect"
68+
)
69+
assert res.status_code == 400
70+
assert res.text == "the provider google could not be found in the configuration"
71+
72+
73+
async def test_calling_authorisation_url_api_with_empty_init_with_dynamic_thirdparty_provider(
74+
app: TestClient,
75+
):
76+
args = get_st_init_args(
77+
[
78+
session.init(
79+
get_token_transfer_method=lambda _, __, ___: "cookie",
80+
anti_csrf="VIA_TOKEN",
81+
),
82+
thirdparty.init(),
83+
]
84+
)
85+
init(**args) # type: ignore
86+
start_st()
87+
88+
await create_or_update_third_party_config(
89+
"public",
90+
ProviderConfig(
91+
third_party_id="google",
92+
name="Google",
93+
clients=[
94+
ProviderClientConfig(
95+
client_id="google-client-id",
96+
client_secret="google-client-secret",
97+
)
98+
],
99+
),
100+
)
101+
102+
res = app.get(
103+
"/auth/authorisationurl?thirdPartyId=google&redirectURIOnProviderDashboard=redirect"
104+
)
105+
body = json.loads(res.text)
106+
assert body["status"] == "OK"
107+
assert (
108+
body["urlWithQueryParams"]
109+
== "https://accounts.google.com/o/oauth2/v2/auth?client_id=google-client-id&redirect_uri=redirect&response_type=code&scope=openid+email&included_grant_scopes=true&access_type=offline"
110+
)
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
2+
#
3+
# This software is licensed under the Apache License, Version 2.0 (the
4+
# "License") as published by the Apache Software Foundation.
5+
#
6+
# You may not use this file except in compliance with the License. You may
7+
# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11+
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12+
# License for the specific language governing permissions and limitations
13+
# under the License.
14+
import json
15+
16+
from fastapi import FastAPI
17+
from pytest import mark, fixture
18+
19+
from supertokens_python.framework.fastapi import get_middleware
20+
from fastapi.testclient import TestClient
21+
from supertokens_python.recipe import session, thirdpartyemailpassword
22+
from supertokens_python import init
23+
from supertokens_python.recipe.multitenancy.asyncio import (
24+
create_or_update_third_party_config,
25+
)
26+
from supertokens_python.recipe.thirdparty.provider import (
27+
ProviderConfig,
28+
ProviderClientConfig,
29+
)
30+
31+
from tests.utils import get_st_init_args
32+
from tests.utils import (
33+
setup_function,
34+
teardown_function,
35+
start_st,
36+
)
37+
38+
39+
_ = setup_function
40+
_ = teardown_function
41+
42+
pytestmark = mark.asyncio
43+
44+
45+
@fixture(scope="function")
46+
async def app():
47+
app = FastAPI()
48+
app.add_middleware(get_middleware())
49+
50+
return TestClient(app)
51+
52+
53+
async def test_calling_authorisation_url_api_with_empty_init(app: TestClient):
54+
args = get_st_init_args(
55+
[
56+
session.init(
57+
get_token_transfer_method=lambda _, __, ___: "cookie",
58+
anti_csrf="VIA_TOKEN",
59+
),
60+
thirdpartyemailpassword.init(),
61+
]
62+
)
63+
init(**args) # type: ignore
64+
start_st()
65+
66+
res = app.get(
67+
"/auth/authorisationurl?thirdPartyId=google&redirectURIOnProviderDashboard=redirect"
68+
)
69+
assert res.status_code == 400
70+
assert res.text == "the provider google could not be found in the configuration"
71+
72+
73+
async def test_calling_authorisation_url_api_with_empty_init_with_dynamic_thirdparty_provider(
74+
app: TestClient,
75+
):
76+
args = get_st_init_args(
77+
[
78+
session.init(
79+
get_token_transfer_method=lambda _, __, ___: "cookie",
80+
anti_csrf="VIA_TOKEN",
81+
),
82+
thirdpartyemailpassword.init(),
83+
]
84+
)
85+
init(**args) # type: ignore
86+
start_st()
87+
88+
await create_or_update_third_party_config(
89+
"public",
90+
ProviderConfig(
91+
third_party_id="google",
92+
name="Google",
93+
clients=[
94+
ProviderClientConfig(
95+
client_id="google-client-id",
96+
client_secret="google-client-secret",
97+
)
98+
],
99+
),
100+
)
101+
102+
res = app.get(
103+
"/auth/authorisationurl?thirdPartyId=google&redirectURIOnProviderDashboard=redirect"
104+
)
105+
body = json.loads(res.text)
106+
assert body["status"] == "OK"
107+
assert (
108+
body["urlWithQueryParams"]
109+
== "https://accounts.google.com/o/oauth2/v2/auth?client_id=google-client-id&redirect_uri=redirect&response_type=code&scope=openid+email&included_grant_scopes=true&access_type=offline"
110+
)

0 commit comments

Comments
 (0)