@@ -184,6 +184,18 @@ def _compose_signup_url(method: DeliveryMethod) -> str:
184184 def _compose_verify_code_url (method : DeliveryMethod ) -> str :
185185 return AuthClient ._compose_url (EndpointsV1 .verifyCodeAuthPath , method )
186186
187+ @staticmethod
188+ def _compose_signin_magiclink_url (method : DeliveryMethod ) -> str :
189+ return AuthClient ._compose_url (EndpointsV1 .signInAuthMagicLinkPath , method )
190+
191+ @staticmethod
192+ def _compose_signup_magiclink_url (method : DeliveryMethod ) -> str :
193+ return AuthClient ._compose_url (EndpointsV1 .signUpAuthMagicLinkPath , method )
194+
195+ @staticmethod
196+ def _compose_verify_magiclink_url () -> str :
197+ return EndpointsV1 .verifyMagicLinkAuthPath
198+
187199 @staticmethod
188200 def _compose_refresh_token_url () -> str :
189201 return EndpointsV1 .refreshTokenPath
@@ -331,6 +343,121 @@ def verify_code(
331343 claims , tokens = self ._validate_and_load_tokens (session_token , refresh_token )
332344 return (claims , tokens )
333345
346+ def sign_up_magiclink (
347+ self , method : DeliveryMethod , identifier : str , uri : str , user : User = None
348+ ) -> None :
349+ """
350+ Sign up a new user by magic link
351+
352+ Args:
353+ method (DeliveryMethod): The Magic Link method you would like to verify the code
354+ sent to you (by the same delivery method)
355+
356+ identifier (str): The identifier based on the chosen delivery method,
357+ For email it should be the email address.
358+ For phone it should be the phone number you would like to get the link
359+ For whatsapp it should be the phone number you would like to get the link
360+
361+ uri (str): The base URI that should contain the magic link code
362+
363+ Raise:
364+ AuthException: for any case sign up by magic link operation failed
365+ """
366+
367+ if not self ._verify_delivery_method (method , identifier ):
368+ raise AuthException (
369+ 500 ,
370+ "identifier failure" ,
371+ f"Identifier { identifier } is not valid by delivery method { method } " ,
372+ )
373+
374+ body = {self ._get_identifier_name_by_method (method ): identifier , "URI" : uri }
375+
376+ if user is not None :
377+ body ["user" ] = user .get_data ()
378+
379+ requestUri = AuthClient ._compose_signup_magiclink_url (method )
380+ response = requests .post (
381+ f"{ DEFAULT_BASE_URI } { requestUri } " ,
382+ headers = self ._get_default_headers (),
383+ data = json .dumps (body ),
384+ )
385+ if not response .ok :
386+ raise AuthException (response .status_code , "" , response .reason )
387+
388+ def sign_in_magiclink (
389+ self , method : DeliveryMethod , identifier : str , uri : str
390+ ) -> None :
391+ """
392+ Sign in a user by magiclink
393+
394+ Args:
395+ method (DeliveryMethod): The Magic Link method you would like to verify the link
396+ sent to you (by the same delivery method)
397+
398+ identifier (str): The identifier based on the chosen delivery method,
399+ For email it should be the email address.
400+ For phone it should be the phone number you would like to get the link
401+ For whatsapp it should be the phone number you would like to get the link
402+
403+ uri (str): The base URI that should contain the magic link code
404+
405+ Raise:
406+ AuthException: for any case sign up by otp operation failed
407+ """
408+
409+ if not self ._verify_delivery_method (method , identifier ):
410+ raise AuthException (
411+ 500 ,
412+ "identifier failure" ,
413+ f"Identifier { identifier } is not valid by delivery method { method } " ,
414+ )
415+
416+ body = {self ._get_identifier_name_by_method (method ): identifier , "URI" : uri }
417+
418+ requestUri = AuthClient ._compose_signin_magiclink_url (method )
419+ response = requests .post (
420+ f"{ DEFAULT_BASE_URI } { requestUri } " ,
421+ headers = self ._get_default_headers (),
422+ data = json .dumps (body ),
423+ )
424+ if not response .ok :
425+ raise AuthException (response .status_code , "" , response .text )
426+
427+ def verify_magiclink (
428+ self , code : str
429+ ) -> Tuple [dict , dict ]: # Tuple(dict of claims, dict of tokens)
430+ """Verify magiclink
431+
432+ Args:
433+ code (str): The authorization code you get by the delivery method during signup/signin
434+
435+ Return value (Tuple[dict, dict]):
436+ Return two dicts where the first contains the jwt claims data and
437+ second contains the existing signed token (or the new signed
438+ token in case the old one expired) and refreshed session token
439+
440+ Raise:
441+ AuthException: for any case code is not valid or tokens verification failed
442+ """
443+
444+ body = {"token" : code }
445+
446+ uri = AuthClient ._compose_verify_magiclink_url ()
447+ response = requests .post (
448+ f"{ DEFAULT_BASE_URI } { uri } " ,
449+ headers = self ._get_default_headers (),
450+ data = json .dumps (body ),
451+ )
452+ if not response .ok :
453+ raise AuthException (response .status_code , "" , response .reason )
454+
455+ session_token = response .cookies .get (SESSION_COOKIE_NAME )
456+ refresh_token = response .cookies .get (REFRESH_SESSION_COOKIE_NAME )
457+
458+ claims , tokens = self ._validate_and_load_tokens (session_token , refresh_token )
459+ return (claims , tokens )
460+
334461 def refresh_token (self , signed_token : str , signed_refresh_token : str ) -> str :
335462 cookies = {
336463 SESSION_COOKIE_NAME : signed_token ,
0 commit comments