airbnbapi: error code 420

I try to get a token with: let token = airbnb.newAccessToken({username:'user', password:'password'})

and I get this error msg:


{ error_code: 420,
  error: 'unknown_error',
  error_message: 'Unable to perform action. Please try again through the website or contact support if you need immediate assistance.',
  client_error_info:
   { airlock:
      { action_name: 'account_login',
        completion_redirect_url: '',
        error_redirect_url: '',
        escapable: true,
        fallback_friction: 'contact_us_form',
        first_name: 'xxxxx',
        flow: 'captcha_flow',
        friction_data: [Object],
        header_text: 'Please verify yourself',
        id: 12345678,
        identifier: null,
        keep_webview_open_on_redirect: false,
        redux: true,
        status: 0,
        should_replay_request: true,
        user_id: 876654321,
        user_message: null },
     metadata: {} } }

About this issue

  • Original URL
  • State: open
  • Created 6 years ago
  • Reactions: 2
  • Comments: 89 (10 by maintainers)

Most upvoted comments

I’ve tried using mitmproxy to intercept the Android app and so far this is what I get (this may be a repeat of previous info but I just wanted to keep it all clear)

  1. As soon as I log in from a previously unused location
POST https://api.airbnb.com/v2/logins?client_id=<CLIENT_ID>&locale=<LOCALE>&currency=<CURRENCY> HTTP/2.0

Request: Handled by airbnbapi already

Response:

HTTP Code 420
{
  "error_code": 420,
  "error_type": "inline_risk_error",
  "error_message": "Unfortunately, a server error prevented your request from being completed. Airbnb may be undergoing maintenance or your connection may have timed out. Please try again.",
  "client_error_info": {
    "airlock": {
      "friction_data": [ // Verification methods. We could potentially use this programmatically
        {
          "name": "phone_verification_via_text",
          "data": {
            "phone_numbers": [
              {
                "id": 0000000,
                "last_four_digits": "xxxx",
                "obfuscated": "+xxxxxxxxxx",
                "verification_method": 1,
                "verified_at": "xxxxxxx"
              }
            ],
            "verification_code_num_digits": 4
          },
          "status": 0,
          "style": "modal",
          "version": "1.0"
        },
        {
          "name": "phone_verification_via_call",
          "data": {
            "phone_numbers": [
              {
                "id": 000000000,
                "last_four_digits": "xxxx",
                "obfuscated": "+xxxxxxxxxxxxx",
                "verification_method": 1,
                "verified_at": "xxxxxxxxxxx"
              }
            ],
            "verification_code_num_digits": 4
          },
          "status": 0,
          "style": "modal",
          "version": "1.0"
        },
        {
          "name": "email_code_verification",
          "data": {
            "delivery_methods": [
              {
                "id": 4,
                "text": "Verify via Email"
              }
            ],
            "obfuscated_email_address": "xxxxxxxxxxx",
            "verification_code_num_digits": 4
          },
          "status": 0,
          "style": "full_page_redirect",
          "version": "1.0"
        }
        // ...
      ],
      "header_text": "Please verify yourself",
      "id": AIRLOCK_ID,
      "identifier": null,
      "keep_webview_open_on_redirect": false,
      "redux": true,
      "status": 0,
      "should_replay_request": true,
      "user_id": USER_ID,
      "user_message": null,
      "bill_version_token": null
    },
    "metadata": {}
  },
  "error_details": "Unable to perform action. Please try again through the website or contact support if you need immediate assistance.",
  "error_id": "00000000000000000000000000000000"
}

Out of this, client_error_info.airlock.id (Airlock ID) and client_error_info.airlock.user_id is important.

  1. Choosing a verification method. I chose email
PUT https://api.airbnb.com/v2/airlocks/AIRLOCK_ID?_format=v1&client_id=<CLIENT ID>&locale=<LOCALE>&currency=<CURRENCY> HTTP/2.0

Request:

{
    "action_name": "account_login",
    "attempt": true,
    "friction": "email_code_verification",
    "friction_data": {},
    "id": <AIRLOCK ID>,
    "user_id": <USER ID>
}

Response: Similar to response in 1. above. HTTP Code 200

  1. After entering verification code
PUT https://api.airbnb.com/v2/airlocks/<AIRLOCK ID>?_format=v1&client_id=<CLIENT ID>&locale=<LOCALE>&currency=<CURRENCY> HTTP/2.0

Request:

{
    "action_name": "account_login",
    "friction": "email_code_verification",
    "friction_data": {
        "response": {
            "code": "<CODE FROM EMAIL>"
        }
    },
    "id": <AIRLOCK ID>,
    "user_id": <USER ID>
}

Response: Similar to 1. above. HTTP Code 200

  1. Login again
POST https://api.airbnb.com/v2/logins?client_id=<CLIENT_ID>&locale=<LOCALE>&currency=<CURRENCY> HTTP/2.0

Request: Handled by airbnbapijs already

Response:

HTTP Code 200
{
    "login": {
        "account": {
            "badges": [
                {
                // ...

From this, the noteworthy point is their use of PUT instead of POST or GET for airlock. That could explain some of the errors above. I’m sorry if anything’s repeated here 😃

I just completed an airlock verification via the web interface and sniffed the traffic, maybe this will help implement the verification?

curl 'https://www.airbnb.hu/api/v2/airlocks/AIRLOCK_ID?key=AUTHKEY&_format=v1' -X PUT -H 'Accept: application/json' -H 'Referer: https://www.airbnb.hu/airlock?al_id=AIRLOCK_ID' -H 'Origin: https://www.airbnb.hu' -H 'X-CSRF-Token: V4$.airbnb.hu$ht-xJp_5PrA$pCBMg_qXfZvN1ZXPAw7YlsjGVhszbh3QsmLApO59GPM=' -H 'User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36' -H 'Content-Type: application/json' --data-binary '{"friction":"phone_verification_via_text","friction_data":{"optionSelection":{"phone_number_id":7531677}},"attempt":true,"enable_throw_errors":true}' --compressed

{"airlock":{"action_name":"account_login","completion_redirect_url":"https://www.airbnb.hu/airlock?al_id=AIRLOCK_ID","error_redirect_url":"","escapable":false,"fallback_friction":"contact_us_form","first_name":"Akos","flow":"account_ownership_verification_for_login","friction_data":[{"name":"phone_verification_via_text","data":{"phone_numbers":[{"id":7531677,"last_four_digits":"YYYY","obfuscated":"+XX (•••) •••-YYYY","verification_method":1,"verified_at":"2014-06-14 08:26:31 UTC"}],"verification_code_num_digits":4},"status":1,"style":"modal","version":"1.0"},{"name":"phone_verification_via_call","data":{"phone_numbers":[{"id":7531677,"last_four_digits":"YYYY","obfuscated":"+XX (•••) •••-YYYY","verification_method":1,"verified_at":"2014-06-14 08:26:31 UTC"}],"verification_code_num_digits":4},"status":0,"style":"modal","version":"1.0"},{"name":"email_code_verification","data":{"delivery_methods":[{"id":4,"text":"Igazolás e-mailben"}],"obfuscated_email_address":"ax•••••@gm•••••.com","verification_code_num_digits":4},"status":0,"style":"full_page_redirect","version":"1.0"},{"name":"contact_us_form","data":{"min_length":25,"max_length":2000},"status":0,"style":"full_page_redirect","version":"1.0"},{"name":"facebook_verification","data":{},"status":0,"style":"full_page_redirect","version":"1.0"}],"header_text":"Igazold magad","id":AIRLOCK_ID,"identifier":"1532332066_Nr97FAEY5rKTLsiw","keep_webview_open_on_redirect":false,"redux":true,"status":1,"should_replay_request":false,"user_id":13381212,"user_message":null},"metadata":{}}



curl 'https://www.airbnb.hu/api/v2/airlocks/AIRLOCK_ID?key=AUTH_KEY&_format=v1' -X PUT -H 'origin: https://www.airbnb.hu' -H 'accept-encoding: gzip, deflate, br' -H 'x-csrf-token: V4$.airbnb.hu$ht-xJp_5PrA$pCBMg_qXfZvN1ZXPAw7YlsjGVhszbh3QsmLApO59GPM=' -H 'accept-language: en-US,en;q=0.9,hu;q=0.8,ro;q=0.7' -H 'user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36' -H 'content-type: application/json' -H 'accept: application/json' -H 'referer: https://www.airbnb.hu/airlock?al_id=AIRLOCK_ID' -H 'authority: www.airbnb.hu' -H 'cookie: bev=1532332066_Nr97FAEY5rKTLsiw; __svt=-1; cache_state=0; 3b689aa21=treatment; jitney_client_session_id=dba5d68d-c744-4492-99d2-64673c758572; jitney_client_session_created_at=1532332067; sdid=; ftv=1532332062729; AMP_TOKEN=%24NOT_FOUND; _ga=GA1.2.331815889.1532332063; _gid=GA1.2.4328824.1532332063; __ssid=ce94a024-27f0-4945-9b85-a71e221cdd3c; _csrf_token=V4%24.airbnb.hu%24ht-xJp_5PrA%24pCBMg_qXfZvN1ZXPAw7YlsjGVhszbh3QsmLApO59GPM%3D; li=1; _pt=1--WyI0OTVmZTE0ZjE5ZGEyZmVlOTAyNTkwYmJmNDY3YWQ0Zjg5OWZhN2E2Il0%3D--e9338f644118c4016cb5adab998196e36796a54d; _aat=0%7C98AfOV4q0y2ajs7d45JqJKSeC9WqHw7yzoCMOp1f8q6nFv5mKnGNLLJ8lI2nvoGk; abb_fa2=%7B%22user_id%22%3A%2217%7C1%7CzSVpC3gromMi3i2QnC%2B0GmZXwMTnvPbNbcLMZmdUhI6GJeuZ7%2BQfBQ%3D%3D%22%7D; alfc=0; alfces=0; jlp3=true; rclu=%7B%2213381212%22%3D%3E%22L%2FofHdWm9gxCkvVtmbNH2ccMErFwrko9%2B9qOlQPEIGE%3D%22%7D; rclmd=%7B%2213381212%22%3D%3E%22email%22%7D; _user_attributes=%7B%22curr%22%3A%22HUF%22%2C%22guest_exchange%22%3A277.54368999999997%2C%22device_profiling_session_id%22%3A%221532332067--e2b65b68ec1ad3fe7117bf5d%22%2C%22giftcard_profiling_session_id%22%3A%221532332067--1dc354f3cc1068197d8013ed%22%2C%22reservation_profiling_session_id%22%3A%221532332067--df1900f69be8434111a3b193%22%2C%22id%22%3A13381212%2C%22hash_user_id%22%3A%22495fe14f19da2fee902590bbf467ad4f899fa7a6%22%2C%22eid%22%3A%229Kh9ZvlbjYv67gdcgSqUjA%3D%3D%22%2C%22num_msg%22%3A0%2C%22num_notif%22%3A2%2C%22num_alert%22%3A3%2C%22num_h%22%3A4%2C%22num_pending_requests%22%3A0%2C%22num_trip_notif%22%3A0%2C%22name%22%3A%22Akos%22%2C%22num_action%22%3A0%2C%22is_admin%22%3Afalse%2C%22can_access_photography%22%3Afalse%2C%22referrals_info%22%3A%7B%22terms_and_conditions_link%22%3A%22%2Fhelp%2Farticle%2F2269%22%2C%22referrer_guest%22%3A%22Ft5+200%22%7D%7D; flags=806494496; roles=0; _airbed_session_id=cabd642dee291fd35abf9d145e8f7aa4; hli=1; har=1; cbkp=3; _gat=1; _uetsid=_uet881953dd; jitney_client_session_updated_at=1532332540' --data-binary '{"friction":"phone_verification_via_text","friction_data":{"optionSelection":{"phone_number_id":7531677},"response":{"code":"5683"}},"enable_throw_errors":true}' --compressed


302 location: https://www.airbnb.hu/dashboard

I just discovered a few days ago that the mobile client started using another api call to get tokens:

curl -H 'Host: api.airbnb.com'  -H 'content-type: application/json; charset=UTF-8'  -H 'x-airbnb-device-id: <snip>' --data-binary '{"authenticationParams":{"email":{"email":"<snip>","password":"<snip>"}}}' --compressed 'https://api.airbnb.com/v2/authentications?client_id=3092nxybyb0otqw18e8nh5nty&locale=en-GB&currency=GBP'

Device id seems to be able to be any 32 character long alphanumberic string . I was changing it every time i logged in with a new user.

So, a 420 error is when Airbnb’s verification system, named airlock, blocks you from getting a token for an account. You may have worked this out already. It doesn’t happen all the time, but when it does, it’s proven a difficult problem to get around.

Unfortunately, I have not been able to discover the correct endpoints and proceedure for the verification check. I’ve tried a lot of different things but I haven’t been able to crack the problem yet.

I will leave this issue open and assign a task to try and fix it, as I’m aware this is a large issue for the project.

I’m sorry I couldn’t offer more help at this stage.

FYI: The token doesn’t need to be renewed. I’ve been using the same token for almost 2 years now. I do make a lot of requests with it though.

@Technohacker

Your method helps me a lot and I wrote 2 functions for the airbnbapijs Method 1: Request the verification email from airbnb: async emailVerifyRequest({airlock_id, user_id}){ const options = this.buildOptions({ method: 'PUT', uri: 'https://api.airbnb.com/v2/airlocks/'+airlock_id+'?', _format: 'v1', body:{ action_name: "account_login", attempt: true, friction: 'email_code_verification', id: airlock_id, user_id: user_id }, timeout: 10000 }); try { const response = await (0, _requestPromise2.default)(options); return response; } catch (e) { console.error("Airbnbapi: Could not request an verification code for " + user_id); console.error(e); } } Method 2: Send the verification code back to Airbnb: async emailVerifyCodeSend({airlock_id, user_id, verify_code}){ const options = this.buildOptions({ method: 'PUT', uri: 'https://api.airbnb.com/v2/airlocks/'+airlock_id+'?', _format: 'v1', body:{ action_name: "account_login", friction: 'email_code_verification', friction_data: { response: { code: verify_code } }, id: airlock_id, user_id: user_id }, timeout: 10000 }); try { const response = await (0, _requestPromise2.default)(options); return response; } catch (e) { console.error("Airbnbapi: Your verification Code is Wrong, Please check it and do it again !"); console.error(e); } } These are based on your step 2 and step 3, and it works. Thank you very much. If anyone else has this second type of verification problem, I hope my code could help.

I can confirm that @Technohacker 's method worked. I performed his proposed solution using curls. the client_id is the default api_key that we are using to authenticate (namely this). You must always have the header x-airbnb-device-id present at all requests. This is indeed a random hex 16-long string. Regarding the captcha, I just solved it using my browser, merely following the https://www.airbnb.com/airlock?al_id=AIRLOCK_ID link.

does the api return 420 for wrong creds as well, if so how to differentiate between airlock and wrong passwords?

No, that’s 403.

I’m not too well versed but I’m very certain all apps on Android would pass through the Android java libs before interacting with the native interfaces (or they would (maybe?) sacrifice compatibility for using the native layer directly). So, if one compiled a version of AOSP or something else with a patch to the java framework, it’ll allow intercepting the request before it’s sent over the network and get a response after decoding it from the TLS stream.

I looked up online for a simple HTTP request class and I found this small snippet:

        HttpClient client = new DefaultHttpClient();
        HttpGet request = new HttpGet();
        request.setURI(new URI("https://<url>"));
        response = client.execute(request);

So all we need to get the request would be to patch HttpClient’s execute() to print the request (and similarly for response)

This is a lot of work, but it’s a sure sign that it can be done if nothing else works

wow, OK. PUT request for airlock submission? I didn’t try that. I wonder if this will work for the capcha flow. How did you mitm the airlock system? I thought they implemented cert pinning for the recent versions? Thank you very much for sharing this info.

No it’s not that simple, what we really need to do is another non mobile based project that would use pupeteer and call a human whenever Captha is needed to be solved

charlesproxy. You need to root your phone if you are using >= Android N

If you’re on osx you can try postman. Simply select File / Import - paste raw text and paste the entire curl request (starting with curl -H ...).

It seems to work for me. The question is what it returns if the account is airlocked.

@axos88

  const res = await fetch(
    "https://api.airbnb.com/v2/authentications?client_id=3092nxybyb0otqw18e8nh5nty&locale=en-GB¤cy=GBP",
    {
      body: JSON.stringify({
        authenticationParams: { email: { email: "", password: "?" } },
      }),
      headers: {
        "Content-Type": "application/json; charset=UTF-8",
        "X-Airbnb-Device-Id": deviceId() // 16 random alphanumeric chars,
      },
      method: "POST",
    },
  )
  console.log(res)
Response {
  size: 0,
  timeout: 0,
  [Symbol(Body internals)]: {
    body: PassThrough {
      _readableState: [ReadableState],
      readable: true,
      _events: [Object: null prototype],
      _eventsCount: 2,
      _maxListeners: undefined,
      _writableState: [WritableState],
      writable: false,
      allowHalfOpen: true,
      _transformState: [Object],
      [Symbol(kCapture)]: false
    },
    disturbed: false,
    error: null
  },
  [Symbol(Response internals)]: {
    url: 'https://api.airbnb.com/v2/authentications?client_id=3092nxybyb0otqw18e8nh5nty&locale=en-GB¤cy=GBP',
    status: 400,
    statusText: 'Bad Request',
    headers: Headers { [Symbol(map)]: [Object: null prototype] },
    counter: 0
  }
}

not able to convert the response to json .then(res => res.json()) yield the following error…

(node:7490) UnhandledPromiseRejectionWarning: FetchError: invalid json response body at https://api.airbnb.com/v2/authentications?client_id=3092nxybyb0otqw18e8nh5nty&locale=en-GB¤cy=GBP reason: Unexpected end of JSON input
    at /path/node_modules/node-fetch/lib/index.js:272:32
    at processTicksAndRejections (internal/process/task_queues.js:97:5)
    at async main (/path/file.js:40:15)
(node:7490) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). To terminate the node process on unhandled promise rejection, use the CLI flag `--unhandled-rejections=strict` (see https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode). (rejection id: 2)
(node:7490) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.

it seems to work with curl, do u have a nodejs snippet similar to curl

edit:

actually tried curl again and results in this error

{
  error_code: 500,
  error_type: "unknown_error",
  error_message:
    "Unfortunately, a server error prevented your request from being completed. Airbnb may be undergoing maintenance or your connection may have timed out. Please try again.",
}
 

@beld2107 I think you need to do unlock Airlock via the browser by visiting https://www.airbnb.com/airlock?al_id=<AIRLOCK_ID> and completing the captcha and then /authorize again… that should give you an access token. You should cache the token and renew it monthly.

@a-eid have you tried this: Use Postman POST: https://api.airbnb.com/v1/authorize

Headers: Content-Type: application/x-www-form-urlencoded Accept: application/json Accept-Encoding: br, gzip, deflate User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36 X-Airbnb-Api-Key: d306zoyjsyarp7ifhu67rjxn52tv0t20 (this is a public/general client_id) X-Airbnb-Currency: USD X-Airbnb-Device-Id: 111111111111111 X-Airbnb-Locale: en

Body: x-www-form-urlencoded: username: [your-airbnb-email@email.com] password: [password] client_id: d306zoyjsyarp7ifhu67rjxn52tv0t20 grant_type: password

If you /authorize multiple times, you will get Airlocked. Get the Airlock ID from the 420 Error response body and paste it in url above to confirm you’re a human.

From my small test here, they don’t use cert pinning. However, Android Nougat did reject any use of user-trusted CA certs in all apps unless the app explicitly trusted them, so I used a pre-Nougat device for it.

Result: Flawless mitmproxy action 😄

EDIT: Now that I’ve posted this, Airbnb might start implementing cert pinning 🙃

Hi, Hugo*.

I suggest trying some more IP locations until you find one that works. Currently we don’t have a working solution for a recaptcha airlock. 😦 I have some free time coming up I hope to use to work on this issue. I’ll let you know if i find anything by posting in this thread.

I have been sending people to https://www.airbnb.com/airlock?al_id=AIRLOCK_ID – that seems to work most of the time and once they go there in the browser and authorize, the token will work after that. But sometimes it just continues to get a 420.

I noticed this: https://github.com/dennisvdvliet/airbnb_api mentions “registering your application with Airbnb” and then using OAuth. Obviously that would be by far more ideal than storing raw usernames/passwords, but I don’t see any way to actually do that registration step.

Any other progress?