diff --git a/src/server.ts b/src/server.ts index 952da68..3d80cfb 100644 --- a/src/server.ts +++ b/src/server.ts @@ -39,7 +39,6 @@ import { axiosImageFetcher, ImageFetcher } from "./subsonic"; import { JWTSmapiLoginTokens, SmapiAuthTokens, - smapiTokenFromString, } from "./smapi_auth"; export const BONOB_ACCESS_TOKEN_HEADER = "bat"; @@ -378,28 +377,23 @@ function server( logger.info( `${trace} bnb<- ${req.method} ${req.path}?${JSON.stringify( req.query - )}, headers=${JSON.stringify({ ...req.headers, authorization: "*****" })}` + )}, headers=${JSON.stringify({ ...req.headers, "bnbt": "*****", "bnbk": "*****" })}` ); - const authHeader = E.fromNullable("Missing header"); - const bearerToken = E.fromNullable("No Bearer token"); const serviceToken = pipe( - authHeader(req.headers["authorization"] as string), - E.chain((authorization) => + E.fromNullable("Missing bnbt header")(req.headers["bnbt"] as string), + E.chain(token => pipe( + E.fromNullable("Missing bnbk header")(req.headers["bnbk"] as string), + E.map(key => ({ token, key })) + )), + E.chain((auth) => pipe( - authorization.match(/Bearer (?.*)/), - bearerToken, - E.map((match) => match[1]!) - ) - ), - E.chain((bearerToken) => - pipe( - smapiAuthTokens.verify(smapiTokenFromString(bearerToken)), - E.mapLeft((_) => "Bearer token failed to verify") + smapiAuthTokens.verify(auth), + E.mapLeft((_) => "Auth token failed to verify") ) ), E.getOrElseW(() => undefined) - ); + ) if (!serviceToken) { return res.status(401).send(); diff --git a/src/smapi.ts b/src/smapi.ts index 97f6630..9d5686b 100644 --- a/src/smapi.ts +++ b/src/smapi.ts @@ -32,7 +32,6 @@ import { isExpiredTokenError, MissingLoginTokenError, SmapiAuthTokens, - smapiTokenAsString, SMAPI_FAULT_LOGIN_UNAUTHORIZED, ToSmapiFault, } from "./smapi_auth"; @@ -532,10 +531,14 @@ function bindSmapiSoapServiceToExpress( httpHeaders: [ { httpHeader: { - header: "Authorization", - value: `Bearer ${smapiTokenAsString( - credentials.loginToken - )}`, + header: "bnbt", + value: credentials.loginToken.token, + }, + }, + { + httpHeader: { + header: "bnbk", + value: credentials.loginToken.key, }, }, ], diff --git a/tests/server.test.ts b/tests/server.test.ts index 2a49480..5cead43 100644 --- a/tests/server.test.ts +++ b/tests/server.test.ts @@ -26,7 +26,7 @@ import i8n, { randomLang } from "../src/i8n"; import { SONOS_RECOMMENDED_IMAGE_SIZES } from "../src/smapi"; import { Clock, SystemClock } from "../src/clock"; import { formatForURL } from "../src/burn"; -import { ExpiredTokenError, SmapiAuthTokens, SmapiToken, smapiTokenAsString } from "../src/smapi_auth"; +import { ExpiredTokenError, SmapiAuthTokens, SmapiToken } from "../src/smapi_auth"; describe("rangeFilterFor", () => { describe("invalid range header string", () => { @@ -754,7 +754,7 @@ describe("server", () => { }); }); - describe("when the Bearer token has expired", () => { + describe("when the authorization has expired", () => { it("should return a 401", async () => { smapiAuthTokens.verify.mockReturnValue(E.left(new ExpiredTokenError(serviceToken))) @@ -764,13 +764,15 @@ describe("server", () => { pathname: `/stream/track/${trackId}` }) .path(), - ).set('Authorization', `Bearer ${smapiTokenAsString(smapiAuthToken)}`); + ) + .set('bnbt', smapiAuthToken.token) + .set('bnbk', smapiAuthToken.key); expect(res.status).toEqual(401); }); }); - describe("when the Bearer token is valid", () => { + describe("when the authorization token & key are valid", () => { beforeEach(() => { smapiAuthTokens.verify.mockReturnValue(E.right(serviceToken)); }); @@ -795,7 +797,9 @@ describe("server", () => { bonobUrl .append({ pathname: `/stream/track/${trackId}`}) .path() - ).set('Authorization', `Bearer ${smapiTokenAsString(smapiAuthToken)}`); + ) + .set('bnbt', smapiAuthToken.token) + .set('bnbk', smapiAuthToken.key); expect(res.status).toEqual(trackStream.status); expect(res.headers["content-type"]).toEqual( @@ -821,8 +825,10 @@ describe("server", () => { .head(bonobUrl .append({ pathname: `/stream/track/${trackId}` }) .path() - ) - .set('Authorization', `Bearer ${smapiTokenAsString(smapiAuthToken)}`); + ) + .set('bnbt', smapiAuthToken.token) + .set('bnbk', smapiAuthToken.key); + expect(res.status).toEqual(404); expect(res.body).toEqual({}); @@ -851,13 +857,15 @@ describe("server", () => { bonobUrl .append({ pathname: `/stream/track/${trackId}` }) .path() - ).set('Authorization', `Bearer ${smapiTokenAsString(smapiAuthToken)}`); + ) + .set('bnbt', smapiAuthToken.token) + .set('bnbk', smapiAuthToken.key); expect(res.status).toEqual(401); }); }); - describe("when the Bearer token is valid", () => { + describe("when the authorization token & key is valid", () => { beforeEach(() => { smapiAuthTokens.verify.mockReturnValue(E.right(serviceToken)); }); @@ -878,7 +886,9 @@ describe("server", () => { bonobUrl .append({ pathname: `/stream/track/${trackId}` }) .path() - ).set('Authorization', `Bearer ${smapiTokenAsString(smapiAuthToken)}`); + ) + .set('bnbt', smapiAuthToken.token) + .set('bnbk', smapiAuthToken.key); expect(res.status).toEqual(404); @@ -910,7 +920,9 @@ describe("server", () => { bonobUrl .append({ pathname: `/stream/track/${trackId}` }) .path() - ).set('Authorization', `Bearer ${smapiTokenAsString(smapiAuthToken)}`); + ) + .set('bnbt', smapiAuthToken.token) + .set('bnbk', smapiAuthToken.key); expect(res.status).toEqual(stream.status); expect(res.headers["content-type"]).toEqual( @@ -950,7 +962,9 @@ describe("server", () => { bonobUrl .append({ pathname: `/stream/track/${trackId}` }) .path() - ).set('Authorization', `Bearer ${smapiTokenAsString(smapiAuthToken)}`); + ) + .set('bnbt', smapiAuthToken.token) + .set('bnbk', smapiAuthToken.key); expect(res.status).toEqual(stream.status); expect(res.headers["content-type"]).toEqual( @@ -988,7 +1002,9 @@ describe("server", () => { bonobUrl .append({ pathname: `/stream/track/${trackId}` }) .path() - ).set('Authorization', `Bearer ${smapiTokenAsString(smapiAuthToken)}`); + ) + .set('bnbt', smapiAuthToken.token) + .set('bnbk', smapiAuthToken.key); expect(res.status).toEqual(stream.status); expect(res.header["content-type"]).toEqual( @@ -1027,7 +1043,9 @@ describe("server", () => { bonobUrl .append({ pathname: `/stream/track/${trackId}` }) .path() - ).set('Authorization', `Bearer ${smapiTokenAsString(smapiAuthToken)}`); + ) + .set('bnbt', smapiAuthToken.token) + .set('bnbk', smapiAuthToken.key); expect(res.status).toEqual(stream.status); expect(res.header["content-type"]).toEqual( @@ -1072,7 +1090,8 @@ describe("server", () => { .append({ pathname: `/stream/track/${trackId}` }) .path() ) - .set('Authorization', `Bearer ${smapiTokenAsString(smapiAuthToken)}`) + .set('bnbt', smapiAuthToken.token) + .set('bnbk', smapiAuthToken.key) .set("Range", requestedRange); expect(res.status).toEqual(stream.status); @@ -1116,7 +1135,8 @@ describe("server", () => { .append({ pathname: `/stream/track/${trackId}` }) .path() ) - .set('Authorization', `Bearer ${smapiTokenAsString(smapiAuthToken)}`) + .set('bnbt', smapiAuthToken.token) + .set('bnbk', smapiAuthToken.key) .set("Range", "4000-5000"); expect(res.status).toEqual(stream.status); diff --git a/tests/smapi.test.ts b/tests/smapi.test.ts index 63da529..eca9b52 100644 --- a/tests/smapi.test.ts +++ b/tests/smapi.test.ts @@ -58,7 +58,7 @@ import { iconForGenre } from "../src/icon"; import { formatForURL } from "../src/burn"; import { range } from "underscore"; import { FixedClock } from "../src/clock"; -import { ExpiredTokenError, InvalidTokenError, SmapiAuthTokens, SmapiToken, smapiTokenAsString, ToSmapiFault } from "../src/smapi_auth"; +import { ExpiredTokenError, InvalidTokenError, SmapiAuthTokens, SmapiToken, ToSmapiFault } from "../src/smapi_auth"; const parseXML = (value: string) => new DOMParserImpl().parseFromString(value); @@ -3046,14 +3046,20 @@ describe("wsdl api", () => { pathname: `/stream/track/${trackId}`, }) .href(), - httpHeaders: { - httpHeader: [ - { - header: "Authorization", - value: `Bearer ${smapiTokenAsString(smapiAuthToken)}`, - }, - ], - }, + httpHeaders: [ + { + httpHeader: [{ + header: "bnbt", + value: smapiAuthToken.token, + }], + }, + { + httpHeader: [{ + header: "bnbk", + value: smapiAuthToken.key, + }], + } + ], }); expect(musicService.login).toHaveBeenCalledWith(serviceToken);