コード例 #1
0
def tf_authenticate(app, client, json=False, validate=True, remember=False):
    """Login/Authenticate using two factor.
    This is the equivalent of utils:authenticate
    """
    prev_sms = app.config["SECURITY_SMS_SERVICE"]
    app.config["SECURITY_SMS_SERVICE"] = "test"
    sms_sender = SmsSenderFactory.createSender("test")
    json_data = dict(email="*****@*****.**", password="******", remember=remember)
    response = client.post(
        "/login", json=json_data, headers={"Content-Type": "application/json"}
    )

    assert b'"code": 200' in response.data
    app.config["SECURITY_SMS_SERVICE"] = prev_sms

    if validate:
        code = sms_sender.messages[0].split()[-1]
        if json:
            response = client.post(
                "/tf-validate",
                json=dict(code=code),
                headers={"Content-Type": "application/json"},
            )
            assert b'"code": 200' in response.data
            return response.json["response"].get("tf_validity_token", None)
        else:
            response = client.post(
                "/tf-validate", data=dict(code=code), follow_redirects=True
            )

            assert response.status_code == 200
コード例 #2
0
def test_recoverable(app, client, get_message):
    # make sure 'forgot password' doesn't bypass 2FA.
    # '*****@*****.**' already setup for SMS

    rtokens = []
    sms_sender = SmsSenderFactory.createSender("test")

    @reset_password_instructions_sent.connect_via(app)
    def on_instructions_sent(sapp, **kwargs):
        rtokens.append(kwargs["token"])

    client.post("/reset", data=dict(email="*****@*****.**"), follow_redirects=True)
    response = client.post(
        "/reset/" + rtokens[0],
        data={"password": "******", "password_confirm": "awesome sunset"},
        follow_redirects=True,
    )
    # Should have redirected us to the 2FA login page
    assert b"Please enter your authentication code" in response.data

    # we shouldn't be logged in
    response = client.get("/profile", follow_redirects=False)
    assert response.status_code == 302

    # Grab code that was sent
    assert sms_sender.get_count() == 1
    code = sms_sender.messages[0].split()[-1]
    response = client.post("/tf-validate", data=dict(code=code), follow_redirects=True)
    assert b"Your token has been confirmed" in response.data

    # verify we are logged in
    response = client.get("/profile", follow_redirects=False)
    assert response.status_code == 200
コード例 #3
0
def test_json(app, client):
    """
    Test all endpoints using JSON. (eventually)
    """

    # Test that user not yet setup for 2FA gets correct response.
    data = dict(email="*****@*****.**", password="******")
    response = client.post(
        "/login", json=data, headers={"Content-Type": "application/json"}
    )
    assert response.json["response"]["tf_required"]
    assert response.json["response"]["tf_state"] == "setup_from_login"

    # Login with someone already setup.
    sms_sender = SmsSenderFactory.createSender("test")
    data = dict(email="*****@*****.**", password="******")
    response = client.post(
        "/login", json=data, headers={"Content-Type": "application/json"}
    )
    assert response.status_code == 200
    assert response.json["response"]["tf_required"]
    assert response.json["response"]["tf_state"] == "ready"
    assert response.json["response"]["tf_primary_method"] == "sms"

    # Verify SMS sent
    assert sms_sender.get_count() == 1

    code = sms_sender.messages[0].split()[-1]
    response = client.post(
        "/tf-validate",
        json=dict(code=code),
        headers={"Content-Type": "application/json"},
    )
    assert response.status_code == 200
コード例 #4
0
def test_no_opt_out(app, client):
    # Test if 2FA required, can't opt-out.
    sms_sender = SmsSenderFactory.createSender("test")
    response = client.post(
        "/login",
        data=dict(email="*****@*****.**", password="******"),
        follow_redirects=True,
    )
    assert sms_sender.get_count() == 1
    code = sms_sender.messages[0].split()[-1]

    # submit right token and show appropriate response
    response = client.post("/tf-validate", data=dict(code=code), follow_redirects=True)
    assert b"Your token has been confirmed" in response.data

    response = client.post(
        "/tf-confirm", data=dict(password="******"), follow_redirects=True
    )
    assert b"You successfully confirmed password" in response.data

    response = client.get("/tf-setup", follow_redirects=True)
    assert b"Disable two factor" not in response.data

    # Try to opt-out
    data = dict(setup="disable")
    response = client.post("/tf-setup", data=data, follow_redirects=True)
    assert response.status_code == 200
    assert b"Marked method is not valid" in response.data
コード例 #5
0
def test_setup_timeout(app, client, get_message):
    # Test setup timeout
    us_authenticate(client)
    headers = {
        "Accept": "application/json",
        "Content-Type": "application/json"
    }

    sms_sender = SmsSenderFactory.createSender("test")
    response = client.post(
        "us-setup",
        json=dict(chosen_method="sms", phone="650-555-1212"),
        headers=headers,
    )
    assert response.status_code == 200
    state = response.json["response"]["state"]
    time.sleep(1)

    code = sms_sender.messages[0].split()[-1].strip(".")
    response = client.post("/us-setup/" + state,
                           json=dict(code=code),
                           headers=headers)
    assert response.status_code == 400
    assert response.json["response"]["error"].encode("utf-8") == get_message(
        "US_SETUP_EXPIRED", within=app.config["SECURITY_US_SETUP_WITHIN"])
コード例 #6
0
def test_setup_bad_phone(app, client):
    data = dict(email="*****@*****.**", password="******")
    response = client.post("/login", data=data, follow_redirects=True)
    message = b"Two-factor authentication adds an extra layer of security"
    assert message in response.data

    sms_sender = SmsSenderFactory.createSender("test")
    data = dict(setup="sms", phone="555-1212")
    response = client.post("/tf-setup", data=data, follow_redirects=True)
    assert b"Phone number not valid" in response.data
    assert sms_sender.get_count() == 0

    # Now setup good phone
    response = client.post(
        "/tf-setup", data=dict(setup="sms", phone="650-555-1212"), follow_redirects=True
    )
    assert sms_sender.get_count() == 1
    code = sms_sender.messages[0].split()[-1]
    # shouldn't get authenticator stuff when setting up SMS
    assert b"data:image/png;base64," not in response.data

    response = client.post("/tf-validate", data=dict(code=code), follow_redirects=True)
    assert b"Your token has been confirmed" in response.data
    assert not tf_in_session(get_session(response))

    headers = {"Accept": "application/json", "Content-Type": "application/json"}
    response = client.get("/tf-setup", headers=headers)
    # N.B. right now for tfa - we don't canonicalize phone number (since user
    # never has to type it in).
    assert response.json["response"]["tf_phone_number"] == "650-555-1212"
コード例 #7
0
def test_setup_new_totp(app, client, get_message):
    # us-setup has a 'generate new totp-secret' option
    # Verify that works (and existing codes no longer work)
    us_authenticate(client)

    headers = {
        "Accept": "application/json",
        "Content-Type": "application/json"
    }

    # Start by generating a good code
    sms_sender = SmsSenderFactory.createSender("test")
    response = client.post(
        "us-setup",
        json=dict(chosen_method="sms", phone="650-555-1212"),
        headers=headers,
    )
    assert response.status_code == 200
    code = sms_sender.messages[0].split()[-1].strip(".")

    # Now send correct value - this should generate new totp - so the previous 'code'
    # should no longer work
    sms_sender2 = SmsSenderFactory.createSender("test")
    response = client.post(
        "us-setup",
        json=dict(chosen_method="sms",
                  phone="650-555-1212",
                  new_totp_secret=True),
        headers=headers,
    )
    assert response.status_code == 200
    state = response.json["response"]["state"]
    # Use old code
    response = client.post("/us-setup/" + state,
                           json=dict(code=code),
                           headers=headers)
    assert response.status_code == 400

    # Use new code
    code = sms_sender2.messages[0].split()[-1].strip(".")
    response = client.post("/us-setup/" + state,
                           json=dict(code=code),
                           headers=headers)
    assert response.status_code == 200
    assert response.json["response"]["chosen_method"] == "sms"
    assert response.json["response"]["phone"] == "+16505551212"
コード例 #8
0
def test_admin_setup_reset(app, client, get_message):
    # Verify can use administrative datastore method to setup SMS
    # and that administrative reset removes access.
    sms_sender = SmsSenderFactory.createSender("test")

    data = dict(email="*****@*****.**", password="******")
    response = client.post(
        "/login", json=data, headers={"Content-Type": "application/json"}
    )
    assert response.json["response"]["tf_required"]

    # we shouldn't be logged in
    response = client.get("/profile", follow_redirects=False)
    assert response.status_code == 302
    assert response.location == "http://localhost/login?next=%2Fprofile"

    # Use admin to setup gene's SMS/phone.
    with app.app_context():
        user = app.security.datastore.find_user(email="*****@*****.**")
        totp_secret = app.security._totp_factory.generate_totp_secret()
        app.security.datastore.tf_set(user, "sms", totp_secret, phone="+442083661177")
        app.security.datastore.commit()

    response = authenticate(client, "*****@*****.**")
    session = get_session(response)
    assert session["tf_state"] == "ready"

    # Grab code that was sent
    assert sms_sender.get_count() == 1
    code = sms_sender.messages[0].split()[-1]
    response = client.post("/tf-validate", data=dict(code=code), follow_redirects=True)
    assert b"Your token has been confirmed" in response.data

    # verify we are logged in
    response = client.get("/profile", follow_redirects=False)
    assert response.status_code == 200

    # logout
    logout(client)

    # use administrative reset method
    with app.app_context():
        user = app.security.datastore.find_user(email="*****@*****.**")
        app.security.datastore.reset_user_access(user)
        app.security.datastore.commit()

    data = dict(email="*****@*****.**", password="******")
    response = client.post(
        "/login", json=data, headers={"Content-Type": "application/json"}
    )
    assert response.json["response"]["tf_required"]
    assert response.json["response"]["tf_state"] == "setup_from_login"

    # we shouldn't be logged in
    response = client.get("/profile", follow_redirects=False)
    assert response.status_code == 302
コード例 #9
0
def test_tf_not(app, client, get_message):
    # Test basic two-factor - when first factor doesn't require second (e.g. SMS)
    # 1. sign in and setup TFA
    client.post(
        "/us-signin",
        data=dict(identity="*****@*****.**", passcode="password", remember=True),
        follow_redirects=True,
    )

    sms_sender = SmsSenderFactory.createSender("test")
    data = dict(setup="sms", phone="+442083661177")
    client.post("/tf-setup", data=data, follow_redirects=True)
    code = sms_sender.messages[0].split()[-1]
    client.post("/tf-validate", data=dict(code=code), follow_redirects=True)

    # 2. setup unified sign in with SMS
    response = client.post("us-setup",
                           data=dict(chosen_method="sms",
                                     phone="650-555-1212"))
    matcher = re.match(
        r'.*<form action="([^\s]*)".*',
        response.data.decode("utf-8"),
        re.IGNORECASE | re.DOTALL,
    )
    verify_url = matcher.group(1)
    code = sms_sender.messages[0].split()[-1].strip(".")
    response = client.post(verify_url,
                           data=dict(code=code),
                           follow_redirects=True)
    assert response.status_code == 200
    assert get_message("US_SETUP_SUCCESSFUL") in response.data

    # 3. logout
    logout(client)

    # 4. sign in with SMS - should not require TFA
    client.post(
        "/us-send-code",
        data=dict(identity="*****@*****.**", chosen_method="sms"),
        follow_redirects=True,
    )

    code = sms_sender.messages[0].split()[-1].strip(".")
    response = client.post(
        "/us-signin",
        data=dict(identity="6505551212", passcode=code),
        follow_redirects=True,
    )
    assert response.status_code == 200
    # assert "sms" in auths[1][1]

    # Verify authenticated
    response = client.get("/profile", follow_redirects=False)
    assert response.status_code == 200
コード例 #10
0
def test_setup(app, client, get_message):
    us_authenticate(client)
    response = client.get("us-setup")
    assert all(
        i in response.data
        for i in [b"chosen_method-0", b"chosen_method-1", b"chosen_method-2"])

    # test missing phone
    response = client.post("us-setup",
                           data=dict(chosen_method="sms", phone=""))
    assert response.status_code == 200
    assert get_message("PHONE_INVALID") in response.data

    # test invalid phone
    response = client.post("us-setup",
                           data=dict(chosen_method="sms", phone="555-1212"))
    assert response.status_code == 200
    assert get_message("PHONE_INVALID") in response.data

    sms_sender = SmsSenderFactory.createSender("test")
    response = client.post("us-setup",
                           data=dict(chosen_method="sms",
                                     phone="650-555-1212"))
    assert response.status_code == 200
    assert b"Submit Code" in response.data
    matcher = re.match(
        r'.*<form action="([^\s]*)".*',
        response.data.decode("utf-8"),
        re.IGNORECASE | re.DOTALL,
    )
    verify_url = matcher.group(1)

    # Try invalid code
    response = client.post(verify_url,
                           data=dict(code=12345),
                           follow_redirects=True)
    assert get_message("INVALID_CODE") in response.data

    code = sms_sender.messages[0].split()[-1].strip(".")
    response = client.post(verify_url,
                           data=dict(code=code),
                           follow_redirects=True)
    assert response.status_code == 200
    assert get_message("US_SETUP_SUCCESSFUL") in response.data
コード例 #11
0
def test_setup_bad_phone(app, client):
    data = dict(email="*****@*****.**", password="******")
    response = client.post("/login", data=data, follow_redirects=True)
    message = b"Two-factor authentication adds an extra layer of security"
    assert message in response.data

    sms_sender = SmsSenderFactory.createSender("test")
    data = dict(setup="sms", phone="555-1212")
    response = client.post("/tf-setup", data=data, follow_redirects=True)
    assert b"Phone number not valid" in response.data
    assert sms_sender.get_count() == 0

    client.post(
        "/tf-setup", data=dict(setup="sms", phone="650-555-1212"), follow_redirects=True
    )
    assert sms_sender.get_count() == 1
    code = sms_sender.messages[0].split()[-1]

    response = client.post("/tf-validate", data=dict(code=code), follow_redirects=True)
    assert b"Your token has been confirmed" in response.data
    assert not tf_in_session(get_session(response))
コード例 #12
0
def test_tf(app, client, get_message):
    # Test basic two-factor - default for signing in with password.
    response = client.post(
        "/us-signin",
        data=dict(identity="*****@*****.**", passcode="password", remember=True),
        follow_redirects=True,
    )
    assert response.status_code == 200
    message = b"Two-factor authentication adds an extra layer of security"
    assert message in response.data
    assert b"Set up using SMS" in response.data

    sms_sender = SmsSenderFactory.createSender("test")
    data = dict(setup="sms", phone="+442083661177")
    response = client.post("/tf-setup", data=data, follow_redirects=True)
    assert b"To Which Phone Number Should We Send Code To" in response.data
    code = sms_sender.messages[0].split()[-1]

    response = client.post("/tf-validate",
                           data=dict(code=code),
                           follow_redirects=True)
    assert b"Your token has been confirmed" in response.data
コード例 #13
0
def test_datastore(app, client):
    # Test that user record is properly set after proper 2FA setup.
    sms_sender = SmsSenderFactory.createSender("test")
    data = dict(email="*****@*****.**", password="******")
    response = client.post("/login",
                           json=data,
                           headers={"Content-Type": "application/json"})
    assert response.json["meta"]["code"] == 200
    session = get_session(response)
    assert session["tf_state"] == "setup_from_login"

    # setup
    data = dict(setup="sms", phone="+442083661177")
    response = client.post("/tf-setup",
                           json=data,
                           headers={"Content-Type": "application/json"})

    assert sms_sender.get_count() == 1
    session = get_session(response)
    assert session["tf_state"] == "validating_profile"
    assert session["tf_primary_method"] == "sms"

    code = sms_sender.messages[0].split()[-1]

    # submit token and show appropriate response
    response = client.post("/tf-validate",
                           data=dict(code=code),
                           follow_redirects=True)
    assert b"Your token has been confirmed" in response.data
    session = get_session(response)
    # Verify that successful login clears session info
    assert not tf_in_session(session)

    with app.app_context():
        user = app.security.datastore.find_user(email="*****@*****.**")
        assert user.tf_primary_method == "sms"
        assert user.tf_phone_number == "+442083661177"
        assert "enckey" in user.tf_totp_secret
コード例 #14
0
def test_setup_json(app, client_nc, get_message):
    @us_profile_changed.connect_via(app)
    def pc(sender, user, method):
        assert method == "sms"
        assert user.us_phone_number == "+16505551212"

    token = us_authenticate(client_nc)
    headers = {
        "Authentication-Token": token,
        "Accept": "application/json",
        "Content-Type": "application/json",
    }
    response = client_nc.get("/us-setup", headers=headers)
    assert response.status_code == 200
    assert response.json["response"]["methods"] == ["email", "sms"]

    sms_sender = SmsSenderFactory.createSender("test")
    response = client_nc.post(
        "us-setup",
        json=dict(chosen_method="sms", phone="650-555-1212"),
        headers=headers,
    )
    assert response.status_code == 200
    state = response.json["response"]["state"]
    assert state

    # send invalid code
    response = client_nc.post("/us-setup/" + state,
                              json=dict(code=12344),
                              headers=headers)
    assert response.status_code == 400
    assert response.json["response"]["errors"]["code"][0].encode(
        "utf-8") == get_message("INVALID_CODE")
    code = sms_sender.messages[0].split()[-1].strip(".")
    response = client_nc.post("/us-setup/" + state,
                              json=dict(code=code),
                              headers=headers)
    assert response.status_code == 200
    assert response.json["response"]["chosen_method"] == "sms"
    assert response.json["response"]["phone"] == "+16505551212"

    # now login with phone - send in different format than we set up with.
    headers = {
        "Accept": "application/json",
        "Content-Type": "application/json"
    }

    sms_sender = SmsSenderFactory.createSender("test")
    response = client_nc.post(
        "/us-send-code",
        json=dict(identity="6505551212", chosen_method="sms"),
        headers=headers,
    )
    assert response.status_code == 200
    code = sms_sender.messages[0].split()[-1].strip(".")
    response = client_nc.post(
        "/us-signin?include_auth_token",
        json=dict(identity="*****@*****.**", passcode=code),
        headers=headers,
    )
    assert response.status_code == 200
    assert "authentication_token" in response.json["response"]["user"]
コード例 #15
0
def test_opt_in(app, client):
    """
    Test entire lifecycle of user not having 2FA - setting it up, then deciding
    to turn it back off
    All using forms based API
    """

    signalled_identity = []

    @identity_changed.connect_via(app)
    def on_identity_changed(app, identity):
        signalled_identity.append(identity.id)

    response = authenticate(client, "*****@*****.**")
    session = get_session(response)
    assert "tf_state" not in session
    # Jill is 4th user to be added in utils.py
    assert signalled_identity[0] == 4
    del signalled_identity[:]

    # opt-in for SMS 2FA - but we haven't re-verified password
    sms_sender = SmsSenderFactory.createSender("test")
    data = dict(setup="sms", phone="+442083661177")
    response = client.post("/tf-setup", data=data, follow_redirects=True)
    message = b"You currently do not have permissions to access this page"
    assert message in response.data

    # Confirm password - then opt-in
    password = "******"
    response = client.post(
        "/tf-confirm", data=dict(password=password), follow_redirects=True
    )
    data = dict(setup="sms", phone="+442083661177")
    response = client.post("/tf-setup", data=data, follow_redirects=True)
    assert b"To Which Phone Number Should We Send Code To" in response.data
    assert sms_sender.get_count() == 1
    code = sms_sender.messages[0].split()[-1]

    # Validate token - this should complete 2FA setup
    response = client.post("/tf-validate", data=dict(code=code), follow_redirects=True)
    assert b"You successfully changed" in response.data

    # Upon completion, session cookie shouldnt have any two factor stuff in it.
    session = get_session(response)
    assert not tf_in_session(session)

    # Log out
    logout(client)
    assert not signalled_identity[0]
    del signalled_identity[:]

    # Login now should require 2FA with sms
    sms_sender = SmsSenderFactory.createSender("test")
    response = authenticate(client, "*****@*****.**")
    session = get_session(response)
    assert session["tf_state"] == "ready"
    assert len(signalled_identity) == 0

    assert sms_sender.get_count() == 1
    code = sms_sender.messages[0].split()[-1]
    response = client.post("/tf-validate", data=dict(code=code), follow_redirects=True)
    assert b"Your token has been confirmed" in response.data
    # Verify now logged in
    assert signalled_identity[0] == 4
    del signalled_identity[:]

    # Now opt back out.
    # as before must reconfirm password first
    response = client.get("/tf-setup", data=data, follow_redirects=True)
    message = b"You currently do not have permissions to access this page"
    assert message in response.data

    password = "******"
    client.post("/tf-confirm", data=dict(password=password), follow_redirects=True)
    data = dict(setup="disable")
    response = client.post("/tf-setup", data=data, follow_redirects=True)
    assert b"You successfully disabled two factor authorization." in response.data

    # Log out
    logout(client)
    assert not signalled_identity[0]
    del signalled_identity[:]

    # Should be able to log in with just user/pass
    response = authenticate(client, "*****@*****.**")
    session = get_session(response)
    assert "tf_state" not in session
    # Jill is 4th user to be added in utils.py
    assert signalled_identity[0] == 4
コード例 #16
0
def test_simple_login(app, client, get_message):
    auths = []

    @user_authenticated.connect_via(app)
    def authned(myapp, user, **extra_args):
        auths.append((user.email, extra_args["authn_via"]))

    # Test missing choice
    data = dict(identity="*****@*****.**")
    response = client.post("/us-send-code", data=data, follow_redirects=True)
    assert get_message("US_METHOD_NOT_AVAILABLE") in response.data

    # Test login using invalid email
    data = dict(identity="*****@*****.**", chosen_method="email")
    response = client.post("/us-send-code", data=data, follow_redirects=True)
    assert get_message("US_SPECIFY_IDENTITY") in response.data

    # test disabled account
    data = dict(identity="*****@*****.**", chosen_method="email")
    response = client.post("/us-send-code", data=data, follow_redirects=True)
    assert b"Code has been sent" not in response.data
    assert get_message("DISABLED_ACCOUNT") in response.data

    with capture_send_code_requests() as requests:
        with app.mail.record_messages() as outbox:
            response = client.post(
                "/us-send-code",
                data=dict(identity="*****@*****.**", chosen_method="email"),
                follow_redirects=True,
            )
            assert response.status_code == 200
            assert b"Sign In" in response.data
    assert len(requests) == 1
    assert len(outbox) == 1

    # try bad code
    response = client.post(
        "/us-signin",
        data=dict(identity="*****@*****.**", passcode="blahblah"),
        follow_redirects=True,
    )
    assert get_message("INVALID_PASSWORD") in response.data

    # Correct code
    assert "remember_token" not in [c.name for c in client.cookie_jar]
    assert "session" not in [c.name for c in client.cookie_jar]
    response = client.post(
        "/us-signin",
        data=dict(identity="*****@*****.**", passcode=requests[0]["token"]),
        follow_redirects=False,
    )
    assert "remember_token" not in [c.name for c in client.cookie_jar]
    assert "email" in auths[0][1]

    response = client.get("/profile", follow_redirects=False)
    assert response.status_code == 200

    logout(client)
    response = client.get("/profile", follow_redirects=False)
    assert "/login?next=%2Fprofile" in response.location

    # login via SMS
    sms_sender = SmsSenderFactory.createSender("test")
    set_phone(app)
    response = client.post(
        "/us-send-code",
        data=dict(identity="*****@*****.**", chosen_method="sms"),
        follow_redirects=True,
    )
    assert response.status_code == 200
    assert b"Sign In" in response.data

    code = sms_sender.messages[0].split()[-1].strip(".")
    response = client.post(
        "/us-signin",
        data=dict(identity="*****@*****.**", passcode=code, remember=True),
        follow_redirects=True,
    )
    assert response.status_code == 200
    assert "remember_token" in [c.name for c in client.cookie_jar]
    assert "sms" in auths[1][1]

    response = client.get("/profile", follow_redirects=False)
    assert response.status_code == 200

    logout(client)
    assert "remember_token" not in [c.name for c in client.cookie_jar]
コード例 #17
0
def test_opt_in(app, client):
    """
    Test entire lifecycle of user not having 2FA - setting it up, then deciding
    to turn it back off
    All using forms based API
    """

    signalled_identity = []

    @identity_changed.connect_via(app)
    def on_identity_changed(app, identity):
        signalled_identity.append(identity.id)

    response = authenticate(client, "*****@*****.**")
    session = get_session(response)
    assert "tf_state" not in session
    with app.app_context():
        user = app.security.datastore.find_user(email="*****@*****.**")
        assert signalled_identity[0] == user.fs_uniquifier
    del signalled_identity[:]

    # opt-in for SMS 2FA
    sms_sender = SmsSenderFactory.createSender("test")
    data = dict(setup="sms", phone="+442083661177")
    response = client.post("/tf-setup", data=data, follow_redirects=True)
    assert b"To Which Phone Number Should We Send Code To" in response.data
    assert sms_sender.get_count() == 1
    code = sms_sender.messages[0].split()[-1]

    # Validate token - this should complete 2FA setup
    response = client.post("/tf-validate", data=dict(code=code), follow_redirects=True)
    assert b"You successfully changed" in response.data

    # Upon completion, session cookie shouldnt have any two factor stuff in it.
    session = get_session(response)
    assert not tf_in_session(session)

    # Log out
    logout(client)
    assert not signalled_identity[0]
    del signalled_identity[:]

    # Login now should require 2FA with sms
    sms_sender = SmsSenderFactory.createSender("test")
    response = authenticate(client, "*****@*****.**")
    session = get_session(response)
    assert session["tf_state"] == "ready"
    assert len(signalled_identity) == 0

    assert sms_sender.get_count() == 1
    code = sms_sender.messages[0].split()[-1]
    response = client.post("/tf-validate", data=dict(code=code), follow_redirects=True)
    assert b"Your token has been confirmed" in response.data
    # Verify now logged in
    with app.app_context():
        user = app.security.datastore.find_user(email="*****@*****.**")
        assert signalled_identity[0] == user.fs_uniquifier
    del signalled_identity[:]

    # Now opt back out.
    data = dict(setup="disable")
    response = client.post("/tf-setup", data=data, follow_redirects=True)
    assert b"You successfully disabled two factor authorization." in response.data

    # Log out
    logout(client)
    assert not signalled_identity[0]
    del signalled_identity[:]

    # Should be able to log in with just user/pass
    response = authenticate(client, "*****@*****.**")
    session = get_session(response)
    assert "tf_state" not in session
    with app.app_context():
        user = app.security.datastore.find_user(email="*****@*****.**")
        assert signalled_identity[0] == user.fs_uniquifier
コード例 #18
0
def test_json(app, client):
    """
    Test login/setup using JSON.
    """
    headers = {"Accept": "application/json", "Content-Type": "application/json"}

    # Login with someone already setup.
    sms_sender = SmsSenderFactory.createSender("test")
    data = dict(email="*****@*****.**", password="******")
    response = client.post("/login", json=data, headers=headers)
    assert response.status_code == 200
    assert response.json["response"]["tf_required"]
    assert response.json["response"]["tf_state"] == "ready"
    assert response.json["response"]["tf_primary_method"] == "sms"

    # Verify SMS sent
    assert sms_sender.get_count() == 1
    code = sms_sender.messages[0].split()[-1]
    response = client.post("/tf-validate", json=dict(code=code), headers=headers)
    assert response.status_code == 200
    # verify logged in
    response = client.get("/profile", follow_redirects=False)
    assert response.status_code == 200
    logout(client)

    # Test that user not yet setup for 2FA gets correct response.
    data = dict(email="*****@*****.**", password="******")
    response = client.post("/login", json=data, headers=headers)
    assert response.json["response"]["tf_required"]
    assert response.json["response"]["tf_state"] == "setup_from_login"

    # Start setup process.
    response = client.get("/tf-setup", headers=headers)
    assert response.json["response"]["tf_required"]
    assert "sms" in response.json["response"]["tf_available_methods"]

    # Now try to setup
    data = dict(setup="sms", phone="+442083661177")
    response = client.post("/tf-setup", json=data, headers=headers)
    assert response.status_code == 200
    assert response.json["response"]["tf_state"] == "validating_profile"
    assert response.json["response"]["tf_primary_method"] == "sms"
    code = sms_sender.messages[0].split()[-1]
    response = client.post("/tf-validate", json=dict(code=code), headers=headers)
    assert response.status_code == 200
    assert "csrf_token" in response.json["response"]
    assert response.json["response"]["user"]["email"] == "*****@*****.**"

    logout(client)

    # Verify tf is now setup and can directly get code
    data = dict(email="*****@*****.**", password="******")
    response = client.post("/login", json=data, headers=headers)
    assert response.json["response"]["tf_required"]
    assert response.json["response"]["tf_state"] == "ready"
    code = sms_sender.messages[0].split()[-1]
    response = client.post("/tf-validate", json=dict(code=code), headers=headers)
    assert response.status_code == 200
    # verify logged in
    response = client.get("/profile", follow_redirects=False)
    assert response.status_code == 200

    # tf-setup should provide existing info
    response = client.get("/tf-setup", headers=headers)
    assert response.json["response"]["tf_required"]
    assert "sms" in response.json["response"]["tf_available_methods"]
    assert "disable" not in response.json["response"]["tf_available_methods"]
    assert response.json["response"]["tf_primary_method"] == "sms"
    assert response.json["response"]["tf_phone_number"] == "+442083661177"
    assert not tf_in_session(get_session(response))
コード例 #19
0
def test_two_factor_flag(app, client):
    # trying to verify code without going through two-factor
    # first login function
    wrong_code = b"000000"
    response = client.post(
        "/tf-validate", data=dict(code=wrong_code), follow_redirects=True
    )

    message = b"You currently do not have permissions to access this page"
    assert message in response.data

    # Test login using invalid email
    data = dict(email="*****@*****.**", password="******")
    response = client.post("/login", data=data, follow_redirects=True)
    assert b"Specified user does not exist" in response.data
    response = client.post(
        "/login",
        json=data,
        headers={"Content-Type": "application/json"},
        follow_redirects=True,
    )
    assert b"Specified user does not exist" in response.data

    # Test login using valid email and invalid password
    data = dict(email="*****@*****.**", password="******")
    response = client.post("/login", data=data, follow_redirects=True)
    assert b"Invalid password" in response.data
    response = client.post(
        "/login",
        json=data,
        headers={"Content-Type": "application/json"},
        follow_redirects=True,
    )
    assert b"Invalid password" in response.data

    # Test two-factor authentication first login
    data = dict(email="*****@*****.**", password="******")
    response = client.post("/login", data=data, follow_redirects=True)
    message = b"Two-factor authentication adds an extra layer of security"
    assert message in response.data
    response = client.post(
        "/tf-setup", data=dict(setup="not_a_method"), follow_redirects=True
    )
    assert b"Marked method is not valid" in response.data
    session = get_session(response)
    assert session["tf_state"] == "setup_from_login"

    # try non-existing setup on setup page (using json)
    data = dict(setup="not_a_method")
    response = client.post(
        "/tf-setup",
        json=data,
        headers={"Content-Type": "application/json"},
        follow_redirects=True,
    )
    assert response.status_code == 400
    assert (
        response.json["response"]["errors"]["setup"][0] == "Marked method is not valid"
    )

    data = dict(setup="email")
    response = client.post(
        "/tf-setup",
        json=data,
        headers={"Content-Type": "application/json"},
        follow_redirects=True,
    )

    # Test for sms in process of valid login
    sms_sender = SmsSenderFactory.createSender("test")
    data = dict(email="*****@*****.**", password="******")
    response = client.post(
        "/login",
        json=data,
        headers={"Content-Type": "application/json"},
        follow_redirects=True,
    )
    assert b'"code": 200' in response.data
    assert sms_sender.get_count() == 1
    session = get_session(response)
    assert session["tf_state"] == "ready"

    code = sms_sender.messages[0].split()[-1]
    # submit bad token to two_factor_token_validation
    response = client.post("/tf-validate", data=dict(code=wrong_code))
    assert b"Invalid Token" in response.data

    # sumbit right token and show appropriate response
    response = client.post("/tf-validate", data=dict(code=code), follow_redirects=True)
    assert b"Your token has been confirmed" in response.data

    # Upon completion, session cookie shouldnt have any two factor stuff in it.
    assert not tf_in_session(get_session(response))

    # Test change two_factor view to from sms to mail
    with app.mail.record_messages() as outbox:
        setup_data = dict(setup="email")
        response = client.post("/tf-setup", data=setup_data, follow_redirects=True)
        msg = b"To complete logging in, please enter the code sent to your mail"
        assert msg in response.data

        # Fetch token validate form
        response = client.get("/tf-validate")
        assert response.status_code == 200
        assert b'name="code"' in response.data

    code = outbox[0].body.split()[-1]
    # sumbit right token and show appropriate response
    response = client.post("/tf-validate", data=dict(code=code), follow_redirects=True)
    assert b"You successfully changed your two-factor method" in response.data

    # Test change two_factor password confirmation view to authenticator
    # Setup authenticator
    setup_data = dict(setup="authenticator")
    response = client.post("/tf-setup", data=setup_data, follow_redirects=True)
    assert b"Open an authenticator app on your device" in response.data
    # verify png QRcode is present
    assert b"data:image/svg+xml;base64," in response.data

    # parse out key
    rd = response.data.decode("utf-8")
    matcher = re.match(r".*((?:\S{4}-){7}\S{4}).*", rd, re.DOTALL)
    totp_secret = matcher.group(1)

    # Generate token from passed totp_secret and confirm setup
    totp = TOTP(totp_secret)
    code = totp.generate().token
    response = client.post("/tf-validate", data=dict(code=code), follow_redirects=True)
    assert b"You successfully changed your two-factor method" in response.data

    logout(client)

    # Test login with remember_token
    assert "remember_token" not in [c.name for c in client.cookie_jar]
    data = dict(email="*****@*****.**", password="******", remember=True)
    response = client.post(
        "/login",
        json=data,
        headers={"Content-Type": "application/json"},
        follow_redirects=True,
    )

    # Generate token from passed totp_secret
    code = totp.generate().token
    response = client.post("/tf-validate", data=dict(code=code), follow_redirects=True)
    assert b"Your token has been confirmed" in response.data

    # Verify that the remember token is properly set
    found = False
    for cookie in client.cookie_jar:
        if cookie.name == "remember_token":
            found = True
            assert cookie.path == "/"
    assert found

    response = logout(client)
    # Verify that logout clears session info
    assert not tf_in_session(get_session(response))

    # Test two-factor authentication first login
    data = dict(email="*****@*****.**", password="******")
    response = client.post("/login", data=data, follow_redirects=True)
    message = b"Two-factor authentication adds an extra layer of security"
    assert message in response.data

    # check availability of qrcode when this option is not picked
    assert b"data:image/png;base64," not in response.data

    # check availability of qrcode page when this option is picked
    setup_data = dict(setup="authenticator")
    response = client.post("/tf-setup", data=setup_data, follow_redirects=True)
    assert b"Open an authenticator app on your device" in response.data
    assert b"data:image/svg+xml;base64," in response.data

    # check appearence of setup page when sms picked and phone number entered
    sms_sender = SmsSenderFactory.createSender("test")
    data = dict(setup="sms", phone="+442083661177")
    response = client.post("/tf-setup", data=data, follow_redirects=True)
    assert b"To Which Phone Number Should We Send Code To" in response.data
    assert sms_sender.get_count() == 1
    code = sms_sender.messages[0].split()[-1]

    response = client.post("/tf-validate", data=dict(code=code), follow_redirects=True)
    assert b"Your token has been confirmed" in response.data
    assert not tf_in_session(get_session(response))

    logout(client)

    # check when two_factor_rescue function should not appear
    rescue_data_json = dict(help_setup="lost_device")
    response = client.post(
        "/tf-rescue",
        json=rescue_data_json,
        headers={"Content-Type": "application/json"},
    )
    assert b'"code": 400' in response.data

    # check when two_factor_rescue function should appear
    data = dict(email="*****@*****.**", password="******")
    response = client.post("/login", data=data, follow_redirects=True)
    assert b"Please enter your authentication code" in response.data
    rescue_data = dict(help_setup="lost_device")
    response = client.post("/tf-rescue", data=rescue_data, follow_redirects=True)
    message = b"The code for authentication was sent to your email address"
    assert message in response.data
    rescue_data = dict(help_setup="no_mail_access")
    response = client.post("/tf-rescue", data=rescue_data, follow_redirects=True)
    message = b"A mail was sent to us in order to reset your application account"
    assert message in response.data
コード例 #20
0
def test_totp_secret_generation(app, client):
    """
    Test the totp secret generation upon changing method to make sure
    it stays the same after the process is completed
    """

    # Properly log in jill for this test
    signalled_identity = []

    @identity_changed.connect_via(app)
    def on_identity_changed(app, identity):
        signalled_identity.append(identity.id)

    response = authenticate(client, "*****@*****.**")
    session = get_session(response)
    assert "tf_state" not in session
    # Jill is 4th user to be added in utils.py
    assert signalled_identity[0] == 4
    del signalled_identity[:]

    # Confirm password
    sms_sender = SmsSenderFactory.createSender("test")
    password = "******"
    response = client.post(
        "/tf-confirm", data=dict(password=password), follow_redirects=True
    )
    # Select sms method but do not send a phone number just yet (regenerates secret)
    data = dict(setup="sms")
    response = client.post("/tf-setup", data=data, follow_redirects=True)
    assert b"To Which Phone Number Should We Send Code To" in response.data

    # Retrieve the currently generated totp secret for later comparison
    session = get_session(response)
    if "tf_totp_secret" in session:
        generated_secret = session["tf_totp_secret"]
    else:
        with app.app_context():
            user = app.security.datastore.find_user(email="*****@*****.**")
            generated_secret = user.tf_totp_secret
    assert "enckey" in generated_secret

    # Send a new phone number in the second step, method remains unchanged
    data = dict(setup="sms", phone="+442083661188")
    response = client.post("/tf-setup", data=data, follow_redirects=True)
    assert sms_sender.get_count() == 1
    code = sms_sender.messages[0].split()[-1]

    # Validate token - this should complete 2FA setup
    response = client.post("/tf-validate", data=dict(code=code), follow_redirects=True)
    assert b"You successfully changed" in response.data

    # Retrieve the final totp secret and make sure it matches the previous one
    with app.app_context():
        user = app.security.datastore.find_user(email="*****@*****.**")
        assert generated_secret == user.tf_totp_secret

    # Finally opt back out and check that tf_totp_secret is None
    response = client.get("/tf-setup", data=data, follow_redirects=True)
    message = b"You currently do not have permissions to access this page"
    assert message in response.data

    password = "******"
    client.post("/tf-confirm", data=dict(password=password), follow_redirects=True)
    data = dict(setup="disable")
    response = client.post("/tf-setup", data=data, follow_redirects=True)
    assert b"You successfully disabled two factor authorization." in response.data
    with app.app_context():
        user = app.security.datastore.find_user(email="*****@*****.**")
        assert user.tf_totp_secret is None

    # Log out
    logout(client)
    assert not signalled_identity[0]
    del signalled_identity[:]
コード例 #21
0
def test_two_factor_flag(app, client):
    # trying to verify code without going through two-factor
    # first login function
    wrong_code = b"000000"
    response = client.post(
        "/tf-validate", data=dict(code=wrong_code), follow_redirects=True
    )

    message = b"You currently do not have permissions to access this page"
    assert message in response.data

    # Test login using invalid email
    data = dict(email="*****@*****.**", password="******")
    response = client.post("/login", data=data, follow_redirects=True)
    assert b"Specified user does not exist" in response.data
    response = client.post(
        "/login",
        json=data,
        headers={"Content-Type": "application/json"},
        follow_redirects=True,
    )
    assert b"Specified user does not exist" in response.data

    # Test login using valid email and invalid password
    data = dict(email="*****@*****.**", password="******")
    response = client.post("/login", data=data, follow_redirects=True)
    assert b"Invalid password" in response.data
    response = client.post(
        "/login",
        json=data,
        headers={"Content-Type": "application/json"},
        follow_redirects=True,
    )
    assert b"Invalid password" in response.data

    # Test two-factor authentication first login
    data = dict(email="*****@*****.**", password="******")
    response = client.post("/login", data=data, follow_redirects=True)
    message = b"Two-factor authentication adds an extra layer of security"
    assert message in response.data
    response = client.post(
        "/tf-setup", data=dict(setup="not_a_method"), follow_redirects=True
    )
    assert b"Marked method is not valid" in response.data
    session = get_session(response)
    assert session["tf_state"] == "setup_from_login"

    # try non-existing setup on setup page (using json)
    data = dict(setup="not_a_method")
    response = client.post(
        "/tf-setup",
        json=data,
        headers={"Content-Type": "application/json"},
        follow_redirects=True,
    )
    assert response.status_code == 400
    assert (
        response.json["response"]["errors"]["setup"][0] == "Marked method is not valid"
    )

    data = dict(setup="email")
    response = client.post(
        "/tf-setup",
        json=data,
        headers={"Content-Type": "application/json"},
        follow_redirects=True,
    )

    # Test for sms in process of valid login
    sms_sender = SmsSenderFactory.createSender("test")
    data = dict(email="*****@*****.**", password="******")
    response = client.post(
        "/login",
        json=data,
        headers={"Content-Type": "application/json"},
        follow_redirects=True,
    )
    assert b'"code": 200' in response.data
    assert sms_sender.get_count() == 1
    session = get_session(response)
    assert session["tf_state"] == "ready"

    code = sms_sender.messages[0].split()[-1]
    # submit bad token to two_factor_token_validation
    response = client.post("/tf-validate", data=dict(code=wrong_code))
    assert b"Invalid Token" in response.data

    # sumbit right token and show appropriate response
    response = client.post("/tf-validate", data=dict(code=code), follow_redirects=True)
    assert b"Your token has been confirmed" in response.data

    # Upon completion, session cookie shouldnt have any two factor stuff in it.
    assert not tf_in_session(get_session(response))

    # try confirming password with a wrong one
    response = client.post("/tf-confirm", data=dict(password=""), follow_redirects=True)
    assert b"Password not provided" in response.data

    # try confirming password with a wrong one + json
    data = dict(password="******")
    response = client.post(
        "/tf-confirm",
        json=data,
        headers={"Content-Type": "application/json"},
        follow_redirects=True,
    )

    assert response.json["meta"]["code"] == 400

    # Test change two_factor password confirmation view to mail
    password = "******"
    response = client.post(
        "/tf-confirm", data=dict(password=password), follow_redirects=True
    )

    assert b"You successfully confirmed password" in response.data
    message = b"Two-factor authentication adds an extra layer of security"
    assert message in response.data

    # change method (from sms to mail)
    setup_data = dict(setup="email")
    testMail = TestMail()
    app.extensions["mail"] = testMail
    response = client.post("/tf-setup", data=setup_data, follow_redirects=True)
    msg = b"To complete logging in, please enter the code sent to your mail"
    assert msg in response.data

    # Fetch token validate form
    response = client.get("/tf-validate")
    assert response.status_code == 200
    assert b'name="code"' in response.data

    code = testMail.msg.body.split()[-1]
    # sumbit right token and show appropriate response
    response = client.post("/tf-validate", data=dict(code=code), follow_redirects=True)
    assert b"You successfully changed your two-factor method" in response.data

    # Test change two_factor password confirmation view to google authenticator
    password = "******"
    response = client.post(
        "/tf-confirm", data=dict(password=password), follow_redirects=True
    )
    assert b"You successfully confirmed password" in response.data
    message = b"Two-factor authentication adds an extra layer of security"
    assert message in response.data

    # Setup authenticator
    setup_data = dict(setup="authenticator")
    response = client.post("/tf-setup", data=setup_data, follow_redirects=True)
    assert b"Open your authenticator app on your device" in response.data

    # Now request code. We can't test the qrcode easily - but we can get the totp_secret
    # that goes into the qrcode and make sure that works
    mtf = Mock(wraps=app.security._totp_factory)
    app.security.totp_factory(mtf)
    qrcode_page_response = client.get(
        "/tf-qrcode", data=setup_data, follow_redirects=True
    )
    assert mtf.get_totp_uri.call_count == 1
    (username, totp_secret), _ = mtf.get_totp_uri.call_args
    assert username == "*****@*****.**"
    assert b"svg" in qrcode_page_response.data

    # Generate token from passed totp_secret and confirm setup
    code = app.security._totp_factory.generate_totp_password(totp_secret)
    response = client.post("/tf-validate", data=dict(code=code), follow_redirects=True)
    assert b"You successfully changed your two-factor method" in response.data

    logout(client)

    # Test login with remember_token
    assert "remember_token" not in [c.name for c in client.cookie_jar]
    data = dict(email="*****@*****.**", password="******", remember=True)
    response = client.post(
        "/login",
        json=data,
        headers={"Content-Type": "application/json"},
        follow_redirects=True,
    )

    # Generate token from passed totp_secret
    code = app.security._totp_factory.generate_totp_password(totp_secret)
    response = client.post("/tf-validate", data=dict(code=code), follow_redirects=True)
    assert b"Your token has been confirmed" in response.data

    # Verify that the remember token is properly set
    found = False
    for cookie in client.cookie_jar:
        if cookie.name == "remember_token":
            found = True
            assert cookie.path == "/"
    assert found

    response = logout(client)
    # Verify that logout clears session info
    assert not tf_in_session(get_session(response))

    # Test two-factor authentication first login
    data = dict(email="*****@*****.**", password="******")
    response = client.post("/login", data=data, follow_redirects=True)
    message = b"Two-factor authentication adds an extra layer of security"
    assert message in response.data

    # check availability of qrcode page when this option is not picked
    qrcode_page_response = client.get("/two_factor_qrcode/", follow_redirects=False)
    assert qrcode_page_response.status_code == 404

    # check availability of qrcode page when this option is picked
    setup_data = dict(setup="authenticator")
    response = client.post("/tf-setup", data=setup_data, follow_redirects=True)
    assert b"Open your authenticator app on your device" in response.data

    qrcode_page_response = client.get(
        "/tf-qrcode", data=setup_data, follow_redirects=True
    )
    print(qrcode_page_response)
    assert b"svg" in qrcode_page_response.data

    # check appearence of setup page when sms picked and phone number entered
    sms_sender = SmsSenderFactory.createSender("test")
    data = dict(setup="sms", phone="+442083661177")
    response = client.post("/tf-setup", data=data, follow_redirects=True)
    assert b"To Which Phone Number Should We Send Code To" in response.data
    assert sms_sender.get_count() == 1
    code = sms_sender.messages[0].split()[-1]

    response = client.post("/tf-validate", data=dict(code=code), follow_redirects=True)
    assert b"Your token has been confirmed" in response.data
    assert not tf_in_session(get_session(response))

    logout(client)

    # check when two_factor_rescue function should not appear
    rescue_data_json = dict(help_setup="lost_device")
    response = client.post(
        "/tf-rescue",
        json=rescue_data_json,
        headers={"Content-Type": "application/json"},
    )
    assert b'"code": 400' in response.data

    # check when two_factor_rescue function should appear
    data = dict(email="*****@*****.**", password="******")
    response = client.post("/login", data=data, follow_redirects=True)
    assert b"Please enter your authentication code" in response.data
    rescue_data = dict(help_setup="lost_device")
    response = client.post("/tf-rescue", data=rescue_data, follow_redirects=True)
    message = b"The code for authentication was sent to your email address"
    assert message in response.data
    rescue_data = dict(help_setup="no_mail_access")
    response = client.post("/tf-rescue", data=rescue_data, follow_redirects=True)
    message = b"A mail was sent to us in order" + b" to reset your application account"
    assert message in response.data
コード例 #22
0
def test_simple_login_json(app, client_nc, get_message):
    auths = []

    @user_authenticated.connect_via(app)
    def authned(myapp, user, **extra_args):
        auths.append((user.email, extra_args["authn_via"]))

    headers = {
        "Accept": "application/json",
        "Content-Type": "application/json"
    }

    with capture_flashes() as flashes:

        response = client_nc.get("/us-signin", headers=headers)
        assert (response.json["response"]["methods"] ==
                app.config["SECURITY_US_ENABLED_METHODS"])
        assert (response.json["response"]["identity_attributes"] ==
                app.config["SECURITY_USER_IDENTITY_ATTRIBUTES"])

        with capture_send_code_requests() as requests:
            with app.mail.record_messages() as outbox:
                response = client_nc.post(
                    "/us-send-code",
                    json=dict(identity="*****@*****.**", chosen_method="email"),
                    headers=headers,
                    follow_redirects=True,
                )
                assert response.status_code == 200
                assert "csrf_token" in response.json["response"]
                assert "user" not in response.json["response"]
        assert len(requests) == 1
        assert len(outbox) == 1

        # try bad code
        response = client_nc.post(
            "/us-signin",
            json=dict(identity="*****@*****.**", passcode="blahblah"),
            headers=headers,
            follow_redirects=True,
        )
        assert response.status_code == 400
        assert response.json["response"]["errors"]["passcode"][0].encode(
            "utf-8") == get_message("INVALID_PASSWORD")

        # Login successfully with code
        response = client_nc.post(
            "/us-signin?include_auth_token",
            json=dict(identity="*****@*****.**", passcode=requests[0]["token"]),
            headers=headers,
            follow_redirects=True,
        )
        assert response.status_code == 200
        assert "authentication_token" in response.json["response"]["user"]
        assert "email" in auths[0][1]

        logout(client_nc)
        response = client_nc.get("/profile",
                                 headers=headers,
                                 follow_redirects=False)
        assert response.status_code == 401

        # login via SMS
        sms_sender = SmsSenderFactory.createSender("test")
        set_phone(app)
        response = client_nc.post(
            "/us-send-code",
            json=dict(identity="*****@*****.**", chosen_method="sms"),
            headers=headers,
            follow_redirects=True,
        )
        assert response.status_code == 200

        code = sms_sender.messages[0].split()[-1].strip(".")
        response = client_nc.post(
            "/us-signin?include_auth_token",
            json=dict(identity="*****@*****.**", passcode=code),
            headers=headers,
            follow_redirects=True,
        )
        assert response.status_code == 200
        assert "authentication_token" in response.json["response"]["user"]
    assert len(flashes) == 0