Repository: auth0/auth0-python
Pull request #211 (2020-04-20 22:19:43)

Accept client_secret as passwordless/start param
### Changes

Tenants created after January 2, 2020 are required to set the client_secret when starting the passwordless flow.

### References

API docs https://auth0.com/docs/api/authentication?#get-code-or-link
Migration guide: https://auth0.com/docs/migrations/guides/passwordless-start

### Testing

Added tests for the optional parameter

- [x] This change adds unit test coverage
- [ ] This change adds integration test coverage
- [x] This change has been tested on the latest version of the platform/language or why not

### Checklist

- [x] I have read the [Auth0 general contribution guidelines](https://github.com/auth0/open-source-template/blob/master/GENERAL-CONTRIBUTING.md)
- [x] I have read the [Auth0 Code of Conduct](https://github.com/auth0/open-source-template/blob/master/CODE-OF-CONDUCT.md)
- [x] All existing and new tests complete without errors



Patch:
diff --git a/auth0/v3/authentication/passwordless.py b/auth0/v3/authentication/passwordless.py
index dd8db97..8b4f8a0 100644
--- a/auth0/v3/authentication/passwordless.py
+++ b/auth0/v3/authentication/passwordless.py
@@ -9,7 +9,7 @@ class Passwordless(AuthenticationBase):
         domain (str): Your auth0 domain (e.g: username.auth0.com)
     """
 
-    def email(self, client_id, email, send='link', auth_params=None):
+    def email(self, client_id, email, send='link', auth_params=None, client_secret=None):
         """Start flow sending an email.
 
         Given the user email address, it will send an email with:
@@ -31,34 +31,65 @@ class Passwordless(AuthenticationBase):
             send (str, optional): Can be: 'link' or 'code'. Defaults to 'link'.
 
             auth_params (dict, optional): Parameters to append or override.
+
+            client_secret (str): Client Secret of the application.
         """
 
+        data={
+            'client_id': client_id,
+            'connection': 'email',
+            'email': email,
+            'send': send,
+        }
+        if auth_params:
+            data.update({'authParams': auth_params})
+        if client_secret:
+            data.update({'client_secret': client_secret})
+
         return self.post(
             'https://{}/passwordless/start'.format(self.domain),
-            data={
-                'client_id': client_id,
-                'connection': 'email',
-                'email': email,
-                'send': send,
-                'authParams': auth_params
-            }
+            data=data
         )
 
-    def sms(self, client_id, phone_number):
-        """Start flow sending a SMS message.
+    def sms(self, client_id, phone_number, client_secret=None):
+        """Start flow sending an SMS message.
+
+        Given the user phone number, it will send an SMS with 
+        a verification code. You can then authenticate with
+        this user using phone number as username and code as password.
+
+        Args:
+            client_id (str): Client Id of the application.
+
+            client_secret (str): Client Secret of the application.
+
+            phone_number (str): Phone number.
         """
 
+        data={
+            'client_id': client_id,
+            'connection': 'sms',
+            'phone_number': phone_number,
+        }
+        if client_secret:
+            data.update({'client_secret': client_secret})
+            
         return self.post(
             'https://{}/passwordless/start'.format(self.domain),
-            data={
-                'client_id': client_id,
-                'connection': 'sms',
-                'phone_number': phone_number,
-            }
+            data=data
         )
 
     def sms_login(self, client_id, phone_number, code, scope='openid'):
         """Login using phone number/verification code.
+
+        Args:
+            client_id (str): Client Id of the application.
+
+            phone_number (str): Phone number.
+
+            code (str): Code received in the SMS.
+
+            scope (str, optional): Scope to use. Defaults to 'openid'.
         """
 
         return self.post(


Test patch:
diff --git a/auth0/v3/test/authentication/test_passwordless.py b/auth0/v3/test/authentication/test_passwordless.py
index 4a59a7a..86fa985 100644
--- a/auth0/v3/test/authentication/test_passwordless.py
+++ b/auth0/v3/test/authentication/test_passwordless.py
@@ -10,6 +10,25 @@ class TestPasswordless(unittest.TestCase):
 
         p = Passwordless('my.domain.com')
 
+        p.email(client_id='cid',
+                email='a@b.com',
+                send='snd')
+
+        args, kwargs = mock_post.call_args
+
+        self.assertEqual(args[0], 'https://my.domain.com/passwordless/start')
+        self.assertEqual(kwargs['data'], {
+            'client_id': 'cid',
+            'email': 'a@b.com',
+            'send': 'snd',
+            'connection': 'email',
+        })
+
+    @mock.patch('auth0.v3.authentication.passwordless.Passwordless.post')
+    def test_email_with_auth_params(self, mock_post):
+
+        p = Passwordless('my.domain.com')
+
         p.email(client_id='cid',
                 email='a@b.com',
                 send='snd',
@@ -26,6 +45,27 @@ class TestPasswordless(unittest.TestCase):
             'connection': 'email',
         })
 
+    @mock.patch('auth0.v3.authentication.passwordless.Passwordless.post')
+    def test_email_with_client_secret(self, mock_post):
+
+        p = Passwordless('my.domain.com')
+
+        p.email(client_id='cid',
+                client_secret='csecret',
+                email='a@b.com',
+                send='snd')
+
+        args, kwargs = mock_post.call_args
+
+        self.assertEqual(args[0], 'https://my.domain.com/passwordless/start')
+        self.assertEqual(kwargs['data'], {
+            'client_id': 'cid',
+            'client_secret': 'csecret',
+            'email': 'a@b.com',
+            'send': 'snd',
+            'connection': 'email',
+        })
+
     @mock.patch('auth0.v3.authentication.passwordless.Passwordless.post')
     def test_sms(self, mock_post):
         p = Passwordless('my.domain.com')
@@ -40,6 +80,22 @@ class TestPasswordless(unittest.TestCase):
             'phone_number': '123456',
             'connection': 'sms',
         })
+
+    @mock.patch('auth0.v3.authentication.passwordless.Passwordless.post')
+    def test_sms_with_client_secret(self, mock_post):
+        p = Passwordless('my.domain.com')
+
+        p.sms(client_id='cid', client_secret='csecret', phone_number='123456')
+
+        args, kwargs = mock_post.call_args
+
+        self.assertEqual(args[0], 'https://my.domain.com/passwordless/start')
+        self.assertEqual(kwargs['data'], {
+            'client_id': 'cid',
+            'client_secret': 'csecret',
+            'phone_number': '123456',
+            'connection': 'sms',
+        })
         
     @mock.patch('auth0.v3.authentication.passwordless.Passwordless.post')
     def test_sms_login(self, mock_post):