diff --git a/src/access_tokens.ts b/src/access_tokens.ts index ca21e9b..90898de 100644 --- a/src/access_tokens.ts +++ b/src/access_tokens.ts @@ -1,5 +1,7 @@ import dayjs, { Dayjs } from "dayjs"; import { v4 as uuid } from "uuid"; +import crypto from "crypto"; + import { Encryption } from "./encryption"; import logger from "./logger"; @@ -94,3 +96,25 @@ export class AccessTokenPerAuthToken implements AccessTokens { authTokenFor = (value: string): string | undefined => this.accessTokenToAuthToken.get(value); } + +export const sha256 = (salt: string) => (authToken: string) => crypto + .createHash("sha256") + .update(`${authToken}${salt}`) + .digest("hex") + +export class InMemoryAccessTokens implements AccessTokens { + tokens = new Map(); + minter; + + constructor(minter: (authToken: string) => string) { + this.minter = minter + } + + mint = (authToken: string): string => { + const accessToken = this.minter(authToken); + this.tokens.set(accessToken, authToken); + return accessToken; + } + + authTokenFor = (value: string): string | undefined => this.tokens.get(value); +} diff --git a/src/app.ts b/src/app.ts index e21ffbf..d9dde3d 100644 --- a/src/app.ts +++ b/src/app.ts @@ -3,6 +3,8 @@ import server from "./server"; import logger from "./logger"; import { Navidrome } from "./navidrome"; import encryption from "./encryption"; +import { InMemoryAccessTokens, sha256 } from "./access_tokens"; +import { InMemoryLinkCodes } from "./link_codes"; const PORT = +(process.env["BONOB_PORT"] || 4534); const WEB_ADDRESS = @@ -18,6 +20,7 @@ const bonob = bonobService( WEB_ADDRESS, "AppLink" ); +const secret = process.env["BONOB_SECRET"] || "bonob"; const sonosSystem = sonos(SONOS_DEVICE_DISCOVERY, SONOS_SEED_HOST); if(process.env["BONOB_SONOS_AUTO_REGISTER"] == "true") { @@ -34,8 +37,10 @@ const app = server( WEB_ADDRESS, new Navidrome( process.env["BONOB_NAVIDROME_URL"] || "http://localhost:4533", - encryption(process.env["BONOB_SECRET"] || "bonob") - ) + encryption(secret) + ), + new InMemoryLinkCodes(), + new InMemoryAccessTokens(sha256(secret)) ); app.listen(PORT, () => { diff --git a/tests/access_tokens.test.ts b/tests/access_tokens.test.ts index 331b208..478d368 100644 --- a/tests/access_tokens.test.ts +++ b/tests/access_tokens.test.ts @@ -5,6 +5,8 @@ import { AccessTokenPerAuthToken, EncryptedAccessTokens, ExpiringAccessTokens, + InMemoryAccessTokens, + sha256 } from "../src/access_tokens"; import { Encryption } from "../src/encryption"; @@ -207,5 +209,65 @@ describe("AccessTokenPerAuthToken", () => { expect(accessTokens.authTokenFor(uuid())).toBeUndefined(); }); }); - +}); + +describe('sha256 minter', () => { + it('should return the same value for the same salt and authToken', () => { + const authToken = uuid(); + const token1 = sha256("salty")(authToken); + const token2 = sha256("salty")(authToken); + + expect(token1).not.toEqual(authToken); + expect(token1).toEqual(token2); + }); + + it('should returrn different values for the same salt but different authTokens', () => { + const authToken1 = uuid(); + const authToken2 = uuid(); + + const token1 = sha256("salty")(authToken1); + const token2= sha256("salty")(authToken2); + + expect(token1).not.toEqual(token2); + }); + + it('should return different values for the same authToken but different salts', () => { + const authToken = uuid(); + + const token1 = sha256("salt1")(authToken); + const token2= sha256("salt2")(authToken); + + expect(token1).not.toEqual(token2); + }); +}); + +describe("InMemoryAccessTokens", () => { + const reverseAuthToken = (authToken: string) => authToken.split("").reverse().join(""); + + const accessTokens = new InMemoryAccessTokens(reverseAuthToken); + + it("should return the same access token for the same auth token", () => { + const authToken = "token1"; + + const accessToken1 = accessTokens.mint(authToken); + const accessToken2 = accessTokens.mint(authToken); + + expect(accessToken1).not.toEqual(authToken); + expect(accessToken1).toEqual(accessToken2); + }); + + describe("when there is an auth token for the access token", () => { + it("should be able to retrieve it", () => { + const authToken = uuid(); + const accessToken = accessTokens.mint(authToken); + + expect(accessTokens.authTokenFor(accessToken)).toEqual(authToken); + }); + }); + + describe("when there is no auth token for the access token", () => { + it("should return undefined", () => { + expect(accessTokens.authTokenFor(uuid())).toBeUndefined(); + }); + }); });