diff --git a/src/TokenProviders/Database/index.ts b/src/TokenProviders/Database/index.ts index 160d9ab..715d385 100644 --- a/src/TokenProviders/Database/index.ts +++ b/src/TokenProviders/Database/index.ts @@ -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 diff --git a/test-helpers/index.ts b/test-helpers/index.ts index 6e8aeaa..dc500a1 100644 --- a/test-helpers/index.ts +++ b/test-helpers/index.ts @@ -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) diff --git a/test/guards/oat.spec.ts b/test/guards/oat.spec.ts index 7bbef05..8f7d4c7 100644 --- a/test/guards/oat.spec.ts +++ b/test/guards/oat.spec.ts @@ -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: 'virk@adonisjs.com', + 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: 'virk@adonisjs.com', + 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) => {