diff --git a/backend/src/auth/auth.service.spec.ts b/backend/src/auth/auth.service.spec.ts index fd8aa014a..17c9297b6 100644 --- a/backend/src/auth/auth.service.spec.ts +++ b/backend/src/auth/auth.service.spec.ts @@ -230,7 +230,7 @@ describe('AuthService', () => { return authToken; }); const userByToken = await service.validateToken( - `${authToken.keyId}.${testSecret}`, + `hd2.${authToken.keyId}.${testSecret}`, ); expect(userByToken).toEqual({ ...user, @@ -238,14 +238,31 @@ describe('AuthService', () => { }); }); describe('fails:', () => { + it('the prefix is missing', async () => { + await expect( + service.validateToken(`${authToken.keyId}.${'a'.repeat(73)}`), + ).rejects.toThrow(TokenNotValidError); + }); + it('the prefix is wrong', async () => { + await expect( + service.validateToken(`hd1.${authToken.keyId}.${'a'.repeat(73)}`), + ).rejects.toThrow(TokenNotValidError); + }); it('the secret is missing', async () => { await expect( - service.validateToken(`${authToken.keyId}`), + service.validateToken(`hd2.${authToken.keyId}`), ).rejects.toThrow(TokenNotValidError); }); it('the secret is too long', async () => { await expect( - service.validateToken(`${authToken.keyId}.${'a'.repeat(73)}`), + service.validateToken(`hd2.${authToken.keyId}.${'a'.repeat(73)}`), + ).rejects.toThrow(TokenNotValidError); + }); + it('the token contains sections after the secret', async () => { + await expect( + service.validateToken( + `hd2.${authToken.keyId}.${'a'.repeat(73)}.extra`, + ), ).rejects.toThrow(TokenNotValidError); }); }); @@ -296,7 +313,7 @@ describe('AuthService', () => { (new Date().getTime() + 2 * 365 * 24 * 60 * 60 * 1000), ).toBeLessThanOrEqual(10000); expect(token.lastUsedAt).toBeNull(); - expect(token.secret.startsWith(token.keyId)).toBeTruthy(); + expect(token.secret.startsWith('hd2.' + token.keyId)).toBeTruthy(); }); it('with validUntil not 0', async () => { jest.spyOn(authTokenRepo, 'find').mockResolvedValueOnce([authToken]); @@ -313,7 +330,7 @@ describe('AuthService', () => { expect(token.label).toEqual(identifier); expect(token.validUntil.getTime()).toEqual(validUntil); expect(token.lastUsedAt).toBeNull(); - expect(token.secret.startsWith(token.keyId)).toBeTruthy(); + expect(token.secret.startsWith('hd2.' + token.keyId)).toBeTruthy(); }); it('should throw TooManyTokensError when number of tokens >= 200', async () => { jest diff --git a/backend/src/auth/auth.service.ts b/backend/src/auth/auth.service.ts index f95d63db7..135ee116e 100644 --- a/backend/src/auth/auth.service.ts +++ b/backend/src/auth/auth.service.ts @@ -21,6 +21,8 @@ import { TimestampMillis } from '../utils/timestamp'; import { AuthTokenDto, AuthTokenWithSecretDto } from './auth-token.dto'; import { AuthToken } from './auth-token.entity'; +export const AUTH_TOKEN_PREFIX = 'hd2'; + @Injectable() export class AuthService { constructor( @@ -32,8 +34,8 @@ export class AuthService { } async validateToken(tokenString: string): Promise { - const [keyId, secret] = tokenString.split('.'); - if (!secret) { + const [prefix, keyId, secret, ...rest] = tokenString.split('.'); + if (!keyId || !secret || prefix !== AUTH_TOKEN_PREFIX || rest.length > 0) { throw new TokenNotValidError('Invalid AuthToken format'); } if (secret.length != 86) { @@ -105,7 +107,7 @@ export class AuthService { )) as AuthToken; return this.toAuthTokenWithSecretDto( createdToken, - `${createdToken.keyId}.${secret}`, + `${AUTH_TOKEN_PREFIX}.${createdToken.keyId}.${secret}`, ); } diff --git a/backend/test/private-api/tokens.e2e-spec.ts b/backend/test/private-api/tokens.e2e-spec.ts index 9a54d262a..e40f251c2 100644 --- a/backend/test/private-api/tokens.e2e-spec.ts +++ b/backend/test/private-api/tokens.e2e-spec.ts @@ -51,7 +51,7 @@ describe('Tokens', () => { Date.now(), ); expect(response.body.lastUsedAt).toBe(null); - expect(response.body.secret.length).toBe(98); + expect(response.body.secret.length).toBe(102); }); it(`GET /tokens`, async () => {