backend-manager 4.1.3 → 4.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json
CHANGED
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
BackendAssistant.prototype.authenticate = async function (options) {
|
|
2
|
+
const self = this;
|
|
3
|
+
|
|
4
|
+
// Shortcuts
|
|
5
|
+
let admin = self.ref.admin;
|
|
6
|
+
let functions = self.ref.functions;
|
|
7
|
+
let req = self.ref.req;
|
|
8
|
+
let res = self.ref.res;
|
|
9
|
+
let data = self.request.data;
|
|
10
|
+
let idToken;
|
|
11
|
+
|
|
12
|
+
options = options || {};
|
|
13
|
+
options.resolve = typeof options.resolve === 'undefined' ? true : options.resolve;
|
|
14
|
+
|
|
15
|
+
function _resolve(user) {
|
|
16
|
+
user = user || {};
|
|
17
|
+
user.authenticated = typeof user.authenticated === 'undefined'
|
|
18
|
+
? false
|
|
19
|
+
: user.authenticated;
|
|
20
|
+
|
|
21
|
+
if (options.resolve) {
|
|
22
|
+
self.request.user = self.resolveAccount(user);
|
|
23
|
+
return self.request.user;
|
|
24
|
+
} else {
|
|
25
|
+
return user;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if (req?.headers?.authorization && req?.headers?.authorization?.startsWith('Bearer ')) {
|
|
30
|
+
// Read the ID Token from the Authorization header.
|
|
31
|
+
idToken = req.headers.authorization.split('Bearer ')[1];
|
|
32
|
+
self.log('Found "Authorization" header', idToken);
|
|
33
|
+
} else if (req?.cookies?.__session) {
|
|
34
|
+
// Read the ID Token from cookie.
|
|
35
|
+
idToken = req.cookies.__session;
|
|
36
|
+
self.log('Found "__session" cookie', idToken);
|
|
37
|
+
} else if (data.backendManagerKey || data.authenticationToken) {
|
|
38
|
+
// Check with custom BEM Token
|
|
39
|
+
let storedApiKey;
|
|
40
|
+
try {
|
|
41
|
+
// Disabled this 5/11/24 because i dont know why we would need to do functions.config() if we already have the Manager
|
|
42
|
+
// const workingConfig = self.Manager?.config || functions.config();
|
|
43
|
+
storedApiKey = self.Manager?.config?.backend_manager?.key || '';
|
|
44
|
+
} catch (e) {
|
|
45
|
+
// Do nothing
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Set idToken as working token of either backendManagerKey or authenticationToken
|
|
49
|
+
idToken = data.backendManagerKey || data.authenticationToken;
|
|
50
|
+
|
|
51
|
+
// Log the token
|
|
52
|
+
self.log('Found "backendManagerKey" or "authenticationToken" parameter', {storedApiKey: storedApiKey, idToken: idToken});
|
|
53
|
+
|
|
54
|
+
// Check if the token is correct
|
|
55
|
+
if (storedApiKey && (storedApiKey === data.backendManagerKey || storedApiKey === data.authenticationToken)) {
|
|
56
|
+
self.request.user.authenticated = true;
|
|
57
|
+
self.request.user.roles.admin = true;
|
|
58
|
+
return _resolve(self.request.user);
|
|
59
|
+
}
|
|
60
|
+
} else if (options.apiKey || data.apiKey) {
|
|
61
|
+
const apiKey = options.apiKey || data.apiKey;
|
|
62
|
+
self.log('Found "options.apiKey"', apiKey);
|
|
63
|
+
|
|
64
|
+
if (apiKey.includes('test')) {
|
|
65
|
+
return _resolve(self.request.user);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
await admin.firestore().collection(`users`)
|
|
69
|
+
.where('api.privateKey', '==', apiKey)
|
|
70
|
+
.get()
|
|
71
|
+
.then(function(querySnapshot) {
|
|
72
|
+
querySnapshot.forEach(function(doc) {
|
|
73
|
+
self.request.user = doc.data();
|
|
74
|
+
self.request.user.authenticated = true;
|
|
75
|
+
});
|
|
76
|
+
})
|
|
77
|
+
.catch(function(error) {
|
|
78
|
+
console.error('Error getting documents: ', error);
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
return _resolve(self.request.user);
|
|
82
|
+
} else {
|
|
83
|
+
// self.log('No Firebase ID token was able to be extracted.',
|
|
84
|
+
// 'Make sure you authenticate your request by providing either the following HTTP header:',
|
|
85
|
+
// 'Authorization: Bearer <Firebase ID Token>',
|
|
86
|
+
// 'or by passing a "__session" cookie',
|
|
87
|
+
// 'or by passing backendManagerKey or authenticationToken in the body or query');
|
|
88
|
+
|
|
89
|
+
return _resolve(self.request.user);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Check with firebase
|
|
93
|
+
try {
|
|
94
|
+
const decodedIdToken = await admin.auth().verifyIdToken(idToken);
|
|
95
|
+
if (options.debug) {
|
|
96
|
+
self.log('Token correctly decoded', decodedIdToken.email, decodedIdToken.user_id);
|
|
97
|
+
}
|
|
98
|
+
await admin.firestore().doc(`users/${decodedIdToken.user_id}`)
|
|
99
|
+
.get()
|
|
100
|
+
.then(async function (doc) {
|
|
101
|
+
if (doc.exists) {
|
|
102
|
+
self.request.user = Object.assign({}, self.request.user, doc.data());
|
|
103
|
+
}
|
|
104
|
+
self.request.user.authenticated = true;
|
|
105
|
+
self.request.user.auth.uid = decodedIdToken.user_id;
|
|
106
|
+
self.request.user.auth.email = decodedIdToken.email;
|
|
107
|
+
if (options.debug) {
|
|
108
|
+
self.log('Found user doc', self.request.user)
|
|
109
|
+
}
|
|
110
|
+
})
|
|
111
|
+
return _resolve(self.request.user);
|
|
112
|
+
} catch (error) {
|
|
113
|
+
self.error('Error while verifying Firebase ID token:', error);
|
|
114
|
+
return _resolve(self.request.user);
|
|
115
|
+
}
|
|
116
|
+
};
|
|
@@ -69,7 +69,7 @@ function tryUrl(self) {
|
|
|
69
69
|
: `${protocol}://${host}/${self.meta.name}`;
|
|
70
70
|
}
|
|
71
71
|
} else if (projectType === 'custom') {
|
|
72
|
-
return
|
|
72
|
+
return `@TODO`;
|
|
73
73
|
}
|
|
74
74
|
|
|
75
75
|
return '';
|
|
@@ -598,22 +598,44 @@ function _attachHeaderProperties(self, options, error) {
|
|
|
598
598
|
BackendAssistant.prototype.authenticate = async function (options) {
|
|
599
599
|
const self = this;
|
|
600
600
|
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
601
|
+
// Shortcuts
|
|
602
|
+
const admin = self.ref.admin;
|
|
603
|
+
const functions = self.ref.functions;
|
|
604
|
+
const req = self.ref.req;
|
|
605
|
+
const res = self.ref.res;
|
|
606
|
+
const data = self.request.data;
|
|
607
|
+
|
|
608
|
+
// Get stored backendManagerKey
|
|
609
|
+
const BACKEND_MANAGER_KEY = self.Manager?.config?.backend_manager?.key || '';
|
|
610
|
+
|
|
611
|
+
// Build the ID token from the request
|
|
606
612
|
let idToken;
|
|
613
|
+
let backendManagerKey;
|
|
614
|
+
// let user;
|
|
607
615
|
|
|
616
|
+
// Set options
|
|
608
617
|
options = options || {};
|
|
609
618
|
options.resolve = typeof options.resolve === 'undefined' ? true : options.resolve;
|
|
619
|
+
options.debug = typeof options.debug === 'undefined' ? false : options.debug;
|
|
610
620
|
|
|
611
621
|
function _resolve(user) {
|
|
622
|
+
// Resolve the properties
|
|
612
623
|
user = user || {};
|
|
613
624
|
user.authenticated = typeof user.authenticated === 'undefined'
|
|
614
625
|
? false
|
|
615
626
|
: user.authenticated;
|
|
616
627
|
|
|
628
|
+
// Validate BACKEND_MANAGER_KEY
|
|
629
|
+
if (backendManagerKey && backendManagerKey === BACKEND_MANAGER_KEY) {
|
|
630
|
+
// Update roles
|
|
631
|
+
user.roles = user.roles || {};
|
|
632
|
+
user.roles.admin = true;
|
|
633
|
+
|
|
634
|
+
// Set authenticated
|
|
635
|
+
user.authenticated = true;
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
// Resolve the user
|
|
617
639
|
if (options.resolve) {
|
|
618
640
|
self.request.user = self.resolveAccount(user);
|
|
619
641
|
return self.request.user;
|
|
@@ -622,91 +644,102 @@ BackendAssistant.prototype.authenticate = async function (options) {
|
|
|
622
644
|
}
|
|
623
645
|
}
|
|
624
646
|
|
|
625
|
-
|
|
647
|
+
// Get shortcuts
|
|
648
|
+
const authHeader = req?.headers?.authorization || '';
|
|
649
|
+
|
|
650
|
+
// Extract the BEM token
|
|
651
|
+
// Having this is separate from the ID token allows for the user to be authenticated as an ADMIN
|
|
652
|
+
if (options.backendManagerKey || data.backendManagerKey) {
|
|
653
|
+
// Read token from backendManagerKey or authenticationToken or apiKey
|
|
654
|
+
backendManagerKey = options.backendManagerKey || data.backendManagerKey;
|
|
655
|
+
|
|
656
|
+
// Log the token
|
|
657
|
+
self.log('Found "backendManagerKey" parameter', backendManagerKey);
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
// Extract the token / API key
|
|
661
|
+
// This is the main token that will be used to authenticate the user (it can be a JWT or a user's API key)
|
|
662
|
+
if (authHeader.startsWith('Bearer ')) {
|
|
626
663
|
// Read the ID Token from the Authorization header.
|
|
627
|
-
idToken =
|
|
664
|
+
idToken = authHeader.split('Bearer ')[1];
|
|
665
|
+
|
|
666
|
+
// Log the token
|
|
628
667
|
self.log('Found "Authorization" header', idToken);
|
|
629
668
|
} else if (req?.cookies?.__session) {
|
|
630
669
|
// Read the ID Token from cookie.
|
|
631
670
|
idToken = req.cookies.__session;
|
|
671
|
+
|
|
672
|
+
// Log the token
|
|
632
673
|
self.log('Found "__session" cookie', idToken);
|
|
633
|
-
} else if (
|
|
634
|
-
|
|
635
|
-
|
|
674
|
+
} else if (
|
|
675
|
+
options.authenticationToken || data.authenticationToken
|
|
676
|
+
|| options.apiKey || data.apiKey
|
|
677
|
+
) {
|
|
678
|
+
// Read token OR API Key from options or data
|
|
679
|
+
idToken = options.authenticationToken || data.authenticationToken
|
|
680
|
+
|| options.apiKey || data.apiKey;
|
|
681
|
+
|
|
682
|
+
// Log the token
|
|
683
|
+
self.log('Found "authenticationToken" parameter', idToken);
|
|
684
|
+
} else {
|
|
685
|
+
// No token found
|
|
686
|
+
return _resolve(self.request.user);
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
// Check if the token is a JWT
|
|
690
|
+
if (isJWT(idToken)) {
|
|
691
|
+
// Check with firebase
|
|
636
692
|
try {
|
|
637
|
-
|
|
638
|
-
// const workingConfig = self.Manager?.config || functions.config();
|
|
639
|
-
storedApiKey = self.Manager?.config?.backend_manager?.key || '';
|
|
640
|
-
} catch (e) {
|
|
641
|
-
// Do nothing
|
|
642
|
-
}
|
|
693
|
+
const decodedIdToken = await admin.auth().verifyIdToken(idToken);
|
|
643
694
|
|
|
644
|
-
|
|
645
|
-
|
|
695
|
+
// Log the token
|
|
696
|
+
if (options.debug) {
|
|
697
|
+
self.log('JWT token decoded', decodedIdToken.email, decodedIdToken.user_id);
|
|
698
|
+
}
|
|
646
699
|
|
|
647
|
-
|
|
648
|
-
|
|
700
|
+
// Get the user
|
|
701
|
+
await admin.firestore().doc(`users/${decodedIdToken.user_id}`)
|
|
702
|
+
.get()
|
|
703
|
+
.then((doc) => {
|
|
704
|
+
// Set the user
|
|
705
|
+
if (doc.exists) {
|
|
706
|
+
self.request.user = Object.assign({}, self.request.user, doc.data());
|
|
707
|
+
self.request.user.authenticated = true;
|
|
708
|
+
self.request.user.auth.uid = decodedIdToken.user_id;
|
|
709
|
+
self.request.user.auth.email = decodedIdToken.email;
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
// Log the user
|
|
713
|
+
if (options.debug) {
|
|
714
|
+
self.log('Found user doc', self.request.user)
|
|
715
|
+
}
|
|
716
|
+
})
|
|
649
717
|
|
|
650
|
-
|
|
651
|
-
if (storedApiKey && (storedApiKey === data.backendManagerKey || storedApiKey === data.authenticationToken)) {
|
|
652
|
-
self.request.user.authenticated = true;
|
|
653
|
-
self.request.user.roles.admin = true;
|
|
718
|
+
// Return the user
|
|
654
719
|
return _resolve(self.request.user);
|
|
655
|
-
}
|
|
656
|
-
|
|
657
|
-
const apiKey = options.apiKey || data.apiKey;
|
|
658
|
-
self.log('Found "options.apiKey"', apiKey);
|
|
720
|
+
} catch (error) {
|
|
721
|
+
self.error('Error while verifying JWT:', error);
|
|
659
722
|
|
|
660
|
-
|
|
723
|
+
// Return the user
|
|
661
724
|
return _resolve(self.request.user);
|
|
662
725
|
}
|
|
663
|
-
|
|
726
|
+
} else {
|
|
727
|
+
// Query by API key
|
|
664
728
|
await admin.firestore().collection(`users`)
|
|
665
|
-
.where('api.privateKey', '==',
|
|
729
|
+
.where('api.privateKey', '==', idToken)
|
|
666
730
|
.get()
|
|
667
|
-
.then(
|
|
668
|
-
querySnapshot.forEach(
|
|
731
|
+
.then((querySnapshot) => {
|
|
732
|
+
querySnapshot.forEach((doc) => {
|
|
669
733
|
self.request.user = doc.data();
|
|
734
|
+
self.request.user = Object.assign({}, self.request.user, doc.data());
|
|
670
735
|
self.request.user.authenticated = true;
|
|
671
736
|
});
|
|
672
737
|
})
|
|
673
|
-
.catch(
|
|
738
|
+
.catch((error) => {
|
|
674
739
|
console.error('Error getting documents: ', error);
|
|
675
740
|
});
|
|
676
741
|
|
|
677
|
-
|
|
678
|
-
} else {
|
|
679
|
-
// self.log('No Firebase ID token was able to be extracted.',
|
|
680
|
-
// 'Make sure you authenticate your request by providing either the following HTTP header:',
|
|
681
|
-
// 'Authorization: Bearer <Firebase ID Token>',
|
|
682
|
-
// 'or by passing a "__session" cookie',
|
|
683
|
-
// 'or by passing backendManagerKey or authenticationToken in the body or query');
|
|
684
|
-
|
|
685
|
-
return _resolve(self.request.user);
|
|
686
|
-
}
|
|
687
|
-
|
|
688
|
-
// Check with firebase
|
|
689
|
-
try {
|
|
690
|
-
const decodedIdToken = await admin.auth().verifyIdToken(idToken);
|
|
691
|
-
if (options.debug) {
|
|
692
|
-
self.log('Token correctly decoded', decodedIdToken.email, decodedIdToken.user_id);
|
|
693
|
-
}
|
|
694
|
-
await admin.firestore().doc(`users/${decodedIdToken.user_id}`)
|
|
695
|
-
.get()
|
|
696
|
-
.then(async function (doc) {
|
|
697
|
-
if (doc.exists) {
|
|
698
|
-
self.request.user = Object.assign({}, self.request.user, doc.data());
|
|
699
|
-
}
|
|
700
|
-
self.request.user.authenticated = true;
|
|
701
|
-
self.request.user.auth.uid = decodedIdToken.user_id;
|
|
702
|
-
self.request.user.auth.email = decodedIdToken.email;
|
|
703
|
-
if (options.debug) {
|
|
704
|
-
self.log('Found user doc', self.request.user)
|
|
705
|
-
}
|
|
706
|
-
})
|
|
707
|
-
return _resolve(self.request.user);
|
|
708
|
-
} catch (error) {
|
|
709
|
-
self.error('Error while verifying Firebase ID token:', error);
|
|
742
|
+
// Return the user
|
|
710
743
|
return _resolve(self.request.user);
|
|
711
744
|
}
|
|
712
745
|
};
|
|
@@ -726,7 +759,7 @@ BackendAssistant.prototype.parseRepo = function (repo) {
|
|
|
726
759
|
}
|
|
727
760
|
|
|
728
761
|
// Remove unnecessary parts
|
|
729
|
-
repoSplit = repoSplit.filter(
|
|
762
|
+
repoSplit = repoSplit.filter((value, index, arr) => {
|
|
730
763
|
return value !== 'http:'
|
|
731
764
|
&& value !== 'https:'
|
|
732
765
|
&& value !== ''
|
|
@@ -1034,6 +1067,21 @@ BackendAssistant.prototype.parseMultipartFormData = function (options) {
|
|
|
1034
1067
|
});
|
|
1035
1068
|
}
|
|
1036
1069
|
|
|
1070
|
+
const isJWT = (token) => {
|
|
1071
|
+
// Ensure the token has three parts separated by dots
|
|
1072
|
+
const parts = token.split('.');
|
|
1073
|
+
|
|
1074
|
+
try {
|
|
1075
|
+
// Decode the header (first part) to verify it is JSON
|
|
1076
|
+
const header = JSON.parse(Buffer.from(parts[0], 'base64').toString('utf8'));
|
|
1077
|
+
// Check for expected JWT keys in the header
|
|
1078
|
+
return header.alg && header.typ === 'JWT';
|
|
1079
|
+
} catch (err) {
|
|
1080
|
+
// If parsing fails, it's not a valid JWT
|
|
1081
|
+
return false;
|
|
1082
|
+
}
|
|
1083
|
+
};
|
|
1084
|
+
|
|
1037
1085
|
// Not sure what this is for? But it has a good serializer code
|
|
1038
1086
|
// Disabled 2024-03-21 because there was another stringify() function that i was intending to use but it was actually using this
|
|
1039
1087
|
// It was adding escaped quotes to strings
|