Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Login approvals are on. Expect an SMS shortly with a code to use for log in (406) #1

Closed
usvi opened this issue Jul 6, 2022 · 34 comments

Comments

@usvi
Copy link
Owner

usvi commented Jul 6, 2022

When taking plain bitlbee-facebook, compiling and running it, we get something like this:

05:08 <@root> facebook - Logging in: Authenticating
05:08 <@root> facebook - Login error: Login approvals are on. Expect an SMS shortly with a code to use for log in (406)
05:08 <@root> facebook - Logging in: Signing off..
05:08 <@root> facebook - Logging in: Reconnecting in 15 seconds..

@usvi
Copy link
Owner Author

usvi commented Jul 6, 2022

Complete set I'm using to compile and set running foreground while it is not running yet

make && make install && libtool --finish /opt/bitlbee/lib/bitlbee/ && /opt/bitlbee/sbin/bitlbee -D -u bitlbee -n

@usvi
Copy link
Owner Author

usvi commented Jul 6, 2022

/**
 * FbApi:token:
 *
 * The access token for authentication. This value should be
 * saved and loaded for persistence.
 */
props[PROP_TOKEN] = g_param_spec_string(
    "token",
    "Access Token",
    "Access token for authentication",
    NULL,
    G_PARAM_READWRITE);

@usvi
Copy link
Owner Author

usvi commented Jul 6, 2022

Token manipulation from IRC "command line"

Addition:
05:59 <@usvi> account facebook set token foobar
05:59 <@root> token = `foobar'

Checking:
06:00 <@usvi> account facebook set token
06:00 <@root> token = `foobar'

Deletion:
05:58 <@usvi> account facebook set -del token
05:58 <@root> Setting changed successfully

Unrecognized settings are ignored, yay!

@usvi
Copy link
Owner Author

usvi commented Jul 6, 2022

/**
 * FbApi::auth:
 * @api: The #FbApi.
 *
 * Emitted upon the successful completion of the authentication
 * process. This is emitted as a result of #fb_api_auth().
 */
g_signal_new("auth",
             G_TYPE_FROM_CLASS(klass),
             G_SIGNAL_ACTION,
             0,
             NULL, NULL,
             fb_marshal_VOID__VOID,
             G_TYPE_NONE,
             0);

@usvi
Copy link
Owner Author

usvi commented Jul 6, 2022

Do we need a new pre-auth signal? How does work login do things?

@usvi
Copy link
Owner Author

usvi commented Jul 6, 2022

WORK login:

void
fb_api_work_login(FbApi *api, gchar *user, gchar *pass)
{
    FbApiPrivate *priv = api->priv;
    FbHttpRequest *req;
    FbHttpValues *prms, *hdrs;
    FbApiPreloginData *pata = g_new0(FbApiPreloginData, 1);

    pata->api = api;
    pata->user = user;
    pata->pass = pass;

    priv->is_work = TRUE;

    req = fb_http_request_new(priv->http, FB_API_URL_WORK_PRELOGIN, TRUE,
        fb_api_cb_work_prelogin, pata);

    hdrs = fb_http_request_get_headers(req);
    fb_http_values_set_str(hdrs, "Authorization", "OAuth null");

    prms = fb_http_request_get_params(req);
    fb_http_values_set_str(prms, "email", user);
    fb_http_values_set_str(prms, "access_token",
        FB_WORK_API_KEY "|" FB_WORK_API_SECRET);

    fb_http_request_send(req);
}

static void
fb_api_cb_work_prelogin(FbHttpRequest *req, gpointer data)
{
    FbApiPreloginData *pata = data;
    FbApi *api = pata->api;
    FbApiPrivate *priv = api->priv;
    GError *err = NULL;
    JsonNode *root;
    gchar *status;
    gchar *user = pata->user;
    gchar *pass = pata->pass;

    g_free(pata);

    if (!fb_api_http_chk(api, req, &root)) {
        return;
    }

    status = fb_json_node_get_str(root, "$.status", &err);

    FB_API_ERROR_EMIT(api, err,
        json_node_free(root);
        return;
    );

    if (g_strcmp0(status, "can_login_password") == 0) {
        fb_api_auth(api, user, pass, "work_account_password");

    } else if (g_strcmp0(status, "can_login_via_linked_account") == 0) {
        fb_api_auth(api, user, pass, "personal_account_password_with_work_username");
        priv->need_work_switch = TRUE;

    } else if (g_strcmp0(status, "can_login_sso") == 0) {
        g_signal_emit_by_name(api, "work-sso-login");

    } else if (g_strcmp0(status, "cannot_login") == 0) {
        char *reason = fb_json_node_get_str(root, "$.cannot_login_reason", NULL);

        if (g_strcmp0(reason, "non_business_email") == 0) {
            fb_api_error(api, FB_API_ERROR_AUTH,
                         "Cannot login with non-business email. "
                         "Change the 'username' setting or disable 'work'");
        } else {
            char *title = fb_json_node_get_str(root, "$.error_title", NULL);
            char *body = fb_json_node_get_str(root, "$.error_body", NULL);

            fb_api_error(api, FB_API_ERROR_AUTH,
                         "Work prelogin failed (%s - %s)", title, body);

            g_free(title);
            g_free(body);
        }

        g_free(reason);

    } else if (g_strcmp0(status, "can_self_invite") == 0) {
        fb_api_error(api, FB_API_ERROR_AUTH, "Unknown email. "
                     "Change the 'username' setting or disable 'work'");
    }

    g_free(status);
    json_node_free(root);
}

So basically on success calls fb_api_cb_work_prelogin->fb_api_auth and on error fb_api_error .

fb_api_error finally emits "error" signal.

fb_api_auth calls fb_api_cb_auth when returning, which finally emits "auth" signal.

@usvi
Copy link
Owner Author

usvi commented Jul 10, 2022

Parapharsing myself from bitlbee#108 :

"I used this script: https://gist.github.com/Tatsujin/953551fe38d8e38aac43b423998d3deb/raw/fdad42d41161f5b6ea0f9d343e2057f99e4db8fb/bitlbee-fb-login-2fa.py

I butchered the script and copied uid, did and mid from bitlbee nick.xml . uid was 0 ! I changed it temporarily to my actual fb uid. MAYBE IT WORKS FOR 0 , I DONT KNOW.

I enabled SMS 2-factor auth in FB.

I ran the butchered script, got SMS, I entered the SMS code (6 numbers) to the script, then I got

Access token: FLIAIJ9z82u3o8muasdodsfijodifjmOIJOIJoijfOIJOIJmu9z82u3o8muasdodsfijodifjmOIJOIJoijfOIJOIJmu9z82u3o8muasdodsfijodifjmOIJOIJoijfOIJOIJmu9z82u3o8muasdodsfijodifjmOIJOIJoijfOIJOIJmu9z82u3o8muasdodsfijodifjmOIJOIJoi
Traceback (most recent call last):
File "./bitlbee-fb-login-2fa.py", line 157, in
if ( DID != response['device_id'] ):
KeyError: 'device_id'

I put the access token to my irc client ( account facebook set token FLIAIJ9z82u3o8muasdod... )

And it worked. I don't know what exactly was needed but this worked for me, thank heavens."

@usvi
Copy link
Owner Author

usvi commented Jul 10, 2022

Here's from the script essentials:

headers = {"Content-type": "application/x-www-form-urlencoded", "Accept": "/"}
conn = http.client.HTTPSConnection('b-api.facebook.com:443')
params = urlencode(fb_sig(data), quote_via=quote_plus)
conn.request('POST', '/method/auth.login', params, headers)
response = conn.getresponse()
debug("status, reason: %s, %s" % (response.status, response.reason))
response_data = response.read()
debug("undecoded response: %s" % response_data)
response = json.loads(response_data.decode('utf-8'))

check to make sure that worked...

if response['error_code'] != 406:
print(
"ERROR: Incorrect password, 2-fac is not enabled, or some other issue."
" Dumping results:\n\n\t",
end=''
)
print(response)
sys.exit(1)

code = input('Code: ')

error_data = json.loads(response['error_data'])
first_fac = error_data['login_first_factor']

data['credentials_type'] = 'two_factor'
data['error_detail_type'] = 'button_with_disabled'
data['first_factor'] = first_fac
data['twofactor_code'] = code
data['password'] = data['twofactor_code']
data['userid'] = error_data['uid']
data['machine_id'] = error_data['machine_id']

params = urlencode(fb_sig(data))
conn.request('POST', '/method/auth.login', params, headers)
response = conn.getresponse()
debug("status, reason: %s, %s" % (response.status, response.reason))
response_data = response.read()
debug("undecoded response: %s" % response_data)
response = json.loads(response_data.decode('utf-8'))

print("Access token:", response['access_token'])

@usvi
Copy link
Owner Author

usvi commented Jul 11, 2022

Lets reason again.

  1. There is nothing wrong in fb_api_auth() if (full length) token is present => don't change

  2. Don't start doing stupid stuff like heuristically determining with previous login state information how the current stuff should be handled. I mean, use callbacks, callbacks, callbacks until it is authed successfully

  3. Well, do one heuristics. If token small enough, consider it as 2fa code.

@usvi
Copy link
Owner Author

usvi commented Jul 11, 2022

Made TWOFACTOR_CODE / twofactor_code changes. See xml:

123456

Making it actually so that IF this exists, then run twofactor_prelogin.

@usvi
Copy link
Owner Author

usvi commented Jul 11, 2022

Handy debug messages can be printed:

imcb_log(ic, "Authenticating just auth");

@usvi
Copy link
Owner Author

usvi commented Jul 11, 2022

Random gibberish:

08:19 <@root> facebook - Logging in: Reconnecting in 5 seconds..
08:19 <@root> facebook - Logging in: Failing because stoken
08:19 <@root> facebook - Logging in: Failing because token
08:19 <@root> facebook - Logging in: Failing because twofactor_code

@usvi
Copy link
Owner Author

usvi commented Jul 12, 2022

Mystery: With token, how does the login flow go? I don't have a work account.

@usvi
Copy link
Owner Author

usvi commented Jul 12, 2022

Found:

static FbHttpRequest *
fb_api_http_req(FbApi *api, const gchar *url, const gchar *name,
const gchar *method, FbHttpValues *values,
FbHttpFunc func)
{
...
if (priv->token != NULL) {
hdrs = fb_http_request_get_headers(req);
fb_http_values_set_strf(hdrs, "Authorization", "OAuth %s", priv->token);
}

Also, in
static void
fb_api_cb_auth(FbHttpRequest *req, gpointer data):
...
g_free(priv->token);
priv->token = fb_json_values_next_str_dup(values, NULL);

EDIT: Yes:
SETTING priv->token as OAUTH!

So, fb_api_http_req does it

@usvi
Copy link
Owner Author

usvi commented Jul 12, 2022

In Facebook.com, this is how to go check your authorized devices:

Top right profile picture => Settings & privacy => Settings => Security and login

Or more directly here:
https://www.facebook.com/settings?tab=security

@usvi
Copy link
Owner Author

usvi commented Jul 12, 2022

Proper way to do things seems to be (now while debugging):

  1. Try to connect with bitlbee-facebook, get message Login error: Login approvals are on. Expect an SMS shortly with a code to use for log in (406)
  2. Temporarily set acc facebook off
  3. Get UID, DID, MID manually from bitlbee via IRC (acc facebook set mid , for example)
  4. Put manually the info to bitlbee-fb-login-2fa.py
  5. Run script, give password, get token
  6. Go to facebook.com to tell that your device was you
  7. Put token to bitlbee via irc (acc facebook set token 89iuhsadisaudaushdiaushdusihd... )
  8. Set acc facebook on

@usvi
Copy link
Owner Author

usvi commented Jul 13, 2022

Priority one: Need to print and save login_first_factor when response is 406.

@usvi
Copy link
Owner Author

usvi commented Jul 13, 2022

Try to intercept in fb_api_cb_auth ?

@usvi
Copy link
Owner Author

usvi commented Jul 13, 2022

static gboolean
fb_api_json_chk(FbApi *api, gconstpointer data, gssize size, JsonNode **node)
{

Adding this print:

    if (msg != NULL) {
        printf("msg = %s\n", msg);
        success = FALSE;
        break;
    }

Gets me this:
msg = Login approvals are on. Expect an SMS shortly with a code to use for log in (406)

@usvi
Copy link
Owner Author

usvi commented Jul 13, 2022

Printing the full json:

Full json: {"error_code":406,"error_msg":"Login approvals are on. Expect an SMS shortly with a code to use for log in (406)","error_data":"{"uid":1000010492XXXX,"login_first_factor":"99ucgfzVsMBKCLN0M9B2iD0ZApeqnxPq",

blaah blaah

Here we have first factor. Now just need to extract and same it.

@usvi
Copy link
Owner Author

usvi commented Jul 14, 2022

Losing my mind. I think I have everything. Now even the original, unmodified script is not working.

But turns out my telco is not sending SMS to here, Mexico anymore.

@usvi
Copy link
Owner Author

usvi commented Jul 14, 2022

@usvi
Copy link
Owner Author

usvi commented Jul 14, 2022

GREAT NEWS!

One can go to https://www.facebook.com/security/2fac/settings/

Then select Recovery codes and use them.

THE EXTERNAL SCRIPT WORKS THUSLY! Now I can actually verify the my bitlbee changes.

@usvi
Copy link
Owner Author

usvi commented Jul 15, 2022

There is now confusion about variables.

List follows:

DID
JSON: "device_id"
UUID!!! Genereted by bitlbee-facebook if empty.
OK.

MID
JSON: "mid"
Some kind of message id. Maybe not needed in auth.
NOTE: SCRIPT USES "MID" BUT THIS IS LIKELY WRONG. It tries to get this info from .xml so what it is getting is MQTT message id. But it sends it as machine_id.
Actual machine_id does not (yet) exist in bitlbee-facebook. Maybe I need to make it.

UID
JSON: "uid"
Auth callback sets this for us. I see some stupid variance of type (str vs. int?) based of it is work account or not.

CID
JSON: "cid"
Some kind of client identifier. Not used in auth. Used/written in "normal" communication. Bitlbee-facebook creates this, says it is 32byte alphanumeric.

machine_id
As told, does not exist yet.

@usvi
Copy link
Owner Author

usvi commented Jul 15, 2022

When creating new account on fresh project, uid is empty. We need to build from this I'm afraid. uid is present in the error data, so we could harvest it.

Lets make a prop for machine_id and then make it to be generated also and then in one function store uid, machine_id, login_first_factor

@usvi
Copy link
Owner Author

usvi commented Jul 15, 2022

Random ramble:

This is our auth func and see the callback:

void
fb_api_auth(FbApi *api, const gchar *user, const gchar *pass, const gchar *credentials_type)
{
FbApiPrivate *priv = api->priv;
FbHttpValues *prms;

prms = fb_http_values_new();
fb_http_values_set_str(prms, "email", user);
fb_http_values_set_str(prms, "password", pass);

if (credentials_type) {
    fb_http_values_set_str(prms, "credentials_type", credentials_type);
}

if (priv->sso_verifier) {
    fb_http_values_set_str(prms, "code_verifier", priv->sso_verifier);
    g_free(priv->sso_verifier);
    priv->sso_verifier = NULL;
}

if (priv->work_community_id) {
    fb_http_values_set_int(prms, "community_id", priv->work_community_id);
}

if (priv->is_work && priv->token) {
    fb_http_values_set_str(prms, "access_token", priv->token);
}

fb_api_http_req(api, FB_API_URL_AUTH, "authenticate", "auth.login", prms,
                fb_api_cb_auth);

}

@usvi
Copy link
Owner Author

usvi commented Jul 15, 2022

Private note: Asking for generation works. Now I need to make function to scoop the data.

@usvi
Copy link
Owner Author

usvi commented Jul 15, 2022

IN

static gboolean
fb_api_http_chk(FbApi *api, FbHttpRequest *req, JsonNode **root)

Making

// For 406 / 2FA / Login approvals, use specific handler

if (err != NULL && err->code == 406) {
    fb_api_json_update_data_from_error_response(api, data, size, root);

    if (G_UNLIKELY(err != NULL)) {
        g_error_free(err);
    }

    return FALSE; // Returns always false so we don't disturb much
}

The function will be a partial copy of fb_api_json_chk()

@usvi
Copy link
Owner Author

usvi commented Jul 16, 2022

Looks like commit 000d974 fixes this.

See it in action:

image

Needs a bit of testing for sure though.

@usvi
Copy link
Owner Author

usvi commented Jul 22, 2022

Made a simple test by invalidating my login. Result:

image

So I get the new token fine and can continue without issues.

In https://www.facebook.com/settings/?tab=security it is clearly listed as:

image

So, so far so good.

@usvi
Copy link
Owner Author

usvi commented Jul 22, 2022

Quick check on memory.

root@comms:~/tmp/bitlbee/bitlbee-facebook/facebook# ps aux | grep bitl
bitlbee 14925 0.0 2.0 35260 21184 pts/2 S+ Jul16 0:07 /opt/bitlbee/sbin/bitlbee -D -u bitlbee -n
root 17803 0.0 0.0 6072 888 pts/1 S+ 18:43 0:00 grep bitl

Then I do the log off:
bitlbee 14925 0.0 2.0 35260 21184 pts/2 S+ Jul16 0:07 /opt/bitlbee/sbin/bitlbee -D -u bitlbee -n
root 17805 0.0 0.0 6072 888 pts/1 S+ 18:47 0:00 grep bitl

Then I activate the account again:
bitlbee 14925 0.0 2.0 35260 21184 pts/2 S+ Jul16 0:07 /opt/bitlbee/sbin/bitlbee -D -u bitlbee -n
root 17807 0.0 0.0 6072 816 pts/1 S+ 18:48 0:00 grep bitl

And then I put twofactor in and get logged in:
bitlbee 14925 0.0 2.0 35260 21184 pts/2 S+ Jul16 0:08 /opt/bitlbee/sbin/bitlbee -D -u bitlbee -n
root 17809 0.0 0.0 6072 820 pts/1 S+ 18:49 0:00 grep bitl

I see no imminent memory leaks. And no crashes. Considering this working.

@usvi usvi closed this as completed Jul 22, 2022
@usvi
Copy link
Owner Author

usvi commented Jul 22, 2022

Note for posterity: With the machine_id changes I now get only one auth code. Previously I think got multiple. Could be due to other reasons but I feel this change has possibly stabilized the thing.

@usvi
Copy link
Owner Author

usvi commented Nov 5, 2022

Force pushed both changes to commit 31b56ec because nobody else was probably interested in this in the first place.

@usvi
Copy link
Owner Author

usvi commented Nov 21, 2022

Actually made this now into a branch as requested in bitlbee#215

Branch is here:

https://github.com/usvi/bitlbee-facebook/tree/automatic-2fa-tokens

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant