I subscribed some time ago to a climbing magazine through Zinio. And I wanted to see how they implemented the authentication system in their app. (Lately in Spain we had some huge problem with the same topic: Metrovalencia)

Zinio app is based in Node.js. Wasn’t difficult to see under the hood. After so time I figure out the headers request

headers = {
  'authority':'www.zinio.com',
  'method':'POST',
  'path':'/api/login?project=99',
  'scheme':'https',
  "authorization": "",
  "X-ZINIO-User-Id": "",
  "User-Agent": "Mozilla/5.0 ...",
  "accept" : "*/*",
  "accept-encoding" : "gzip, deflate, br",
  "accept-language" : "en-US,en;q=0.9",
  "content-length" : "53",
  "content-type" : "application/json",
  "origin" : "null"
}

I found quite interesting that they have in the code fixed credentials for identify yourself as a valid client. Could those fixed credentials be used in other places?

  "client_id": 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX',
  "client_secret": 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'

Next step was discover how they prepare the authorization field in the headers. They do something like this:

  cred_concat = fixed_credentials['client_id'] + fixed_credentials['client_secret']
  cred_hash = hashlib.sha1(cred_concat.encode('utf8'))
  cred_hash = cred_hash.hexdigest()

  headers['authorization'] = 'desktop ' + cred_hash

The only thing that is missing is the user and pwd.

  body_cred = {
      'username' : 'secret@email.com',
      'password' : 'supersecretpwd'
  }

We are ready to do our successful request

  response = requests.post(url_login, headers=headers, params={"project":"99"}, data=json.dumps(body_cred))
  response_json = response.json()

And we get a nice response, I point out the most significative fields

  {
    'data':
      {
        'user': {
           'email': 'secret@email.com',
           'user_id_string': 'XXXXXXXXXXXXXXXXXXX'
        },
        'token': {
          'token_type': 'bearer',
          'access_token': 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX',
          'expires_in': 7200,
          'EXPIRES_AT': '2019-03-30T10:36:37.699Z'
        },
        'refreshToken': 'secret-string-of-243-characters'},
        'status': True
  }

Now, we know:

  • Zinio uses a bearer auth system
  • We know our user_id after a successful login

After checking how is the refreshToken with JWT we see the below structure

###header

{
  "alg": "HS256",
  "typ": "JWT"
}

###payload

{
  "payload": {
    "userId": "XXXXXXXXXXXXXXXXXXX",
    "token": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
  },
  "iat": 1213935449,
  "exp": 1215231449
}

From now on, we could create a nice code which download all our magazines :)