Repository: privacyidea/privacyidea

Issue:
enroll_via_multichallenge add otpauth link to returned data
What are you trying to achieve?
Have the plugins show the `otpauth` link when `enroll_via_multichallenge` is triggered.

**Describe the solution you'd like**
In addition to the qr code data, the server should return the link.


# Top level requirements and scenarios
Improve enrollment when authenticating on mobile devices.

*Describe what needs to be achieved and how the scenario looks like*
Check how its done in `/token/init` and the webui and see if something is reusable


Discussion:

Patch:
diff --git a/privacyidea/lib/tokens/hotptoken.py b/privacyidea/lib/tokens/hotptoken.py
index e8f2134e8b..77537edcb6 100644
--- a/privacyidea/lib/tokens/hotptoken.py
+++ b/privacyidea/lib/tokens/hotptoken.py
@@ -866,6 +866,7 @@ def enroll_via_validate(cls, g, content, user_obj, message=None):
         detail["messages"] = [message]
         chal = {"transaction_id": c[2],
                 "image": init_details.get("googleurl").get("img"),
+                "link": init_details.get("googleurl").get("value"),
                 "client_mode": CLIENTMODE.INTERACTIVE,
                 "serial": token_obj.token.serial,
                 "type": token_obj.type,
diff --git a/privacyidea/lib/tokens/pushtoken.py b/privacyidea/lib/tokens/pushtoken.py
index 1d531898cc..3bfff98b26 100644
--- a/privacyidea/lib/tokens/pushtoken.py
+++ b/privacyidea/lib/tokens/pushtoken.py
@@ -1186,6 +1186,7 @@ def enroll_via_validate(cls, g, content, user_obj, message=None):
         detail["transaction_ids"] = [c[2]]
         chal = {"transaction_id": c[2],
                 "image": init_details.get("pushurl", {}).get("img"),
+                "link": init_details.get("pushurl", {}).get("value"),
                 "client_mode": CLIENTMODE.POLL,
                 "serial": token_obj.token.serial,
                 "type": token_obj.type,


Test patch:
diff --git a/tests/test_api_push_validate.py b/tests/test_api_push_validate.py
index 09dc5a71d0..7407e5dfdc 100644
--- a/tests/test_api_push_validate.py
+++ b/tests/test_api_push_validate.py
@@ -428,6 +428,9 @@ def test_10_enroll_push(self):
             # Check, that multi_challenge is also contained.
             self.assertEqual(CLIENTMODE.POLL, detail.get("multi_challenge")[0].get("client_mode"))
             self.assertIn("image", detail)
+            self.assertIn("link", detail)
+            link = detail.get("link")
+            self.assertTrue(link.startswith("otpauth://pipush"))
             serial = detail.get("serial")
 
         # The Application starts polling, if the token is enrolled
diff --git a/tests/test_api_validate.py b/tests/test_api_validate.py
index 3962dcb761..1e9f6056b4 100644
--- a/tests/test_api_validate.py
+++ b/tests/test_api_validate.py
@@ -5615,6 +5615,9 @@ def test_01_enroll_HOTP(self, capture):
             chal = detail.get("multi_challenge")[0]
             self.assertEqual(CLIENTMODE.INTERACTIVE, chal.get("client_mode"), detail)
             self.assertIn("image", detail, detail)
+            self.assertIn("link", detail)
+            link = detail.get("link")
+            self.assertTrue(link.startswith("otpauth://hotp"), link)
             self.assertEqual(1, len(detail.get("messages")))
             self.assertEqual("Please scan the QR code and enter the OTP value!", detail.get("messages")[0])
             serial = detail.get("serial")
@@ -5717,6 +5720,9 @@ def test_02_enroll_TOTP(self, capture):
             # Check, that multi_challenge is also contained.
             self.assertEqual(CLIENTMODE.INTERACTIVE, detail.get("multi_challenge")[0].get("client_mode"))
             self.assertIn("image", detail)
+            self.assertIn("link", detail)
+            link = detail.get("link")
+            self.assertTrue(link.startswith("otpauth://totp"), link)
             serial = detail.get("serial")
 
         # 3. scan the qrcode / Get the OTP value