Skip to content

Commit 2542a0c

Browse files
authored
Embedded link signup (#585)
related to descope/etc#10884 Also added timeout of token to signin + tests
1 parent a62b460 commit 2542a0c

File tree

4 files changed

+99
-3
lines changed

4 files changed

+99
-3
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1113,7 +1113,7 @@ Embedded links can be created to directly receive a verifiable token without sen
11131113
This token can then be verified using the magic link 'verify' function, either directly or through a flow.
11141114

11151115
```python
1116-
token = descope_client.mgmt.user.generate_embedded_link("[email protected]", {"key1":"value1"})
1116+
token = descope_client.mgmt.user.generate_embedded_link("[email protected]", {"key1":"value1"}, 120)
11171117
```
11181118

11191119
### Audit

descope/management/common.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ class MgmtV1:
5757
user_generate_magic_link_for_test_path = "/v1/mgmt/tests/generate/magiclink"
5858
user_generate_enchanted_link_for_test_path = "/v1/mgmt/tests/generate/enchantedlink"
5959
user_generate_embedded_link_path = "/v1/mgmt/user/signin/embeddedlink"
60+
user_generate_sign_up_embedded_link_path = "/v1/mgmt/user/signup/embeddedlink"
6061
user_history_path = "/v1/mgmt/user/history"
6162

6263
# access key

descope/management/user.py

Lines changed: 57 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,24 @@ def __init__(
5656
self.status = status
5757

5858

59+
class CreateUserObj:
60+
def __init__(
61+
self,
62+
email: Optional[str] = None,
63+
phone: Optional[str] = None,
64+
name: Optional[str] = None,
65+
given_name: Optional[str] = None,
66+
middle_name: Optional[str] = None,
67+
family_name: Optional[str] = None,
68+
):
69+
self.email = email
70+
self.phone = phone
71+
self.name = name
72+
self.given_name = given_name
73+
self.middle_name = middle_name
74+
self.family_name = family_name
75+
76+
5977
class User(AuthBase):
6078
def create(
6179
self,
@@ -1655,7 +1673,7 @@ def generate_enchanted_link_for_test_user(
16551673
return response.json()
16561674

16571675
def generate_embedded_link(
1658-
self, login_id: str, custom_claims: Optional[dict] = None
1676+
self, login_id: str, custom_claims: Optional[dict] = None, timeout: int = 0
16591677
) -> str:
16601678
"""
16611679
Generate Embedded Link for the given user login ID.
@@ -1673,7 +1691,44 @@ def generate_embedded_link(
16731691
"""
16741692
response = self._auth.do_post(
16751693
MgmtV1.user_generate_embedded_link_path,
1676-
{"loginId": login_id, "customClaims": custom_claims},
1694+
{"loginId": login_id, "customClaims": custom_claims, "timeout": timeout},
1695+
pswd=self._auth.management_key,
1696+
)
1697+
return response.json()["token"]
1698+
1699+
def generate_sign_up_embedded_link(
1700+
self, login_id: str, user: Optional[CreateUserObj] = None,
1701+
email_verified: bool = False, phone_verified: bool = False,
1702+
login_options: Optional[LoginOptions] = None, timeout: int = 0
1703+
) -> str:
1704+
"""
1705+
Generate sign up Embedded Link for the given user login ID.
1706+
The return value is a token that can be verified via magic link, or using flows
1707+
1708+
Args:
1709+
login_id (str): The login ID of the user to authenticate with.
1710+
user (CreateUserObj): Optional user object to create the user with
1711+
email_verified (bool): Optional, set to true if the email is verified
1712+
phone_verified (bool): Optional, set to true if the phone is verified
1713+
login_options (LoginOptions): Optional login options to customize the link
1714+
timeout (int): Optional, the timeout in seconds for the link to be valid
1715+
1716+
Return value (str):
1717+
Return the token to be used in verification process
1718+
1719+
Raise:
1720+
AuthException: raised if the operation fails
1721+
"""
1722+
response = self._auth.do_post(
1723+
MgmtV1.user_generate_sign_up_embedded_link_path,
1724+
{
1725+
"loginId": login_id,
1726+
"user": user.__dict__ if user else {},
1727+
"loginOptions": login_options.__dict__ if login_options else {},
1728+
"emailVerified": email_verified,
1729+
"phoneVerified": phone_verified,
1730+
"timeout": timeout
1731+
},
16771732
pswd=self._auth.management_key,
16781733
)
16791734
return response.json()["token"]

tests/management/test_user.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2406,6 +2406,46 @@ def test_generate_embedded_link(self):
24062406
json={
24072407
"loginId": "login-id",
24082408
"customClaims": {"k1": "v1"},
2409+
"timeout": 0,
2410+
},
2411+
allow_redirects=False,
2412+
verify=True,
2413+
params=None,
2414+
timeout=DEFAULT_TIMEOUT_SECONDS,
2415+
)
2416+
2417+
def test_generate_sign_up_embedded_link(self):
2418+
# Test failed flows
2419+
with patch("requests.post") as mock_post:
2420+
mock_post.return_value.ok = False
2421+
self.assertRaises(
2422+
AuthException, self.client.mgmt.user.generate_sign_up_embedded_link, "login-id"
2423+
)
2424+
2425+
# Test success flow
2426+
with patch("requests.post") as mock_post:
2427+
network_resp = mock.Mock()
2428+
network_resp.ok = True
2429+
network_resp.json.return_value = json.loads("""{"token": "some-token"}""")
2430+
mock_post.return_value = network_resp
2431+
resp = self.client.mgmt.user.generate_sign_up_embedded_link(
2432+
"login-id", email_verified=True, phone_verified=True
2433+
)
2434+
self.assertEqual(resp, "some-token")
2435+
mock_post.assert_called_with(
2436+
f"{common.DEFAULT_BASE_URL}{MgmtV1.user_generate_sign_up_embedded_link_path}",
2437+
headers={
2438+
**common.default_headers,
2439+
"Authorization": f"Bearer {self.dummy_project_id}:{self.dummy_management_key}",
2440+
"x-descope-project-id": self.dummy_project_id,
2441+
},
2442+
json={
2443+
"loginId": "login-id",
2444+
"phoneVerified": True,
2445+
"emailVerified": True,
2446+
"user": {},
2447+
"loginOptions": {},
2448+
"timeout": 0,
24092449
},
24102450
allow_redirects=False,
24112451
verify=True,

0 commit comments

Comments
 (0)