Files
Bose-SoundTouch/tests/integration/http-client/post_oauth_token_amazon.http
T
Tobias GesellchenandClaude Sonnet 4.6 f1c2b7a53f feat: implement HandleBoseAmazonToken and wire amazonService into Server
- Add GetAccountByRefreshToken to amazon.Service — the speaker sends
  the bare Atzr| refresh token (extracted from AmazonSecret JSON), not
  a surrogate, so lookup must match against Account.RefreshToken
- Add amazonService field, SetAmazonService and IsAmazonConfigured to
  Server (step 5 essentials required by the handler)
- Replace HandleBoseAmazonToken 501 stub with full implementation:
  lookup by refresh token → RefreshAccessToken; fallback to
  GetFreshToken; fallback to HandleBoseProxy if no service configured;
  scope intentionally omitted from response
- Add handler tests covering the by-refresh-token path (mock LWA
  server), the default-account path, and the no-service fallback
- Unlock assertions in post_oauth_token_amazon.http integration test

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-29 20:30:06 +02:00

40 lines
1.8 KiB
HTTP

### POST /oauth/device/{{deviceId}}/music/musicprovider/20/token/cs1
# Mirrors the exact request the SoundTouch firmware makes when it needs a fresh Amazon access token.
# The speaker extracts the bare refresh_token from its stored AmazonSecret JSON and sends it here.
# Log reference: STSOAuth2::ConstructPostDataString — grant_type=refresh_token, code and redirect_uri are always empty.
POST {{host}}/oauth/device/{{deviceId}}/music/musicprovider/20/token/cs1
Authorization: Bearer {{token}}
Content-Type: application/json
{
"code": "",
"grant_type": "refresh_token",
"redirect_uri": "",
"refresh_token": "{{amazonRefreshToken}}"
}
> {%
client.test("Request executed successfully", function() {
client.assert(response.status === 200, "Response status is not 200");
});
client.test("Response content type is JSON", function() {
var type = response.contentType.mimeType;
client.assert(type === "application/json", "Expected 'application/json' but received '" + type + "'");
});
client.test("Response body has expected structure", function() {
client.assert(response.body.hasOwnProperty("access_token"), "Response body missing 'access_token'");
client.assert(response.body.access_token.length > 0, "access_token is empty");
client.assert(response.body.hasOwnProperty("expires_in"), "Response body missing 'expires_in'");
client.assert(typeof response.body.expires_in === "number", "expires_in is not a number");
client.assert(response.body.hasOwnProperty("token_type"), "Response body missing 'token_type'");
client.assert(response.body.token_type === "Bearer", "token_type is not 'Bearer'");
// Amazon omits 'scope' — do not assert its presence.
client.assert(!response.body.hasOwnProperty("scope"), "Response must NOT include 'scope' for Amazon");
});
%}