PromyLOPh / pandora-apidoc

pandora.com API documentation
https://6xq.net/pandora-apidoc/
Do What The F*ck You Want To Public License
48 stars 19 forks source link

JSON API reauthentication #47

Open hacker1024 opened 3 years ago

hacker1024 commented 3 years ago

The official Android app can reauthenticate without storing the username and password, but I'm unable to do so myself. It's be great if we could work this out, and add it to the documentation. Below are my findings after exploring the login system.

Login types

There are four loginType values that can be used with auth.userLogin: user, deviceId, accessToken, and firstIntroToken.

user logs in with a username and password. An example request, with all the common user response parameters removed, looks like this:

{
  "username": "<username>",
  "password": "<password>",
  "appSignature": "0735356f37ee31a95dcbe016fcc8c5b2a6a4ac93",
  "loginType": "user"
}

This login type is used by the Pandora Android app when a user first logs in, but not for subsequent logins. To reauthenticate expired sessions, and log in from storage, the app uses the deviceId type, which looks like this:

{
  "loginType": "deviceId"
}

As you can see, no loginType-specific user-specific information is required with this login type. Instead, the backend uses the deviceId value sent in every type of login request to authenticate the user.

The deviceId

The deviceId used is generated with [UUID.randomUUID()](https://developer.android.com/reference/java/util/UUID?hl=en#randomUUID()) and stored upon first access. The same value is then used for all relevant API requests.

The initial Pandora Android login flow

When the Android app launches for the first time, it does the following:

  1. Authenticate the partner with auth.partnerLogin
  2. Retrieve some UI labels with firstintroduction.v2.startFirstIntroduction
    • This method won't be recognised unless there's a slash before the query string, i.e. .../services/json/?method=....
    • The deviceId is sent in this request, but I don't think this matters due to the next response.
  3. Attempt to log in with the deviceId login type
    • This will always fail with error 1009, which is mapped to a string titled deviceNotFound.
  4. Prompt for the username and password, and then authenticate with the user login type

Reauthentication

When an authentication token expires, subsequent API requests will fail with error 1001 (invalidAuthToken). When this happens, the official Android app will reauthenticate with the deviceId login type.

Additionally, the deviceId login type is used on startup when the user is signed in. The username and password aren't stored.

The issue

When I try to reauthenticate with the deviceId login type, it always fails with an error 1009 - even after I log in with user and provide the same deviceId.

It looks like the Android app is somehow registering the device with Pandora, so reauthentication can be done. This must be happening somewhere in between step 3 of the login flow and reauthentication, but I can't work out how it works.

hacker1024 commented 3 years ago

I worked it out - the device is associated with the user like so:

Method user.associateDevice

Request (encrypted)

{
  "deviceId": "<deviceId>"
}

Response

{
  "stat": "ok"
}
hacker1024 commented 3 years ago

Leaving this issue open as this can be added to the documentation.