@@ -31,19 +31,44 @@ const CryptoJS = require('crypto-js');
31
31
const AES = require ( 'crypto-js/aes' ) ;
32
32
import Flagsmith from 'flagsmith-nodejs' ;
33
33
import { LoginWithUniqueIdDto } from './dto/login.dto' ;
34
+ import { InjectRedis } from '@nestjs-modules/ioredis' ;
35
+ import Redis from 'ioredis' ;
36
+ const jwksClient = require ( 'jwks-rsa' ) ;
37
+ import * as jwt from 'jsonwebtoken' ;
34
38
35
39
CryptoJS . lib . WordArray . words ;
36
40
37
41
@Injectable ( )
38
42
export class ApiService {
43
+ private client : any
44
+ private getKey : any ;
39
45
encodedBase64Key ;
40
46
parsedBase64Key ;
41
47
constructor (
42
48
private configService : ConfigService ,
43
49
private readonly fusionAuthService : FusionauthService ,
44
50
private readonly otpService : OtpService ,
45
51
private readonly configResolverService : ConfigResolverService ,
46
- ) { }
52
+ @InjectRedis ( ) private readonly redis : Redis
53
+ ) {
54
+ this . client = jwksClient ( {
55
+ jwksUri : this . configService . get ( "JWKS_URI" ) ,
56
+ requestHeaders : { } , // Optional
57
+ timeout : 30000 , // Defaults to 30s
58
+ } ) ;
59
+
60
+ this . getKey = ( header : jwt . JwtHeader , callback : any ) => {
61
+ this . client . getSigningKey ( header . kid , ( err , key : any ) => {
62
+ if ( err ) {
63
+ console . error ( `Error fetching signing key: ${ err } ` ) ;
64
+ callback ( err ) ;
65
+ } else {
66
+ const signingKey = key . publicKey || key . rsaPublicKey ;
67
+ callback ( null , signingKey ) ;
68
+ }
69
+ } ) ;
70
+ } ;
71
+ }
47
72
48
73
login ( user : any , authHeader : string ) : Promise < SignupResponse > {
49
74
return this . fusionAuthService
@@ -535,6 +560,7 @@ export class ApiService {
535
560
3.2. If new user, register to this application.
536
561
4. Send login response with the token
537
562
*/
563
+ let otp = loginDto . password ;
538
564
const salt = this . configResolverService . getSalt ( loginDto . applicationId ) ;
539
565
let verifyOTPResult ;
540
566
if (
@@ -562,6 +588,7 @@ export class ApiService {
562
588
loginDto . password = salt + loginDto . password ; // mix OTP with salt
563
589
564
590
if ( verifyOTPResult . status === SMSResponseStatus . success ) {
591
+ let response ;
565
592
const {
566
593
statusFA,
567
594
userId,
@@ -595,19 +622,31 @@ export class ApiService {
595
622
id : registrationId ,
596
623
} ,
597
624
] ,
625
+ data : {
626
+ loginId : loginDto . loginId ,
627
+ fingerprint : loginDto ?. fingerprint ,
628
+ timestamp : loginDto ?. timestamp ,
629
+ otp
630
+ }
598
631
} ,
599
632
loginDto . applicationId ,
600
633
authHeader ,
601
634
) ;
602
- return this . login ( loginDto , authHeader ) ;
635
+ response = await this . login ( loginDto , authHeader ) ;
603
636
} else {
604
637
// create a new user
605
638
const createUserPayload : UserRegistration = {
606
639
user : {
607
640
timezone : "Asia/Kolkata" ,
608
641
username : loginDto . loginId ,
609
642
mobilePhone : loginDto . loginId ,
610
- password : loginDto . password
643
+ password : loginDto . password ,
644
+ data : {
645
+ loginId : loginDto . loginId ,
646
+ fingerprint : loginDto ?. fingerprint ,
647
+ timestamp : loginDto ?. timestamp ,
648
+ otp
649
+ }
611
650
} ,
612
651
registration : {
613
652
applicationId : loginDto . applicationId ,
@@ -626,8 +665,17 @@ export class ApiService {
626
665
if ( userId == null || user == null ) {
627
666
throw new HttpException ( err , HttpStatus . BAD_REQUEST ) ;
628
667
}
629
- return this . login ( loginDto , authHeader ) ;
668
+ response = await this . login ( loginDto , authHeader ) ;
669
+ }
670
+ let existingJWTS :any = await this . redis . get ( response ?. result ?. data ?. user ?. user ?. id ) ;
671
+ if ( existingJWTS ) {
672
+ existingJWTS = JSON . parse ( existingJWTS ) ;
673
+ } else {
674
+ existingJWTS = [ ]
630
675
}
676
+ existingJWTS . push ( response ?. result ?. data ?. user ?. token ) ;
677
+ await this . redis . set ( response ?. result ?. data ?. user ?. user ?. id , JSON . stringify ( existingJWTS ) ) ;
678
+ return response ;
631
679
} else {
632
680
const response : SignupResponse = new SignupResponse ( ) . init ( uuidv4 ( ) ) ;
633
681
response . responseCode = ResponseCode . FAILURE ;
@@ -706,4 +754,71 @@ export class ApiService {
706
754
}
707
755
return registration ;
708
756
}
757
+
758
+ async verifyFusionAuthJWT ( token : string ) : Promise < any > {
759
+ return new Promise < any > ( ( resolve , reject ) => {
760
+ jwt . verify ( token , this . getKey , async ( err , decoded ) => {
761
+ if ( err ) {
762
+ console . error ( 'APP JWT verification error:' , err ) ;
763
+ resolve ( {
764
+ isValidFusionAuthToken : false ,
765
+ claims : null
766
+ } )
767
+ } else {
768
+ resolve ( {
769
+ isValidFusionAuthToken : true ,
770
+ claims : decoded
771
+ } )
772
+ }
773
+ } ) ;
774
+ } ) ;
775
+ }
776
+
777
+ async verifyJWT ( token :string ) : Promise < any > {
778
+ const { isValidFusionAuthToken, claims} = await this . verifyFusionAuthJWT ( token ) ;
779
+
780
+ let existingUserJWTS :any = JSON . parse ( await this . redis . get ( claims . sub ) ) ;
781
+
782
+ if ( ! isValidFusionAuthToken ) {
783
+ if ( existingUserJWTS . indexOf ( token ) != - 1 ) {
784
+ existingUserJWTS . splice ( existingUserJWTS . indexOf ( token ) , 1 ) ;
785
+ await this . redis . set ( claims . sub , JSON . stringify ( existingUserJWTS ) ) ;
786
+ }
787
+ return {
788
+ "isValid" : false ,
789
+ "message" : "Invalid/Expired token."
790
+ }
791
+ }
792
+
793
+ if ( existingUserJWTS . indexOf ( token ) == - 1 ) {
794
+ return {
795
+ "isValid" : false ,
796
+ "message" : "Token is not authorized."
797
+ }
798
+ }
799
+
800
+ return {
801
+ "isValid" : true ,
802
+ "message" : "Token is valid."
803
+ }
804
+ }
805
+
806
+ async logout ( token :string ) : Promise < any > {
807
+ const { isValidFusionAuthToken, claims} = await this . verifyFusionAuthJWT ( token ) ;
808
+ if ( isValidFusionAuthToken ) {
809
+ let existingUserJWTS :any = JSON . parse ( await this . redis . get ( claims . sub ) ) ;
810
+ if ( existingUserJWTS . indexOf ( token ) != - 1 ) {
811
+ existingUserJWTS . splice ( existingUserJWTS . indexOf ( token ) , 1 ) ;
812
+ await this . redis . set ( claims . sub , JSON . stringify ( existingUserJWTS ) ) ;
813
+ }
814
+ return {
815
+ "message" : "Logout successful. Token invalidated."
816
+ }
817
+ } else {
818
+ return {
819
+ "message" : "Invalid or expired token."
820
+ }
821
+ }
822
+ }
823
+
709
824
}
0 commit comments