1
1
const JWT = require ( 'jsonwebtoken' ) ;
2
2
const ethers = require ( 'ethers' ) ;
3
- const { organizationAbi } = require ( './contracts/organization' ) ;
3
+ const KeyEncoder = require ( 'key-encoder' ) . default ;
4
+ const { ec, eddsa } = require ( 'elliptic' ) ;
5
+ const Web3 = require ( 'web3' ) ;
6
+ const { OrgIdResolver, httpFetchMethod } = require ( '@windingtree/org.id-resolver' ) ;
7
+ const { addresses } = require ( '@windingtree/org.id' ) ;
8
+ const web3 = new Web3 ( process . env . INFURA_ENDPOINT ) ;
4
9
5
- const validisTyp = [ "jwt" , 'application/jwt' ] ;
6
- const verifyJWT = ( type , jwt ) => {
7
- if ( type !== 'Bearer' ) throw new Error ( 'JWT Token format is not valid' ) ;
8
-
9
- const decodedToken = JWT . decode ( jwt , { complete : true } ) ;
10
- if ( ! decodedToken ) throw new Error ( 'JWT Token format is not valid' ) ;
11
- const { header, payload, signature} = decodedToken ;
12
-
13
- if ( ! validisTyp . includes ( header . typ . toLowerCase ( ) ) ) throw new Error ( 'JWT Token header typ invalid' ) ;
14
- if ( header . alg !== 'ETH' ) throw new Error ( 'JWT Token algorithm must be ETH' ) ;
15
-
16
- if ( payload . exp < Date . now ( ) / 1000 ) throw new Error ( 'JWT Token has Expired' ) ;
10
+ // ORG.ID resolver configuration
11
+ const orgIdResolver = new OrgIdResolver ( {
12
+ web3,
13
+ orgId : addresses . ropsten // @todo Set the network type on the base of environment config
14
+ } ) ;
15
+ orgIdResolver . registerFetchMethod ( httpFetchMethod ) ;
17
16
18
- const lastPeriod = jwt . lastIndexOf ( '.' ) ;
17
+ const validisTyp = [ 'jwt' , 'application/jwt' ] ;
18
+ const validAlg = [ 'ETH' , 'ES256K' ] ;
19
19
20
- const signedMessage = jwt . substring ( 0 , lastPeriod ) ;
21
- const sigatureB64 = jwt . substring ( lastPeriod + 1 ) ;
20
+ const verifyJWT = async ( type , jwt ) => {
22
21
23
- const signatureB16 = ( Buffer . from ( sigatureB64 . toString ( ) . replace ( '-' , '+' ) . replace ( '_' , '/' ) , 'base64' ) ) . toString ( 'hex' ) ;
24
- const hashedMessage = ethers . utils . hashMessage ( signedMessage ) ;
25
- const signingAddress = ethers . utils . recoverAddress ( hashedMessage , `0x ${ signatureB16 } ` ) ;
22
+ if ( type !== 'Bearer' ) {
23
+ throw new Error ( 'JWT Token format is not valid' ) ;
24
+ } ;
26
25
27
- return { header, payload, signature, signingAddress } ;
28
- } ;
26
+ const decodedToken = JWT . decode ( jwt , {
27
+ complete : true
28
+ } ) ;
29
29
30
- const isAuthorized = async ( contractAddress , signer ) => {
31
- const provider = ethers . getDefaultProvider ( 'ropsten' ) ;
32
- const contract = new ethers . Contract ( contractAddress , organizationAbi , provider ) ;
33
-
34
- const owner = await contract . owner ( ) ;
35
- if ( owner === signer ) return ;
30
+ if ( ! decodedToken ) {
31
+ throw new Error ( 'JWT Token format is not valid' ) ;
32
+ } ;
33
+
34
+ const {
35
+ header,
36
+ payload,
37
+ signature
38
+ } = decodedToken ;
39
+
40
+ if ( ! validisTyp . includes ( header . typ . toLowerCase ( ) ) ) {
41
+ throw new Error ( 'JWT Token header typ invalid' ) ;
42
+ }
43
+
44
+ if ( ! validAlg . includes ( header . alg ) ) {
45
+ throw new Error ( 'JWT Token algorithm is invalid' ) ;
46
+ }
47
+
48
+ if ( payload . exp < Date . now ( ) / 1000 ) {
49
+ throw new Error ( 'JWT Token has Expired' ) ;
50
+ }
51
+
52
+ const [ did , fragment ] = payload . iss . split ( '#' ) ;
53
+ const didResult = await orgIdResolver . resolve ( did ) ;
54
+
55
+ console . log ( '>>>' , didResult ) ;
56
+
57
+ const lastPeriod = jwt . lastIndexOf ( '.' ) ;
58
+ const signedMessage = jwt . substring ( 0 , lastPeriod ) ;
59
+ const signatureB16 = ( Buffer . from (
60
+ signature
61
+ . toString ( )
62
+ . replace ( '-' , '+' )
63
+ . replace ( '_' , '/' ) ,
64
+ 'base64' ) ) . toString ( 'hex' ) ;
36
65
37
- const isAssociadtedKey = await contract . hasAssociatedKey ( signer ) ;
38
- if ( ! isAssociadtedKey ) throw new Error ( 'JWT Token not authorized' ) ;
66
+ if ( header . alg === 'ETH' ) {
67
+ // Validate signature of the organization owner or director
68
+ const hashedMessage = ethers . utils . hashMessage ( signedMessage ) ;
69
+ const signingAddress = ethers . utils . recoverAddress ( hashedMessage , `0x${ signatureB16 } ` ) ;
70
+
71
+ if ( ! [
72
+ didResult . organization . owner ,
73
+ ...didResult . organization . director === '0x0000000000000000000000000000000000000000'
74
+ ? [ ]
75
+ : [ didResult . organization . director ]
76
+ ] . includes ( signingAddress ) ) {
77
+ throw new Error ( 'JWT Token not authorized' ) ;
78
+ }
79
+
80
+ } else if ( fragment && didResult . didDocument . publicKey ) {
81
+ // Validate signature using publickKey
82
+ let publicKey = didResult . didDocument . publicKey . filter (
83
+ p => p . id . match ( RegExp ( `#${ fragment } $` , 'g' ) )
84
+ ) [ 0 ] ;
85
+
86
+ if ( ! publicKey ) {
87
+ throw new Error ( 'Public key definition not found in the DID document' ) ;
88
+ }
89
+
90
+ let curveType ;
91
+
92
+ switch ( publicKey . type ) {
93
+ case 'X25519' :
94
+ curveType = 'ed25519' ;
95
+ break ;
96
+
97
+ case 'secp256k1' :
98
+ curveType = 'secp256k1' ;
99
+ break ;
100
+
101
+ default :
102
+ throw new Error ( 'Signature verification method not found' ) ;
103
+ }
104
+
105
+ const context = new ec ( curveType ) ;
106
+ const keyEncoder = new KeyEncoder ( curveType ) ;
107
+
108
+ if ( ! publicKey . publicKeyPem . match ( RegExp ( 'BEGIN PUBLIC KEY' , 'gi' ) ) ) {
109
+ publicKey . publicKeyPem = `-----BEGIN PUBLIC KEY-----\n${ publicKey . publicKeyPem } \n-----END PUBLIC KEY-----` ;
110
+ }
111
+
112
+ const rawPub = keyEncoder . encodePublic ( publicKey . publicKeyPem , 'pem' , 'raw' ) ;
113
+ const key = context . keyFromPublic ( rawPub , 'hex' ) ;
114
+ const sigParts = signatureB16 . match ( / ( [ a - f \d ] { 64 } ) / gi) ;
115
+ const sig = {
116
+ r : sigParts [ 0 ] ,
117
+ s : sigParts [ 1 ]
118
+ } ;
119
+
120
+ if ( ! key . verify ( signedMessage , sig ) ) {
121
+ throw new Error ( 'JWT Token not authorized' ) ;
122
+ }
123
+
124
+ } else {
125
+ throw new Error ( 'Signature verification method not found' ) ;
126
+ }
127
+
128
+ return {
129
+ header,
130
+ payload,
131
+ signature
132
+ } ;
39
133
} ;
40
134
41
135
module . exports = {
42
- verifyJWT,
43
- isAuthorized,
44
- } ;
136
+ verifyJWT
137
+ } ;
0 commit comments