feat(web,server): manage authorized devices (#2329)

* feat: manage authorized devices

* chore: open api

* get header from mobile app

* write header from mobile app

* styling

* fix unit test

* feat: use relative time

* feat: update access time

* fix: tests

* chore: confirm wording

* chore: bump test coverage thresholds

* feat: add some icons

* chore: icon tweaks

---------

Co-authored-by: Alex Tran <alex.tran1502@gmail.com>
This commit is contained in:
Jason Rasmussen 2023-04-25 22:19:23 -04:00 committed by GitHub
parent aa91b946fa
commit b8313abfa8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
41 changed files with 1209 additions and 93 deletions

View file

@ -1,5 +1,7 @@
import { UserEntity } from '@app/infra/entities';
import { UserEntity, UserTokenEntity } from '@app/infra/entities';
import { Injectable, UnauthorizedException } from '@nestjs/common';
import { DateTime } from 'luxon';
import { LoginDetails } from '../auth';
import { ICryptoRepository } from '../crypto';
import { IUserTokenRepository } from './user-token.repository';
@ -9,9 +11,16 @@ export class UserTokenCore {
async validate(tokenValue: string) {
const hashedToken = this.crypto.hashSha256(tokenValue);
const token = await this.repository.get(hashedToken);
let token = await this.repository.getByToken(hashedToken);
if (token?.user) {
const now = DateTime.now();
const updatedAt = DateTime.fromJSDate(token.updatedAt);
const diff = now.diff(updatedAt, ['hours']);
if (diff.hours > 1) {
token = await this.repository.save({ ...token, updatedAt: new Date() });
}
return {
...token.user,
isPublicUser: false,
@ -25,18 +34,24 @@ export class UserTokenCore {
throw new UnauthorizedException('Invalid user token');
}
public async createToken(user: UserEntity): Promise<string> {
async create(user: UserEntity, loginDetails: LoginDetails): Promise<string> {
const key = this.crypto.randomBytes(32).toString('base64').replace(/\W/g, '');
const token = this.crypto.hashSha256(key);
await this.repository.create({
token,
user,
deviceOS: loginDetails.deviceOS,
deviceType: loginDetails.deviceType,
});
return key;
}
public async deleteToken(id: string): Promise<void> {
await this.repository.delete(id);
async delete(userId: string, id: string): Promise<void> {
await this.repository.delete(userId, id);
}
getAll(userId: string): Promise<UserTokenEntity[]> {
return this.repository.getAll(userId);
}
}