def post(self):
        """POST is used when authenticating via the mobile application."""
        # Validate the request.
        yield gen.Task(self._StartJSONRequest,
                       'merge_token',
                       self.request,
                       json_schema.MERGE_TOKEN_REQUEST,
                       migrators=_REQUEST_MIGRATORS)

        # Validate the identity key.
        identity_key = self._request_message.dict['identity']
        AuthViewfinderHandler._ValidateIdentityKey(identity_key)

        # Require target merge account to be logged in, so that we can get target user name, id, and device type.
        context = ViewfinderContext.current()
        if context.user is None:
            # This case should never happen in the mobile or web clients, since they will not offer
            # the option to merge if the user is not already logged in. But it could happen with a
            # direct API call.
            raise PermissionError(MERGE_REQUIRES_LOGIN)

        identity = yield gen.Task(Identity.Query,
                                  self._client,
                                  identity_key,
                                  None,
                                  must_exist=False)
        if identity is not None and identity.user_id is not None:
            # If "error_if_linked" is true, raise an error, since the identity is already linked to a user.
            if self._request_message.dict.get('error_if_linked', False):
                raise PermissionError(
                    ALREADY_LINKED,
                    account=Identity.GetDescription(identity_key))

        # Send the email or SMS message in order to verify that the user controls it.
        yield VerifyIdBaseHandler.SendVerifyIdMessage(
            self._client,
            'merge_token',
            use_short_token=self._UseShortToken(),
            is_mobile_app=context.IsMobileClient(),
            identity_key=identity_key,
            user_id=context.user.user_id,
            user_name=context.user.name)

        self._FinishAuthViewfinder(identity_key)
  def post(self):
    """POST is used when authenticating via the mobile application."""
    # Validate the request.
    yield gen.Task(self._StartJSONRequest,
                   'merge_token',
                   self.request,
                   json_schema.MERGE_TOKEN_REQUEST,
                   migrators=_REQUEST_MIGRATORS)

    # Validate the identity key.
    identity_key = self._request_message.dict['identity']
    AuthViewfinderHandler._ValidateIdentityKey(identity_key)

    # Require target merge account to be logged in, so that we can get target user name, id, and device type. 
    context = ViewfinderContext.current()
    if context.user is None:
      # This case should never happen in the mobile or web clients, since they will not offer
      # the option to merge if the user is not already logged in. But it could happen with a
      # direct API call.
      raise PermissionError(MERGE_REQUIRES_LOGIN)

    identity = yield gen.Task(Identity.Query, self._client, identity_key, None, must_exist=False)
    if identity is not None and identity.user_id is not None:
      # If "error_if_linked" is true, raise an error, since the identity is already linked to a user.
      if self._request_message.dict.get('error_if_linked', False):
          raise PermissionError(ALREADY_LINKED, account=Identity.GetDescription(identity_key))

    # Send the email or SMS message in order to verify that the user controls it.
    yield VerifyIdBaseHandler.SendVerifyIdMessage(self._client,
                                                  'merge_token',
                                                  use_short_token=self._UseShortToken(),
                                                  is_mobile_app=context.IsMobileClient(),
                                                  identity_key=identity_key,
                                                  user_id=context.user.user_id,
                                                  user_name=context.user.name)

    self._FinishAuthViewfinder(identity_key)
    def _HandleGet(self,
                   short_url,
                   identity_key,
                   viewpoint_id,
                   default_url,
                   is_sms=False,
                   is_first_click=True):
        """Invoked when a user follows a prospective user invitation URL. Sets a prospective user
    cookie that identifies the user and restricts access to a single viewpoint. Typically
    redirects the user to the corresponding website conversation page.
    """
        identity = yield gen.Task(Identity.Query,
                                  self._client,
                                  identity_key,
                                  None,
                                  must_exist=False)

        # Check for rare case where the identity has been unlinked since issuing the prospective user link.
        if identity is None or identity.user_id is None:
            raise ExpiredError(
                'The requested link has expired and can no longer be used.')

        # If the "next" query argument is specified, redirect to that, otherwise fall back on default_url.
        next_url = self.get_argument('next', default_url)
        if urlparse.urlparse(next_url).hostname is not None:
            raise InvalidRequestError('Cannot redirect to absolute URL: %s' %
                                      next_url)

        # Detect photo store redirect, as we should not set a cookie or return redirection to photo store in this case.
        photostore_re = re.match(r'.*/episodes/(.*)/photos/(.*)(\..)',
                                 next_url)

        # If the link was sent via SMS, then reset the SMS alert count (since the link was followed).
        if is_sms:
            settings = AccountSettings.CreateForUser(identity.user_id,
                                                     sms_count=0)
            yield gen.Task(settings.Update, self._client)

        # A registered user can no longer use prospective user links.
        user = yield gen.Task(User.Query, self._client, identity.user_id, None)
        if user.IsRegistered():
            # If not already logged in with the same user with full access, redirect to the auth page.
            context = ViewfinderContext.current()
            if context is None:
                current_user = None
                current_viewpoint_id = None
            else:
                current_user = context.user
                current_viewpoint_id = context.viewpoint_id

            if current_user is None or current_user.user_id != identity.user_id or current_viewpoint_id is not None:
                self.ClearUserCookie()
                self.redirect('/auth?%s' % urlencode(dict(next=next_url)))
                return
        else:
            # If this is the first time the link was clicked, then create a confirmed cookie.
            if is_first_click:
                confirm_time = util.GetCurrentTimestamp()

                # Update is_first_click.
                new_json = deepcopy(short_url.json)
                new_json['is_first_click'] = False
                short_url.json = new_json
                yield gen.Task(short_url.Update, self._client)
            else:
                confirm_time = None

            # Set the prospective user cookie. Make it a session cookie so that it will go away when
            # browser is closed.
            user_cookie_dict = self.CreateUserCookieDict(
                user.user_id,
                user.webapp_dev_id,
                user_name=user.name,
                viewpoint_id=viewpoint_id,
                confirm_time=confirm_time,
                is_session_cookie=True)

            # Do not set the user cookie if this is a photo view request.
            self.LoginUser(user,
                           user_cookie_dict,
                           set_cookie=photostore_re is None)

        # Handle photostore redirect request internally rather than returning the redirect to the
        # client. Email clients do not keep cookies, so it is not possible to redirect to an
        # authenticated URL.
        if photostore_re:
            episode_id = photostore_re.group(1)
            photo_id = photostore_re.group(2)
            suffix = photostore_re.group(3)
            next_url = yield PhotoStoreHandler.GetPhotoUrl(
                self._client, self._obj_store, episode_id, photo_id, suffix)

        # Redirect to the URL of the next page.
        self.redirect(next_url)