Giter Site home page Giter Site logo

Comments (15)

TimotheeGerber avatar TimotheeGerber commented on June 9, 2024

Hi @hillstub!

Thank you for testing the project on your Denon receiver. Too bad it does not work!

I had a look to the Spotify ZeroConf documentation and indeed, the path is not hard coded and can change. I added an optional parameter to take it into account. By the way, librespot uses / as path, but does not check it so everything works. I still set / as the default path.


According to the documentation, ERROR-BAD-REQUEST means "Web server problem or critically malformed request". That's not good...

I saw in the documentation that you can provide an optional version parameter to the requests. If the Spotify SDK in Denon receiver is backward compatible, it could help to give the version. librespot indicates version 2.7.1. The documentation speaks about 2.9.0. Maybe you can modify src/net.rs and test both version number:

// [...]
    let response = minreq::get(base_url)
        .with_param("action", "getInfo")
        .with_param("version", "2.7.1")  // add this line and test both 2.7.1 and 2.9.0
        .send()?;
// [...]
    let response = minreq::post(base_url)
        .with_header("Content-Type", "application/x-www-form-urlencoded")
        .with_param("action", "addUser")
        .with_param("version", "2.7.1")  // add this line and test both 2.7.1 and 2.9.0
        .with_param("userName", username)
        .with_param("blob", encrypted_blob)
        .with_param("clientKey", my_public_key)
        .send()?;
// [...]

If this does not work, I have another idea. But I need to change code first.


By the way, can you send the result of the following command:

curl -X GET http://IP:PORT/spotify?action=getInfo

It could help to understand how the Denon receiver is working. You can redact deviceID or any other personal information.

from spotify-connect.

hillstub avatar hillstub commented on June 9, 2024

Hi Timothee,
Thanks for your reply! I retrieved the device info which shows it's using version 2.7.1:

{
    "status": 101, 
    "statusString": "OK", 
    "spotifyError": 0, 
    "version": "2.7.1", 
    "deviceID": "REDACTED", 
    "remoteName": "Denon AVR-X1600H", 
    "activeUser": "REDACTED", 
    "accountReq": "DONTCARE", 
    "deviceType": "AVR", 
    "publicKey": "REDACTED", 
    "brandDisplayName": "Denon", 
    "modelDisplayName": "Denon AVR-X1600H", 
    "libraryVersion": "3.55.23-g85f0da1e", 
    "resolverVersion": "0", 
    "groupStatus": "NONE", 
    "tokenType": "accesstoken", 
    "clientID": "REDACTED", 
    "productID": 24, 
    "scope": "streaming", 
    "availability": "", 
    "voiceSupport": "NO"
}

When I added this version number to the different request, I'm still getting the same error.

I forgot to tell yesterday that I do get emails from Spotify about a login attempt - so at least something is happening at the Spotify backend.

from spotify-connect.

hillstub avatar hillstub commented on June 9, 2024

From the documentation you mentioned it seemed that tokenType is also a mandatory field. In the add_user function, I added the following:

        .with_param("tokenType", "default")

It now shows the following error:

Found `Denon AVR-X1600H`. Trying to connect...
thread 'main' panicked at 'Authentication on the remote device failed: "ERROR-LOGIN-FAILED"', src/main.rs:65:6
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

(and I don't seem to get an email from Spotify about a login attempt)

from spotify-connect.

hillstub avatar hillstub commented on June 9, 2024

I set up MITMProxy to intercept the traffic between the official Spotify app and the receiver. This is the addUser request in curl format:

curl --compressed -H 'Connection: keep-alive' -H 'Content-Type: application/x-www-form-urlencoded' -H 'Keep-Alive: 0' -H 'User-Agent: Spotify/REDACTED' -X POST http://{IP}/spotify -d 
'action=addUser
&userName=REDACTED
&blob=REDACTED
&clientKey=
&tokenType=accesstoken
&loginId=REDACTED
&deviceName=REDACTED
&deviceId=REDACTED
&version=2.7.1'

from spotify-connect.

TimotheeGerber avatar TimotheeGerber commented on June 9, 2024

Thanks for the getInfo and the addUser interception!


I forgot to tell yesterday that I do get emails from Spotify about a login attempt - so at least something is happening at the Spotify backend.

(and I don't seem to get an email from Spotify about a login attempt)

The tool I created log you on Spotify to get reusable credentials. So, even if the login failed on your receiver, you should have made a successful connection to Spotify in the tool. If you don't see successful connection now, it is certainly because the reusable credentials are cached. If the cache is present, the connection to Spotify is not made. You can try to delete the cache ($HOME/.cache/spotify-connect/credentials.json) to see what exactly triggers an email from Spotify.


It seems that your receiver accept token as a form of authentication. I didn't have enough time to implement it, but I pushed some changes to prepare for token authentication.

And there is something new you can try: direct username/password authentication, instead of using them to get the reusable credentials. To try it, add the following option on the command line:

spotify-connect --auth-type password IP PORT [PATH]

This new authentication flow is working great with librespot. I hope it would be the case on your hardware too!

from spotify-connect.

hillstub avatar hillstub commented on June 9, 2024

Hi Timothee,
Thanks for starting to implement the authentication using a token!

I tested the direct username/password authentication - unfortunately that didn't work..

from spotify-connect.

TimotheeGerber avatar TimotheeGerber commented on June 9, 2024

I just pushed a new version. Token authentication is implemented.

spotify-connect --auth-type token IP PORT [PATH]

It works great with librespot. But librespot is only relying on the content of the blob. And for now, the different authentication methods only change the way the blob is created. Official hardware may also use other query parameters. Can you try both the code as is, and with a .with_param("tokenType", "accesstoken") in the net::add_user()?


If it is still not working, I will have to simulate a Spotify Connect endpoint as close as possible to your receiver and log all the messages received from the official Spotify client.

MITM would not be enough, because at least one private key is necessary to decrypt the blob. Getting the private key from the official client or from your receiver is beyond my competences.

from spotify-connect.

TimotheeGerber avatar TimotheeGerber commented on June 9, 2024

I have simulated a Spotify Connect endpoint and used the official Spotify client to communicate with it. This is what I learnt from this experiment.

The presence and value of tokenType in the getInfo request change the way the official client try to authenticate:

  • If there is no tokenType or if its value is default, the client construct a blob with what I call reusable credentials, encrypt it and send it with the clientKey (necessary to decrypt the blob on the other side).
  • If tokenType is accesstoken, the client does not send a clientKey. So, the blob is not encrypted. I suspect that the blob field is populated with the token directly (at least, they look similar). But "tokenType": "accesstoken" alone is not enough. A clientId field should be present. The value should be a valid clientId (one can ask for a clientId in the Spotify dashboard). Giving the clientId from the official Spotify client does not work. Perhaps there is a clientId verification on Spotify servers (or a blacklist in the official client) to avoid usurpation. I think the token should be generated for this clientId, with the specified scope.

What I don't understand yet is the loginId field in the addUser request. It is different each time, like the token. Are they linked? Or is it simply a unique identifier for this login request?

I will try to make a new version which take everything I noticed into account. But I wanted to write down what I learned first.

from spotify-connect.

TimotheeGerber avatar TimotheeGerber commented on June 9, 2024

I just pushed a version which take both default and accesstoken into account. I have a good feeling about this! @hillstub, can you test both authentication methods and tell me if its working?

spotify-connect --auth-type default-token IP PORT [PATH]
spotify-connect --auth-type access-token IP PORT [PATH]

from spotify-connect.

hillstub avatar hillstub commented on June 9, 2024

Awesome that you did all the research in getting this some steps closer to solving this.

Did you commit the last version? I got the following error when trying access-token:

error: "access-token" isn't a valid value for '--auth-type <AUTH_TYPE>'
	[possible values: reusable, password, token]

from spotify-connect.

TimotheeGerber avatar TimotheeGerber commented on June 9, 2024

Yes, the last version with access-token as been pushed in the middle of the night. Are you up-to-date? It seems your version is on commit 36d5421, while it should be on c236043 for the latest version.

You should execute this to get the latest version:

git pull
cargo install --path .

Maybe your git pull failed. It can happen if you had uncommitted changes for example.

from spotify-connect.

hillstub avatar hillstub commented on June 9, 2024

Whoops - forgot to run the last command.

It worked! Woot woot! Thank you for solving this!!

The only thing is that once I authenticated and try running the spotify-connect command again (with and without removing credentials.json) it fails with the following error:

thread 'main' panicked at 'Authentication on the remote device failed: "{\"spotifyError\":12,\"status\":202,\"statusString\":\"ERROR-LOGIN-FAILED\"}"', src/main.rs:117:6

from spotify-connect.

TimotheeGerber avatar TimotheeGerber commented on June 9, 2024

It worked! Woot woot! Thank you for solving this!!

At last! This is such a good news!

🎉 🎉 🎉 🎉 🎉 🎉 🎉 🎉 🎉

The only thing is that once I authenticated and try running the spotify-connect command again (with and without removing credentials.json) it fails with the following error:

Maybe the login attempt fails if you are already logged in. Try to log out, before running the command once again.

According to the documentation, you should be able to log out by sending an empty POST request:

curl -X POST http://<IP>:<PORT>/<PATH>

If it does not work, you can try to turn off and on your receiver to log out.

from spotify-connect.

hillstub avatar hillstub commented on June 9, 2024

Thanks, Timothee!

I had to use the following command to log out the user:

curl -X POST -d action=resetUsers http://<IP>:<PORT>/<PATH>

After that it was possible to login again using the spotify-connect command.

In the (near) future, I'll follow your approach to write a C++ library so that I can use this on an ESP32. Will link to your repository as soon as I have something working.

from spotify-connect.

TimotheeGerber avatar TimotheeGerber commented on June 9, 2024

I read the documentation too fast... Well done finding the missing action in the request.

I am glad to see that it works correctly. Next step, I will change the default behaviour to automatically choose the best authentication method according to the context. Other methods will still be supported, behind the --auth-type flag.

Good luck writing your C++ library! ESP32 are cool little beast.

I am closing this issue now that it is working on your receiver. Many thanks for your help and for testing all the versions!

from spotify-connect.

Related Issues (2)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.