@@ -31,6 +31,10 @@ 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
@@ -43,6 +47,7 @@ export class ApiService {
43
47
private readonly fusionAuthService : FusionauthService ,
44
48
private readonly otpService : OtpService ,
45
49
private readonly configResolverService : ConfigResolverService ,
50
+ @InjectRedis ( ) private readonly redis : Redis
46
51
) { }
47
52
48
53
login ( user : any , authHeader : string ) : Promise < SignupResponse > {
@@ -535,6 +540,7 @@ export class ApiService {
535
540
3.2. If new user, register to this application.
536
541
4. Send login response with the token
537
542
*/
543
+ let otp = loginDto . password ;
538
544
const salt = this . configResolverService . getSalt ( loginDto . applicationId ) ;
539
545
let verifyOTPResult ;
540
546
if (
@@ -562,6 +568,7 @@ export class ApiService {
562
568
loginDto . password = salt + loginDto . password ; // mix OTP with salt
563
569
564
570
if ( verifyOTPResult . status === SMSResponseStatus . success ) {
571
+ let response ;
565
572
const {
566
573
statusFA,
567
574
userId,
@@ -595,19 +602,31 @@ export class ApiService {
595
602
id : registrationId ,
596
603
} ,
597
604
] ,
605
+ data : {
606
+ loginId : loginDto . loginId ,
607
+ fingerprint : loginDto ?. fingerprint ,
608
+ timestamp : loginDto ?. timestamp ,
609
+ otp
610
+ }
598
611
} ,
599
612
loginDto . applicationId ,
600
613
authHeader ,
601
614
) ;
602
- return this . login ( loginDto , authHeader ) ;
615
+ response = await this . login ( loginDto , authHeader ) ;
603
616
} else {
604
617
// create a new user
605
618
const createUserPayload : UserRegistration = {
606
619
user : {
607
620
timezone : "Asia/Kolkata" ,
608
621
username : loginDto . loginId ,
609
622
mobilePhone : loginDto . loginId ,
610
- password : loginDto . password
623
+ password : loginDto . password ,
624
+ data : {
625
+ loginId : loginDto . loginId ,
626
+ fingerprint : loginDto ?. fingerprint ,
627
+ timestamp : loginDto ?. timestamp ,
628
+ otp
629
+ }
611
630
} ,
612
631
registration : {
613
632
applicationId : loginDto . applicationId ,
@@ -626,8 +645,17 @@ export class ApiService {
626
645
if ( userId == null || user == null ) {
627
646
throw new HttpException ( err , HttpStatus . BAD_REQUEST ) ;
628
647
}
629
- return this . login ( loginDto , authHeader ) ;
648
+ response = await this . login ( loginDto , authHeader ) ;
649
+ }
650
+ let existingJWTS :any = await this . redis . get ( response ?. result ?. data ?. user ?. user ?. id ) ;
651
+ if ( existingJWTS ) {
652
+ existingJWTS = JSON . parse ( existingJWTS ) ;
653
+ } else {
654
+ existingJWTS = [ ]
630
655
}
656
+ existingJWTS . push ( response ?. result ?. data ?. user ?. token ) ;
657
+ await this . redis . set ( response ?. result ?. data ?. user ?. user ?. id , JSON . stringify ( existingJWTS ) ) ;
658
+ return response ;
631
659
} else {
632
660
const response : SignupResponse = new SignupResponse ( ) . init ( uuidv4 ( ) ) ;
633
661
response . responseCode = ResponseCode . FAILURE ;
@@ -706,4 +734,89 @@ export class ApiService {
706
734
}
707
735
return registration ;
708
736
}
737
+
738
+ async verifyFusionAuthJWT ( token : string ) : Promise < any > {
739
+ let client = jwksClient ( {
740
+ jwksUri : this . configService . get ( "JWKS_URI" ) ,
741
+ requestHeaders : { } , // Optional
742
+ timeout : 30000 , // Defaults to 30s
743
+ } ) ;
744
+
745
+ let getKey = ( header : jwt . JwtHeader , callback : any ) => {
746
+ client . getSigningKey ( header . kid , ( err , key : any ) => {
747
+ if ( err ) {
748
+ console . error ( `Error fetching signing key: ${ err } ` ) ;
749
+ callback ( err ) ;
750
+ } else {
751
+ const signingKey = key . publicKey || key . rsaPublicKey ;
752
+ callback ( null , signingKey ) ;
753
+ }
754
+ } ) ;
755
+ } ;
756
+
757
+ return new Promise < any > ( ( resolve , reject ) => {
758
+ jwt . verify ( token , getKey , async ( err , decoded ) => {
759
+ if ( err ) {
760
+ console . error ( 'APP JWT verification error:' , err ) ;
761
+ resolve ( {
762
+ isValidFusionAuthToken : false ,
763
+ claims : null
764
+ } )
765
+ } else {
766
+ resolve ( {
767
+ isValidFusionAuthToken : true ,
768
+ claims : decoded
769
+ } )
770
+ }
771
+ } ) ;
772
+ } ) ;
773
+ }
774
+
775
+ async verifyJWT ( token :string ) : Promise < any > {
776
+ const { isValidFusionAuthToken, claims} = await this . verifyFusionAuthJWT ( token ) ;
777
+
778
+ let existingUserJWTS :any = JSON . parse ( await this . redis . get ( claims . sub ) ) ;
779
+
780
+ if ( ! isValidFusionAuthToken ) {
781
+ if ( existingUserJWTS . indexOf ( token ) != - 1 ) {
782
+ existingUserJWTS . splice ( existingUserJWTS . indexOf ( token ) , 1 ) ;
783
+ await this . redis . set ( claims . sub , JSON . stringify ( existingUserJWTS ) ) ;
784
+ }
785
+ return {
786
+ "isValid" : false ,
787
+ "message" : "Invalid/Expired token."
788
+ }
789
+ }
790
+
791
+ if ( existingUserJWTS . indexOf ( token ) == - 1 ) {
792
+ return {
793
+ "isValid" : false ,
794
+ "message" : "Token is not authorized."
795
+ }
796
+ }
797
+
798
+ return {
799
+ "isValid" : true ,
800
+ "message" : "Token is valid."
801
+ }
802
+ }
803
+
804
+ async logout ( token :string ) : Promise < any > {
805
+ const { isValidFusionAuthToken, claims} = await this . verifyFusionAuthJWT ( token ) ;
806
+ if ( isValidFusionAuthToken ) {
807
+ let existingUserJWTS :any = JSON . parse ( await this . redis . get ( claims . sub ) ) ;
808
+ if ( existingUserJWTS . indexOf ( token ) != - 1 ) {
809
+ existingUserJWTS . splice ( existingUserJWTS . indexOf ( token ) , 1 ) ;
810
+ await this . redis . set ( claims . sub , JSON . stringify ( existingUserJWTS ) ) ;
811
+ }
812
+ return {
813
+ "message" : "Logout successful. Token invalidated."
814
+ }
815
+ } else {
816
+ return {
817
+ "message" : "Invalid or expired token."
818
+ }
819
+ }
820
+ }
821
+
709
822
}
0 commit comments