Sonos authentication when requesting a stream - sonos

Apologies, in advance, that this question is probably too vague to be admitted into the cannon of good StackOverflow questions. However it does reflect the current state of my knowledge of this domain.
Sonos provides the ability for a 3rd Party Service to offer Sonos users access to a music service. This I understand, and the documentation provided by Sonos is comprehensive.
However, we have the scenario of Service Provider A who provides users with the following services:
Access to streaming radio (for which A has the rights and can provide the stream)
Access to on demand / catch-up content (for which A has the rights and can provide the stream)
Ability to create playlists of music played within 1 & 2 (for which A does not have the rights, and instead provides the stream via one or more 3rd Party music services that the user has an account with).
This works perfectly well in the context of A's own apps - which include integrations with 3rd party music services who provide the stream for content that comes under 3 (i.e. if you are a user of A, then while you can create playlists, if you want to actually play a track from the playlist then need an account with a service provider who has the rights to that particular track).
However, I am struggling to reason about this in the context of Sonos.
If, as a user of A, I have the following container:
Container1
item1 ('podcast' belonging to Service A)
item2 (song, belonging to Service B)
I am registered with both Service A and Service B, and both Service A & Service B are available separately on Sonos and both use DeviceLink for authentication (lets for the purposes of this example assume that Service B is Spotify).
If the user requests the container, adds item1 to their queue and then presses play, the Player will request the streaming uri from Service A, and Service A will return it in the format:
http://service-a.uri/some-file
The player will then perform a GET request on this uri, and the item will start playing.
However, if the user adds item2 to their queue and then presses play, Service A will return a streaming uri belonging to Service B like this:
http://service-b.uri/some-file
In this case, how is authentication handled?
The user is authenticated as follows:
Within Service A:
To Service A
To Service B, via Service B's API.
Within Sonos:
To Service A
To Service B
However, Sonos is going to send Service A's credentials to Service B, and thus the stream will fail (because why would it know that Service A has a pre-existing relationship with Service B whereby Service A users consume content from Service B, if said user already has an account with Service B).
So there is no way to achieve what I am looking to do in the context of Sonos, or am I fundamentally misunderstanding something here?

That is a really great question.
For streaming Sonos requests the uri with the getMediaUri request. This uri is then used to stream the content. If uri returned as a result of this request is streamable then there should not be an issue. It is possible to include headers that should be sent with the uri request and that can also be used if some additional authentication is required. (For example passing an authentication token to Service B). However the requirement here is the Service A would have everything it needs to properly pass this information to Sonos so that Sonos can include it in its request to Service B.
For security reasons Sonos does not currently have a way to pass Service Bs authentication token or information to Service A; nor is there a way for the Sonos player to know that a url it is requesting which was provided by Service A is actually intended for Service B. There is also no way for the player to inform Service A that the user also has Service B installed.
This is something we have thought about and will consider providing a mechanism to support this in the future.

Related

How to authenticate user in microservice architecture with Lumen

I'm new to microservice architecture. I was reading about it and start to be interested in developing website using the architecture. I've used Lumen micro framework.
What I am going to ask you has been browsed on the internet and I couldn't find the way. So, I finally reached out to stackoverflow. Below is the overview of my current implementation.
Up until this point, I am able to request user, patient, treatment, etc.. data from the api gateway and get the response data properly.
When client requests user data like name, department, client requests this route, http://localhost:8000/users/1, (port 8000 is for api gateway and 8001 for user service, let's say) and gateway goes to 8001 and grab the user data.
I've also enabled the authorization between api gateway and individual services in order to prevent separately perform CRUD operatons to the individual services - when request goes from gateway to service, I have put the pregenerated token (which is also predefined in the service) in the header and when it reaches the service, the service validates if the token is equal by comparing its predefined one. So, it's working.
But to be able to request from api gateway to services, I've used client credentials grant type. So, here is my question.
How can I implement the login and register? Does client credentials
grant type enable to do so? If not, what is the appropriate one? What
is the right way to implement the system? Could you please kindly explain in
advance? Thank you so much.
Updated
In conclusion, I want to know how to configure authentication between front-end and api gateway.
Your API architecture looks good - nothing there needs to change. However there are 3 parts to the architecture:
APIs (done)
UIs (to do)
Authorization Server (maybe use a free cloud one?)
As a next step maybe focus on login. My tutorial will help you to understand the interaction and what needs to be coded in UIs. Or if you prefer just view the message workflow.
Registering users can be a more complex topic and depends on the type of system. Happy to answer follow up questions if it helps.

Authentication and Services

I am designing a service oriented application where the communication to the database is distributed across multiple services (Authentication service, some service for auditing and other for accessing the db and doing CRUD operation ... etc).
Say a user login to the app using his id and password, the app then talk to the auth service and find out if the information are correct, once done the user want to insert some data, now the app use another service to fulfil the user request. How can the other service now that the user is an authorized user to use the service.
Your use-case seems very similar to what SAML addresses.
Also look at OAuth.
If these standard mechanisms don't work for you, you can at least develop a mechanism where:
The authentication service returns a token on successful login. The
caller app should then be able to use this token to access the data
service and other services.
The data service should be able to independently validate the token (possibly with the authentication service).
You might want to ensure that the tokens remain valid only for a certain duration or certain number of invocations
What this avoids is the need for every back-end service to allow access to the app without using your login details.
Also see: What is token based authentication?

How do I retrieve the catalog of a music service?

I am building an app that integrates nicely with Sonos speakers.
I would like to provide the user the ability to select the container (playlist/stations/...leafs of the tree here) of the music service provider. Say Amazon or Spotify...
I managed to understand the vast majority of the SOAP calls however, I could not figure out how to query the user's selected service (devicelink) and provide the same list that the Sonos controller show.
How do I do that?
thanks!
This is not currently supported by the Sonos APIs and many music services take steps to ensure that only Sonos controllers can browse their catalog.
Sonos decided that access to music services should no longer be allowed by third-party-apps some time ago.
I figured out how it works and made a complete walkthrough here, https://Sonos.svrooij.io/music-services.html
It makes a second connection to the specific music service, just for browsing the catalog.
get some data from the Sonos device
Request an auth code for the service
Let the user login
If the user responded, request an access token and a refresh token.
Save these tokens in your Sonos system (optional)
Use the tokens to talk to the music service
Save and use the new access token if it is expired (optional)
You can also use my sonos-ts library, which has support for music services that require authentication.

Generic Cross-client Authorization workflow

How / can I get Google cross-client auth to work in a client-type-agnostic way (e.g., using only a web browser and cURL commands)?
I have two OAuth 2.0 clients (let's call them Client A and Client B), both of type "Web Application" and in the same Google API Console project. Each client's credentials is used by a different app (say, Apps A & B, respectively):
App A implements Google Sign-In using Client A; the ID token obtained after a user signs in is sent to a /verify endpoint, which verifies the token and authenticates the user to the app.
App B wants to automate the offline authentication of a specific user to App A by sending an ID token (obtained using Client B's credentials) directly to its /verify endpoint. (Note that App B doesn't have access to Client A's secret.) The general strategy is:
manually get a one-time-use auth code for the user (using proper scopes and access_type=offline),
exchange it for tokens (requires client secret),
securely store the refresh token and use it to generate ID tokens whenever I want, which I'll in turn use to authenticate the user to App A.
If App B uses tokens obtained in the "normal" way using Client B credentials, then App A will see my ID token as invalid, since the standard check involves verifying that the decoded token's payload has an aud value equal to Client A's ID, which it won't.
Cross-client identity/auth to the rescue, right?
From https://developers.google.com/identity/protocols/CrossClientAuth:
Google considers that when a user has granted access to a particular scope to any client ID in a project, the grant indicates the user's trust in the whole application for that scope.
The effect is that the user should not be prompted to approve access to any resource more than once for the same logical application, whenever the components of the application can be reliably authenticated by Google's authorization infrastructure, which today includes web clients, JavaScript clients, and Android apps.
(Emphasis mine.)
The Problem
The problem is that, while the docs explicitly say that what I want to do is possible, it only shows an example for Android -> Web cross-client auth, which involves calling a specific utility function available only on Android, passing the scope as a "magic string" of the form oauth2:server:client_id:{CLIENT_A_ID}:api_scope:{SCOPE1 SCOPE2 ...}.
I've scoured the internet and have found zero examples of how to do this in a generic way or for other client combinations. I've tried naively sending a similarly-constructed "magic" scope string when requesting an auth code, which is seen as an invalid by Google's servers (invalid_scope 400 error). I've stopped just short of decompiling Google libraries to see how it translates this string into actual server requests (if that's, in fact, what it's doing).
My question: Does anyone know how to get Google cross-client auth to work in a client-type-agnostic way? (Or at least in the specific way I'm asking?)

Am I allowed to use last.fm API in the following scenario?

I'm building an application which uses last.fm API. I want my server to communicate with last.fm and the users of my application would communicate with the server. So the user is indirectly communicating with last.fm. I'm doing this to speed up the whole communication by caching some data on my server.
Is this OK?
As long as you follow their TOS, you are OK.
Yes, this is OK. Many other services do this.
If you think about it, any action any app takes is always indirect. There is no requirement that the application acting on behalf of the user runs on the same computer as the user. Quite often the application runs on a web server.
Some examples of apps which do this include http://tweekly.fm/ and http://hypem.com/ .
In order for the service to act on the user's behalf (for certain methods such as scrobbling), you have to authorise your application as the user, and this is achieved using the web auth flow described at http://www.last.fm/api/webauth .
(This is one of those flows where the user is directed to a page on Last.fm to confirm that they authorise your app. Your app receives a session key in return, which allows your app to act on behalf of the user).