Fix bug where streaming didnt work due to correct use of Bearer token (#84)

This commit is contained in:
Simon J
2021-12-03 13:51:51 +11:00
committed by GitHub
parent 075538f029
commit 7c0db619c9
4 changed files with 49 additions and 49 deletions

View File

@@ -36,7 +36,7 @@ import morgan from "morgan";
import { takeWithRepeats } from "./utils"; import { takeWithRepeats } from "./utils";
import { parse } from "./burn"; import { parse } from "./burn";
import { axiosImageFetcher, ImageFetcher } from "./subsonic"; import { axiosImageFetcher, ImageFetcher } from "./subsonic";
import { JWTSmapiLoginTokens, SmapiAuthTokens, SmapiToken } from "./smapi_auth"; import { JWTSmapiLoginTokens, SmapiAuthTokens, smapiTokenFromString } from "./smapi_auth";
export const BONOB_ACCESS_TOKEN_HEADER = "bat"; export const BONOB_ACCESS_TOKEN_HEADER = "bat";
@@ -371,7 +371,7 @@ function server(
logger.info( logger.info(
`${trace} bnb<- ${req.method} ${req.path}?${JSON.stringify( `${trace} bnb<- ${req.method} ${req.path}?${JSON.stringify(
req.query req.query
)}, headers=${JSON.stringify({ ...req.headers, "authorization": "***" })}` )}, headers=${JSON.stringify({ ...req.headers, "authorization": "*****" })}`
); );
const authHeader = E.fromNullable("Missing header"); const authHeader = E.fromNullable("Missing header");
@@ -384,7 +384,7 @@ function server(
E.map(match => match[1]!) E.map(match => match[1]!)
)), )),
E.chain(bearerToken => pipe( E.chain(bearerToken => pipe(
smapiAuthTokens.verify(bearerToken as unknown as SmapiToken), smapiAuthTokens.verify(smapiTokenFromString(bearerToken)),
E.mapLeft(_ => "Bearer token failed to verify") E.mapLeft(_ => "Bearer token failed to verify")
)), )),
E.getOrElseW(() => undefined) E.getOrElseW(() => undefined)

View File

@@ -57,17 +57,17 @@ export class InvalidTokenError extends Error implements ToSmapiFault {
export class ExpiredTokenError extends Error implements ToSmapiFault { export class ExpiredTokenError extends Error implements ToSmapiFault {
_tag = "ExpiredTokenError"; _tag = "ExpiredTokenError";
authToken: string; serviceToken: string;
expiredAt: number; expiredAt: number;
constructor(authToken: string, expiredAt: number) { constructor(serviceToken: string, expiredAt: number) {
super("SMAPI token has expired"); super("SMAPI token has expired");
this.authToken = authToken; this.serviceToken = serviceToken;
this.expiredAt = expiredAt; this.expiredAt = expiredAt;
} }
toSmapiFault = (smapiAuthTokens: SmapiAuthTokens) => { toSmapiFault = (smapiAuthTokens: SmapiAuthTokens) => {
const newToken = smapiAuthTokens.issue(this.authToken) const newToken = smapiAuthTokens.issue(this.serviceToken)
return { return {
Fault: { Fault: {
faultcode: "Client.TokenRefreshRequired", faultcode: "Client.TokenRefreshRequired",
@@ -142,8 +142,8 @@ export class JWTSmapiLoginTokens implements SmapiAuthTokens {
return right((jwt.verify(smapiToken.token, this.secret + this.version + smapiToken.key) as any).serviceToken); return right((jwt.verify(smapiToken.token, this.secret + this.version + smapiToken.key) as any).serviceToken);
} catch (e) { } catch (e) {
if(isTokenExpiredError(e)) { if(isTokenExpiredError(e)) {
const x = ((jwt.verify(smapiToken.token, this.secret + this.version + smapiToken.key, { ignoreExpiration: true })) as any).serviceToken; const serviceToken = ((jwt.verify(smapiToken.token, this.secret + this.version + smapiToken.key, { ignoreExpiration: true })) as any).serviceToken;
return left(new ExpiredTokenError(x, e.expiredAt)) return left(new ExpiredTokenError(serviceToken, e.expiredAt))
} else if(isError(e)) } else if(isError(e))
return left(new InvalidTokenError(e.message)); return left(new InvalidTokenError(e.message));
else else

View File

@@ -26,7 +26,7 @@ import i8n, { randomLang } from "../src/i8n";
import { SONOS_RECOMMENDED_IMAGE_SIZES } from "../src/smapi"; import { SONOS_RECOMMENDED_IMAGE_SIZES } from "../src/smapi";
import { Clock, SystemClock } from "../src/clock"; import { Clock, SystemClock } from "../src/clock";
import { formatForURL } from "../src/burn"; import { formatForURL } from "../src/burn";
import { ExpiredTokenError, SmapiAuthTokens } from "../src/smapi_auth"; import { ExpiredTokenError, SmapiAuthTokens, SmapiToken, smapiTokenAsString } from "../src/smapi_auth";
describe("rangeFilterFor", () => { describe("rangeFilterFor", () => {
describe("invalid range header string", () => { describe("invalid range header string", () => {
@@ -750,9 +750,9 @@ describe("server", () => {
} }
); );
const serviceToken = uuid(); const serviceToken = `serviceToken-${uuid()}`;
const trackId = uuid(); const trackId = `t-${uuid()}`;
const smapiAuthToken = `smapiAuthToken-${uuid()}`; const smapiAuthToken: SmapiToken = { token: `token-${uuid()}`, key: `key-${uuid()}` };
const streamContent = (content: string) => ({ const streamContent = (content: string) => ({
pipe: (_: Transform) => { pipe: (_: Transform) => {
@@ -777,7 +777,7 @@ describe("server", () => {
describe("when the Bearer token has expired", () => { describe("when the Bearer token has expired", () => {
it("should return a 401", async () => { it("should return a 401", async () => {
smapiAuthTokens.verify.mockReturnValue(E.left(new ExpiredTokenError(smapiAuthToken, 0))) smapiAuthTokens.verify.mockReturnValue(E.left(new ExpiredTokenError(serviceToken, 0)))
const res = await request(server).head( const res = await request(server).head(
bonobUrl bonobUrl
@@ -785,7 +785,7 @@ describe("server", () => {
pathname: `/stream/track/${trackId}` pathname: `/stream/track/${trackId}`
}) })
.path(), .path(),
).set('Authorization', `Bearer ${smapiAuthToken}`); ).set('Authorization', `Bearer ${smapiTokenAsString(smapiAuthToken)}`);
expect(res.status).toEqual(401); expect(res.status).toEqual(401);
}); });
@@ -816,7 +816,7 @@ describe("server", () => {
bonobUrl bonobUrl
.append({ pathname: `/stream/track/${trackId}`}) .append({ pathname: `/stream/track/${trackId}`})
.path() .path()
).set('Authorization', `Bearer ${smapiAuthToken}`); ).set('Authorization', `Bearer ${smapiTokenAsString(smapiAuthToken)}`);
expect(res.status).toEqual(trackStream.status); expect(res.status).toEqual(trackStream.status);
expect(res.headers["content-type"]).toEqual( expect(res.headers["content-type"]).toEqual(
@@ -843,7 +843,7 @@ describe("server", () => {
.append({ pathname: `/stream/track/${trackId}` }) .append({ pathname: `/stream/track/${trackId}` })
.path() .path()
) )
.set('Authorization', `Bearer ${smapiAuthToken}`); .set('Authorization', `Bearer ${smapiTokenAsString(smapiAuthToken)}`);
expect(res.status).toEqual(404); expect(res.status).toEqual(404);
expect(res.body).toEqual({}); expect(res.body).toEqual({});
@@ -865,14 +865,14 @@ describe("server", () => {
describe("when the Bearer token has expired", () => { describe("when the Bearer token has expired", () => {
it("should return a 401", async () => { it("should return a 401", async () => {
smapiAuthTokens.verify.mockReturnValue(E.left(new ExpiredTokenError(smapiAuthToken, 0))) smapiAuthTokens.verify.mockReturnValue(E.left(new ExpiredTokenError(serviceToken, 0)))
const res = await request(server) const res = await request(server)
.get( .get(
bonobUrl bonobUrl
.append({ pathname: `/stream/track/${trackId}` }) .append({ pathname: `/stream/track/${trackId}` })
.path() .path()
).set('Authorization', `Bearer ${smapiAuthToken}`); ).set('Authorization', `Bearer ${smapiTokenAsString(smapiAuthToken)}`);
expect(res.status).toEqual(401); expect(res.status).toEqual(401);
}); });
@@ -899,7 +899,7 @@ describe("server", () => {
bonobUrl bonobUrl
.append({ pathname: `/stream/track/${trackId}` }) .append({ pathname: `/stream/track/${trackId}` })
.path() .path()
).set('Authorization', `Bearer ${smapiAuthToken}`); ).set('Authorization', `Bearer ${smapiTokenAsString(smapiAuthToken)}`);
expect(res.status).toEqual(404); expect(res.status).toEqual(404);
@@ -931,7 +931,7 @@ describe("server", () => {
bonobUrl bonobUrl
.append({ pathname: `/stream/track/${trackId}` }) .append({ pathname: `/stream/track/${trackId}` })
.path() .path()
).set('Authorization', `Bearer ${smapiAuthToken}`); ).set('Authorization', `Bearer ${smapiTokenAsString(smapiAuthToken)}`);
expect(res.status).toEqual(stream.status); expect(res.status).toEqual(stream.status);
expect(res.headers["content-type"]).toEqual( expect(res.headers["content-type"]).toEqual(
@@ -971,7 +971,7 @@ describe("server", () => {
bonobUrl bonobUrl
.append({ pathname: `/stream/track/${trackId}` }) .append({ pathname: `/stream/track/${trackId}` })
.path() .path()
).set('Authorization', `Bearer ${smapiAuthToken}`); ).set('Authorization', `Bearer ${smapiTokenAsString(smapiAuthToken)}`);
expect(res.status).toEqual(stream.status); expect(res.status).toEqual(stream.status);
expect(res.headers["content-type"]).toEqual( expect(res.headers["content-type"]).toEqual(
@@ -1009,7 +1009,7 @@ describe("server", () => {
bonobUrl bonobUrl
.append({ pathname: `/stream/track/${trackId}` }) .append({ pathname: `/stream/track/${trackId}` })
.path() .path()
).set('Authorization', `Bearer ${smapiAuthToken}`); ).set('Authorization', `Bearer ${smapiTokenAsString(smapiAuthToken)}`);
expect(res.status).toEqual(stream.status); expect(res.status).toEqual(stream.status);
expect(res.header["content-type"]).toEqual( expect(res.header["content-type"]).toEqual(
@@ -1048,7 +1048,7 @@ describe("server", () => {
bonobUrl bonobUrl
.append({ pathname: `/stream/track/${trackId}` }) .append({ pathname: `/stream/track/${trackId}` })
.path() .path()
).set('Authorization', `Bearer ${smapiAuthToken}`); ).set('Authorization', `Bearer ${smapiTokenAsString(smapiAuthToken)}`);
expect(res.status).toEqual(stream.status); expect(res.status).toEqual(stream.status);
expect(res.header["content-type"]).toEqual( expect(res.header["content-type"]).toEqual(
@@ -1093,7 +1093,7 @@ describe("server", () => {
.append({ pathname: `/stream/track/${trackId}` }) .append({ pathname: `/stream/track/${trackId}` })
.path() .path()
) )
.set('Authorization', `Bearer ${smapiAuthToken}`) .set('Authorization', `Bearer ${smapiTokenAsString(smapiAuthToken)}`)
.set("Range", requestedRange); .set("Range", requestedRange);
expect(res.status).toEqual(stream.status); expect(res.status).toEqual(stream.status);
@@ -1137,7 +1137,7 @@ describe("server", () => {
.append({ pathname: `/stream/track/${trackId}` }) .append({ pathname: `/stream/track/${trackId}` })
.path() .path()
) )
.set('Authorization', `Bearer ${smapiAuthToken}`) .set('Authorization', `Bearer ${smapiTokenAsString(smapiAuthToken)}`)
.set("Range", "4000-5000"); .set("Range", "4000-5000");
expect(res.status).toEqual(stream.status); expect(res.status).toEqual(stream.status);