-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #4 from ixofoundation/develop
Add matrix creds passing to login flows
- Loading branch information
Showing
6 changed files
with
284 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
import { Body, Controller, HttpException, Post, Res } from '@nestjs/common'; | ||
import { ApiTags } from '@nestjs/swagger'; | ||
import { MatrixService } from './matrix.service'; | ||
import { MatrixLoginFetchDto, MatrixLoginCreateDto } from './matrix.dto'; | ||
import { Response } from 'express'; | ||
|
||
@Controller('matrix') | ||
@ApiTags('Matrix') | ||
export class MatrixController { | ||
constructor(private readonly matrixService: MatrixService) {} | ||
|
||
@Post('/login/create') // for mobile | ||
createMatrixLoginRequest(@Body() dto: MatrixLoginCreateDto) { | ||
try { | ||
return this.matrixService.createMatrixLogin(dto); | ||
} catch (error) { | ||
throw new HttpException(error.message, 400); | ||
} | ||
} | ||
|
||
private fetchLoginTimeout = 10000; // 12 seconds timeout | ||
private fetchLoginPollInterval = 1500; // check every 1.5 second | ||
@Post('/login/fetch') // for client | ||
fetchMatrixLoginRequest( | ||
@Body() dto: MatrixLoginFetchDto, | ||
@Res() res: Response, | ||
) { | ||
const startTime = Date.now(); | ||
const poll = async (): Promise<any> => { | ||
try { | ||
// Check if the client disconnected before making the next call | ||
if (res.destroyed) return; | ||
|
||
const result = await this.matrixService.fetchMatrixLogin(dto); | ||
if ( | ||
result.success || // if success result | ||
result.code !== 418 || // or if failed result but code is not 418(polling code) | ||
Date.now() - startTime > this.fetchLoginTimeout // or if timeout | ||
) { | ||
return res.send(result); | ||
} | ||
|
||
await new Promise((resolve) => | ||
setTimeout(resolve, this.fetchLoginPollInterval), | ||
); | ||
return poll(); | ||
} catch (error) { | ||
throw new HttpException(error.message, 400); | ||
} | ||
}; | ||
return poll(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
export class MatrixLoginCreateDto { | ||
hash: string; | ||
secureHash: string; | ||
data: string; | ||
success: boolean; | ||
} | ||
|
||
export class MatrixLoginFetchDto { | ||
hash: string; | ||
secureNonce: string; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
import { Module } from '@nestjs/common'; | ||
import { MatrixService } from './matrix.service'; | ||
import { MatrixController } from './matrix.controller'; | ||
|
||
@Module({ | ||
controllers: [MatrixController], | ||
providers: [MatrixService], | ||
}) | ||
export class MatrixModule {} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
import { Injectable } from '@nestjs/common'; | ||
import { MatrixLoginFetchDto, MatrixLoginCreateDto } from './matrix.dto'; | ||
import { PrismaService } from 'nestjs-prisma'; | ||
import { Cron, CronExpression } from '@nestjs/schedule'; | ||
import { generateSecureHash } from '@ixo/signx-sdk'; | ||
import { returnError, returnSuccess } from 'src/utils'; | ||
|
||
@Injectable() | ||
export class MatrixService { | ||
constructor(private prisma: PrismaService) {} | ||
|
||
async createMatrixLogin(dto: MatrixLoginCreateDto) { | ||
// validate request | ||
if ( | ||
!dto.hash || | ||
!dto.secureHash || | ||
!dto.data || | ||
typeof dto.success !== 'boolean' | ||
) { | ||
return returnError('Invalid request, missing parameters'); | ||
} | ||
|
||
const validUntil = new Date(Date.now() + 1000 * 60 * 2); // 2 minutes | ||
|
||
await this.prisma.login.upsert({ | ||
where: { hash: dto.hash }, | ||
create: { | ||
hash: dto.hash, | ||
secureHash: dto.secureHash, | ||
data: dto.data, | ||
validUntil, | ||
success: dto.success, | ||
}, | ||
update: { | ||
secureHash: dto.secureHash, | ||
data: dto.data, | ||
validUntil, | ||
success: dto.success, | ||
}, | ||
}); | ||
|
||
return returnSuccess({ | ||
message: 'Matrix login request created successfully', | ||
}); | ||
} | ||
|
||
async fetchMatrixLogin(dto: MatrixLoginFetchDto): Promise<any> { | ||
// validate request | ||
if (!dto.hash || !dto.secureNonce) { | ||
return returnError('Invalid request, missing parameters'); | ||
} | ||
|
||
const login = await this.prisma.login.findUnique({ | ||
where: { hash: dto.hash }, | ||
}); | ||
if (!login) { | ||
return returnError('Matrix login request not found', 418); // 418 I'm a teapot, for sdk to know to keep polling | ||
} | ||
|
||
// validate request | ||
const secureHash = generateSecureHash(dto.hash, dto.secureNonce); | ||
if (login.secureHash !== secureHash) { | ||
return returnError('Invalid request, hash mismatch'); | ||
} | ||
if (login.validUntil < new Date()) { | ||
return returnError('Matrix login request expired'); | ||
} | ||
|
||
// remove login request after fetching | ||
await this.prisma.login.delete({ where: { hash: dto.hash } }); | ||
|
||
return returnSuccess({ | ||
message: 'Matrix login request fetched successfully', | ||
data: login.data, | ||
success: login.success, | ||
}); | ||
} | ||
|
||
// clear expired login requests every minute | ||
@Cron(CronExpression.EVERY_5_MINUTES) | ||
async clearExpiredLogins() { | ||
await this.prisma.login.deleteMany({ | ||
where: { | ||
validUntil: { | ||
lte: new Date(), | ||
}, | ||
}, | ||
}); | ||
} | ||
} |