mirror of
https://github.com/standardnotes/server
synced 2026-07-14 00:01:54 -04:00
fix(auth): subscription token ttl
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
export type SubscriptionToken = {
|
||||
userUuid: string
|
||||
token: string
|
||||
expiresAt: number
|
||||
ttl: number
|
||||
}
|
||||
|
||||
+2
-8
@@ -1,7 +1,6 @@
|
||||
import 'reflect-metadata'
|
||||
|
||||
import { CryptoNode } from '@standardnotes/sncrypto-node'
|
||||
import { TimerInterface } from '@standardnotes/time'
|
||||
import { SubscriptionTokenRepositoryInterface } from '../../Subscription/SubscriptionTokenRepositoryInterface'
|
||||
|
||||
import { CreateSubscriptionToken } from './CreateSubscriptionToken'
|
||||
@@ -10,10 +9,9 @@ import { Logger } from 'winston'
|
||||
describe('CreateSubscriptionToken', () => {
|
||||
let subscriptionTokenRepository: SubscriptionTokenRepositoryInterface
|
||||
let cryptoNode: CryptoNode
|
||||
let timer: TimerInterface
|
||||
let logger: Logger
|
||||
|
||||
const createUseCase = () => new CreateSubscriptionToken(subscriptionTokenRepository, cryptoNode, timer, logger)
|
||||
const createUseCase = () => new CreateSubscriptionToken(subscriptionTokenRepository, cryptoNode, logger)
|
||||
|
||||
beforeEach(() => {
|
||||
subscriptionTokenRepository = {} as jest.Mocked<SubscriptionTokenRepositoryInterface>
|
||||
@@ -22,10 +20,6 @@ describe('CreateSubscriptionToken', () => {
|
||||
cryptoNode = {} as jest.Mocked<CryptoNode>
|
||||
cryptoNode.generateRandomKey = jest.fn().mockReturnValueOnce('random-string')
|
||||
|
||||
timer = {} as jest.Mocked<TimerInterface>
|
||||
timer.convertStringDateToMicroseconds = jest.fn().mockReturnValue(1)
|
||||
timer.getUTCDateNHoursAhead = jest.fn().mockReturnValue(new Date(1))
|
||||
|
||||
logger = {} as jest.Mocked<Logger>
|
||||
logger.error = jest.fn()
|
||||
})
|
||||
@@ -38,7 +32,7 @@ describe('CreateSubscriptionToken', () => {
|
||||
expect(subscriptionTokenRepository.save).toHaveBeenCalledWith({
|
||||
userUuid: '1-2-3',
|
||||
token: 'random-string',
|
||||
expiresAt: 1,
|
||||
ttl: 10_800,
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { CryptoNode } from '@standardnotes/sncrypto-node'
|
||||
import { TimerInterface } from '@standardnotes/time'
|
||||
import { inject, injectable } from 'inversify'
|
||||
import { Logger } from 'winston'
|
||||
|
||||
@@ -15,7 +14,6 @@ export class CreateSubscriptionToken implements UseCaseInterface {
|
||||
@inject(TYPES.SubscriptionTokenRepository)
|
||||
private subscriptionTokenRepository: SubscriptionTokenRepositoryInterface,
|
||||
@inject(TYPES.CryptoNode) private cryptoNode: CryptoNode,
|
||||
@inject(TYPES.Timer) private timer: TimerInterface,
|
||||
@inject(TYPES.Logger) private logger: Logger,
|
||||
) {}
|
||||
|
||||
@@ -25,7 +23,7 @@ export class CreateSubscriptionToken implements UseCaseInterface {
|
||||
const subscriptionToken = {
|
||||
userUuid: dto.userUuid,
|
||||
token,
|
||||
expiresAt: this.timer.convertStringDateToMicroseconds(this.timer.getUTCDateNHoursAhead(3).toString()),
|
||||
ttl: 10_800,
|
||||
}
|
||||
|
||||
const subscriptionTokenWasSaved = await this.subscriptionTokenRepository.save(subscriptionToken)
|
||||
|
||||
@@ -1,25 +1,19 @@
|
||||
import 'reflect-metadata'
|
||||
|
||||
import * as IORedis from 'ioredis'
|
||||
import { TimerInterface } from '@standardnotes/time'
|
||||
|
||||
import { RedisSubscriptionTokenRepository } from './RedisSubscriptionTokenRepository'
|
||||
import { SubscriptionToken } from '../../Domain/Subscription/SubscriptionToken'
|
||||
|
||||
describe('RedisSubscriptionTokenRepository', () => {
|
||||
let redisClient: IORedis.Redis
|
||||
let timer: TimerInterface
|
||||
|
||||
const createRepository = () => new RedisSubscriptionTokenRepository(redisClient, timer)
|
||||
const createRepository = () => new RedisSubscriptionTokenRepository(redisClient)
|
||||
|
||||
beforeEach(() => {
|
||||
redisClient = {} as jest.Mocked<IORedis.Redis>
|
||||
redisClient.set = jest.fn().mockReturnValue('OK')
|
||||
redisClient.setex = jest.fn().mockReturnValue('OK')
|
||||
redisClient.get = jest.fn()
|
||||
redisClient.expireat = jest.fn().mockReturnValue(1)
|
||||
|
||||
timer = {} as jest.Mocked<TimerInterface>
|
||||
timer.convertMicrosecondsToSeconds = jest.fn().mockReturnValue(1)
|
||||
})
|
||||
|
||||
it('should get a user uuid in exchange for an subscription token', async () => {
|
||||
@@ -42,29 +36,25 @@ describe('RedisSubscriptionTokenRepository', () => {
|
||||
const subscriptionToken: SubscriptionToken = {
|
||||
userUuid: '1-2-3',
|
||||
token: 'random-string',
|
||||
expiresAt: 123,
|
||||
ttl: 123,
|
||||
}
|
||||
|
||||
expect(await createRepository().save(subscriptionToken)).toBeTruthy()
|
||||
|
||||
expect(redisClient.set).toHaveBeenCalledWith('subscription-token:random-string', '1-2-3')
|
||||
|
||||
expect(redisClient.expireat).toHaveBeenCalledWith('subscription-token:random-string', 1)
|
||||
expect(redisClient.setex).toHaveBeenCalledWith('subscription-token:random-string', 123, '1-2-3')
|
||||
})
|
||||
|
||||
it('should indicate subscription token was not saved', async () => {
|
||||
redisClient.set = jest.fn().mockReturnValue(null)
|
||||
redisClient.setex = jest.fn().mockReturnValue(null)
|
||||
|
||||
const subscriptionToken: SubscriptionToken = {
|
||||
userUuid: '1-2-3',
|
||||
token: 'random-string',
|
||||
expiresAt: 123,
|
||||
ttl: 123,
|
||||
}
|
||||
|
||||
expect(await createRepository().save(subscriptionToken)).toBeFalsy()
|
||||
|
||||
expect(redisClient.set).toHaveBeenCalledWith('subscription-token:random-string', '1-2-3')
|
||||
|
||||
expect(redisClient.expireat).toHaveBeenCalledWith('subscription-token:random-string', 1)
|
||||
expect(redisClient.setex).toHaveBeenCalledWith('subscription-token:random-string', 123, '1-2-3')
|
||||
})
|
||||
})
|
||||
|
||||
@@ -4,16 +4,12 @@ import { inject, injectable } from 'inversify'
|
||||
import TYPES from '../../Bootstrap/Types'
|
||||
import { SubscriptionToken } from '../../Domain/Subscription/SubscriptionToken'
|
||||
import { SubscriptionTokenRepositoryInterface } from '../../Domain/Subscription/SubscriptionTokenRepositoryInterface'
|
||||
import { TimerInterface } from '@standardnotes/time'
|
||||
|
||||
@injectable()
|
||||
export class RedisSubscriptionTokenRepository implements SubscriptionTokenRepositoryInterface {
|
||||
private readonly PREFIX = 'subscription-token'
|
||||
|
||||
constructor(
|
||||
@inject(TYPES.Redis) private redisClient: IORedis.Redis,
|
||||
@inject(TYPES.Timer) private timer: TimerInterface,
|
||||
) {}
|
||||
constructor(@inject(TYPES.Redis) private redisClient: IORedis.Redis) {}
|
||||
|
||||
async getUserUuidByToken(token: string): Promise<string | undefined> {
|
||||
const userUuid = await this.redisClient.get(`${this.PREFIX}:${token}`)
|
||||
@@ -26,11 +22,9 @@ export class RedisSubscriptionTokenRepository implements SubscriptionTokenReposi
|
||||
|
||||
async save(subscriptionToken: SubscriptionToken): Promise<boolean> {
|
||||
const key = `${this.PREFIX}:${subscriptionToken.token}`
|
||||
const expiresAtTimestampInSeconds = this.timer.convertMicrosecondsToSeconds(subscriptionToken.expiresAt)
|
||||
|
||||
const wasSet = await this.redisClient.set(key, subscriptionToken.userUuid)
|
||||
const timeoutWasSet = await this.redisClient.expireat(key, expiresAtTimestampInSeconds)
|
||||
const wasSet = await this.redisClient.setex(key, subscriptionToken.ttl, subscriptionToken.userUuid)
|
||||
|
||||
return wasSet === 'OK' && timeoutWasSet !== 0
|
||||
return wasSet === 'OK'
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user