Skip to content

Commit

Permalink
fix: handle different expires_at input values
Browse files Browse the repository at this point in the history
The sqlite returns a timestamp and other dialects returns a date
object and we must handle them properly to cast the value to
luxon datetime object
  • Loading branch information
thetutlage committed Jun 26, 2020
1 parent 14a4da2 commit 163fe31
Show file tree
Hide file tree
Showing 3 changed files with 118 additions and 2 deletions.
13 changes: 12 additions & 1 deletion src/TokenProviders/Database/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,18 @@ export class TokenDatabaseProvider implements TokenProviderContract {
}

const { name, user_id, token: value, expires_at, type, ...meta } = tokenRow
const expiresAt = expires_at ? DateTime.fromFormat(expires_at, client.dialect.dateTimeFormat) : undefined
let expiresAt: undefined | DateTime

/**
* Parse dialect date to an instance of Luxon
*/
if (expires_at instanceof Date) {
expiresAt = DateTime.fromJSDate(expires_at)
} else if (expires_at && typeof (expires_at) === 'string') {
expiresAt = DateTime.fromFormat(expires_at, client.dialect.dateTimeFormat)
} else if (expires_at && typeof (expires_at) === 'number') {
expiresAt = DateTime.fromMillis(expires_at)
}

/**
* Ensure token isn't expired
Expand Down
2 changes: 1 addition & 1 deletion test-helpers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ async function createTokensTable (client: QueryClientContract) {
table.string('name').notNullable()
table.string('type').notNullable()
table.string('token').notNullable()
table.dateTime('expires_at').nullable()
table.timestamp('expires_at', { useTz: true }).nullable()
table.string('ip_address').nullable()
table.string('device_name').nullable()
table.timestamps(true)
Expand Down
105 changes: 105 additions & 0 deletions test/guards/oat.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -669,6 +669,111 @@ test.group('OAT Guard | authenticate', (group) => {
errors: [{ message: 'E_INVALID_API_TOKEN: Invalid API Token' }],
})
})

test('raise error when token is expired', async (assert) => {
const User = getUserModel(BaseModel)
const user = await User.create({
username: 'virk',
email: '[email protected]',
password: await hash.make('secret'),
})

const apiTokensGuard = getApiTokensGuard(
getLucidProvider({ model: User }),
getLucidProviderConfig({ model: User }),
getCtx(),
getTokensDbProvider(db),
)

const token = await apiTokensGuard.loginViaId(user.id, {
device_name: 'Android',
ip_address: '192.168.1.1',
})

await db.from('api_tokens').update({
expires_at: DateTime.local().minus({ days: 1 }).toJSDate(),
})

const server = createServer(async (req, res) => {
const ctx = getCtx(req, res)
const oat1 = getApiTokensGuard(
getLucidProvider({ model: User }),
getLucidProviderConfig({ model: User }),
ctx,
getTokensDbProvider(db),
)

try {
await oat1.authenticate()
ctx.response.send(oat1.token)
} catch (error) {
error.handle(error, ctx)
}

ctx.response.finish()
})

const { body } = await supertest(server)
.get('/')
.set('Accept', 'application/json')
.set('Authorization', `${token.type} ${token.token}`)
.expect(401)

assert.deepEqual(body, {
errors: [{ message: 'E_INVALID_API_TOKEN: Invalid API Token' }],
})
})

test('work fine when token is not expired', async (assert) => {
const User = getUserModel(BaseModel)
const user = await User.create({
username: 'virk',
email: '[email protected]',
password: await hash.make('secret'),
})

const apiTokensGuard = getApiTokensGuard(
getLucidProvider({ model: User }),
getLucidProviderConfig({ model: User }),
getCtx(),
getTokensDbProvider(db),
)

const token = await apiTokensGuard.loginViaId(user.id, {
device_name: 'Android',
ip_address: '192.168.1.1',
expiresIn: '30 mins',
})

const server = createServer(async (req, res) => {
const ctx = getCtx(req, res)
const oat1 = getApiTokensGuard(
getLucidProvider({ model: User }),
getLucidProviderConfig({ model: User }),
ctx,
getTokensDbProvider(db),
)

try {
await oat1.authenticate()
ctx.response.send(oat1.token)
} catch (error) {
error.handle(error, ctx)
}

ctx.response.finish()
})

const { body } = await supertest(server)
.get('/')
.set('Accept', 'application/json')
.set('Authorization', `${token.type} ${token.token}`)
.expect(200)

assert.equal(body.name, 'Opaque Access Token')
assert.equal(body.type, 'opaque_token')
assert.exists(body.tokenHash)
})
})

test.group('OAT Guard | logout', (group) => {
Expand Down

0 comments on commit 163fe31

Please sign in to comment.