diff --git a/configuration.json b/configuration.json index fa20531..9a76cbc 100644 --- a/configuration.json +++ b/configuration.json @@ -1,26 +1,23 @@ { "nodeURL": "http://63.33.206.111/rpc", "addressPassword": "Passw0rd", - "rawPublicKeySubject": "03fdd57adec3d438ea237fe46b33ee1e016eda6b585c3e27ea66686c2ea5358479", - "rawPublicKeyReceiver": "8b82bb2b1b5b4c1d56beeb88c98fcf894c23e8dee598d94c1c77099d3a80367f46", - "firstIdentityPubk": "0x0ce5edc204ae977d6d69fcc6b339a57b544ef4d094929f3eb918a77dfba26998e77ec5b29a379dd59bbe962e2b22745143aa2b52c4a875b7492c137eeebf4055", + "firstIdentityPubk": "0xaca7aa42e831f41b6f246d089fbcabe73e1c6ce01a1bb5d944fda4e148e1d6930f6ebb5138b1d103e3e384c893962a238e4088a549bfe84867da96942931f69a", "entity1Pubk": "0x356e3fce435d8729062e52d263c0c705b3c5e201a9a9608cdb070764e6b8df30ae8423b439a7af2bcc3529778341ab06c1e44411352f217b68ce44a673a1df63", "entity2Pubk": "0xa33e56a80b9dc83a4456265d877c0765cea76146e625572fc679804f8867222ca3c816433a9b6e6690b0b8e919ffa874982706e812314aae09d85fc62fc4fa3c", "entity3Pubk": "0xf6aa52924a7280241bd84d098d7c03b4e3a7e08f206b68868f439b65a6c3b26b1bd30b960325be8670339a66258b851ae170691429248149b601da5798f42d28", "subject1Pubk": "0x2e507af01167c98a6accc0cd46ab2a256dd6b6c69ec1c0c28f80fb62e1f7d70233768b0c58dbbdac1fc358b8141c075a520483cf9779e4ea98d13df2833f3767", - "subject2Pubk": "0xaca7aa42e831f41b6f246d089fbcabe73e1c6ce01a1bb5d944fda4e148e1d6930f6ebb5138b1d103e3e384c893962a238e4088a549bfe84867da96942931f69a", - "rawPrivateKey": "278a5de700e29faae8e40e366ec5012b5ec63d36ec77e8a2417154cc1d25383f", + "subject2Pubk": "0x028c2b8f606bc8b9803f0ccb753bf4ef4372e2ddfa69d875e2bb6cad68fd7b3bb2f42231714c21f1963b3c6282d85619cd52c470cc743277de3f438dec53732e", "context": [ - "https://w3id.org/did/v1", - "JWT" + "https://www.w3.org/2018/credentials/v1", + "https://w3id.org/did/v1" ], "type": [ "CustomType" ], "signedTxCreateAlastriaID": "0xf901888203f180830927c094bd4a2c84edb97be5beff7cd341bd63567e73f8c980b901246d69d99a000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000c450382c1a000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000803265353037616630313136376339386136616363633063643436616232613235366464366236633639656331633063323866383066623632653166376437303233333736386230633538646262646163316663333538623831343163303735613532303438336366393737396534656139386431336466323833336633373637000000000000000000000000000000000000000000000000000000001ca0e3e05abb1592bd15dc72e57d8930c24efdd0e286437a34ca2f118372ad428b25a0480ac816c93a5c1e8eff1dfd96f850dc664c5daa1ff94a88bdf789b0652ebfd3", - "userPublicKey": "AE2309349218937HASKHIUE9287432", - "providerURL": "https://regular.telsius.blockchainbyeveris.io:2000", - "callbackURL": "https://serviceprovider.alastria.blockchainbyeveris.io/api/login/", + "providerURL": "http://63.33.206.111/rpc", + "callbackURL": "https://entity.alastria/api/v1/login", + "mfau": "http://url/mfa_server", "alastriaNetId": "Alastria network", "tokenExpTime": 1563783392, "tokenActivationDate": 1563782792, @@ -28,56 +25,40 @@ "jsonTokenId": "ze298y42sba", "network": "quor", "networkId": "redT", - "tokenPayload": { - "header": { - "alg": "ES256K", - "typ": "JWT", - "kid": "did:ala:quor:redt:0x12eeaCCA9eEbB78eB97d7cac6b#keys-1", - "jwk": "0x12345" - }, - "payload": { - "iss": "did:ala:quor:redT:e53d78c1c6fc694a0f29b3f24bee439338acbe3e", - "gwu": "http://1.2.3.4:8097/alastria/presentation", - "cbu": "http://1.2.3.4:8097/alastria/presentation", - "iat": 1590569132, - "ani": "redT", - "exp": 1563783392 - } - }, - "jti": "https://www.empresa.com/alastria/credentials/3734", + "jti": "https://www.entity.com/alastria/credentials/3734", "kidCredential": "did:ala:quor:redt:12eeaCCA9eEbB78eB97d7cac6b#keys-1", "subjectAlastriaID": "did:ala:quor:redt:0x12eeaCCA9eEbB78eB97d7cac6b", "credentialKey": "StudentID", "credentialValue": "11235813", - "credentialKeyFather":"Driving licence", + "credentialKeyFather": "Driving license", "credentialValueFather": {}, "credentialSubKey1": "Driver`s Name", "credentialSubKey2": "Driver`s Surname", - "credentialSubKey3": "Type of licence", - "credentialSubKey4": "Years with the licence", + "credentialSubKey3": "Type of license", + "credentialSubKey4": "Years with the license", "credentialSubValue1": "PersonName", "credentialSubValue2": "PersonSurname", "credentialSubValue3": "B", "credentialSubValue4": "20 years", "uri": "www.google.com", - "procUrl": "https://www.empresa.com/alastria/businessprocess/4583", - "procHash": "H398sjHd...kldjUYn475n", + "procUrl": "https://www.entity.com/alastria/businessprocess/4583", + "procHash": "dccf25f46542dfb668b931e7b7cef29731a1754f659c393eb801ca851bd15ffc", "data": [ { - "@context": "JWT", + "@context": "https://www.w3.org/2018/identity/driving-license/v1", "levelOfAssurance": 3, "required": true, - "field_name": "name" + "field_name": "driving_license" }, { - "@context": "JWT", - "levelOfAssurance": 3, + "@context": "https://www.w3.org/2018/identity/email/v1", + "levelOfAssurance": 0, "required": true, "field_name": "email" } ], - "entity1": "0xd1f4cfd94a0b3d92f737f9a16685fa69ed2c9a8f", - "didEntity1": "did:ala:quor:redT:d1f4cfd94a0b3d92f737f9a16685fa69ed2c9a8f", + "entity1": "0xdb040fd6acca0e742898484bd2738b38a8c57aa8", + "didEntity1": "did:ala:quor:redT:db040fd6acca0e742898484bd2738b38a8c57aa8", "entity2": "0x82f83d802f235955e9543d3f8e485ccd06436a89", "didEntity2": "did:ala:quor:redT:82f83d802f235955e9543d3f8e485ccd06436a89", "entity3": "0x220ea50a0315eb025ebfb8c309d515ea5a4ac3c4", diff --git a/exampleAuthentication/1.authentication.js b/exampleAuthentication/1.authentication.js index d605a87..53ca303 100644 --- a/exampleAuthentication/1.authentication.js +++ b/exampleAuthentication/1.authentication.js @@ -2,22 +2,16 @@ const { tokensFactory } = require('alastria-identity-lib') const fs = require('fs') const keythereum = require('keythereum') +//Preparing to read configuration.json const rawdata = fs.readFileSync('../configuration.json') const configData = JSON.parse(rawdata) +//Preparing entity1 and subject1 keystore (privateKey) const keyDataEntity1 = fs.readFileSync( '../keystores/entity1-a9728125c573924b2b1ad6a8a8cd9bf6858ced49.json' ) const keystoreDataEntity1 = JSON.parse(keyDataEntity1) -const keyDataSubject1 = fs.readFileSync( - '../keystores/subject1-806bc0d7a47b890383a831634bcb92dd4030b092.json' -) -const keystoreDataSubject1 = JSON.parse(keyDataSubject1) - -// Init your blockchain provider - const entity1KeyStore = keystoreDataEntity1 - let entity1PrivateKey try { entity1PrivateKey = keythereum.recover( @@ -29,8 +23,11 @@ try { process.exit(1) } +const keyDataSubject1 = fs.readFileSync( + '../keystores/subject1-806bc0d7a47b890383a831634bcb92dd4030b092.json' +) +const keystoreDataSubject1 = JSON.parse(keyDataSubject1) const subject1Keystore = keystoreDataSubject1 - let subject1PrivateKey try { subject1PrivateKey = keythereum.recover( @@ -42,51 +39,101 @@ try { process.exit(1) } -console.log('\n ------ Example of Authentication ------ \n') +// *********************************************************************************************************************** +// Starting reading/calculating DATA declared in configuration.json used to create the Alastria Token and Alastria Session +const randomCharacters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" +//Alastria Token info +const issAT = configData.didEntity1 +const gwu = configData.providerURL +const cbu = configData.callbackURL +const ani = configData.networkId +const exp = Math.round(Date.now() / 1000) + 600 // 10 min = 600 seconds +const nbf = Math.round(Date.now() / 1000) - 600 // 10 min before +const kidAT = issAT + "#keys-1" //header.KID +const jwkAT = configData.entity1Pubk //header.JWK +let jtiAT = "" +const jtiVariableLength = 20 //length of the variable part of the jti + +//Generating a random JTI to AT +for (let i = 0; i < jtiVariableLength; i++) { + jtiAT += randomCharacters.charAt(Math.floor(Math.random() * randomCharacters.length)); +} +jtiAT = "nameEntity/alastria/alastria-token/" + jtiAT + +//Alastria Session info +const context = configData.context +const issAS = configData.didSubject1 +const kidAS = issAS + "#keys-1" +const type = ["US211"] //other info setted in the library +const jwkAS = configData.subject1Pubk +let jtiAS = "" + +//Generating a random JTI to AS +for (let i = 0; i < jtiVariableLength; i++) { + jtiAS += randomCharacters.charAt(Math.floor(Math.random() * randomCharacters.length)); +} +jtiAS = "nameEntity/alastria/alastria-session/" + jtiAS +// Ending DATA reading/calculating +// *********************************************************************************************************************** + +console.log('\t ------ Example of Authentication ------ \n') + +//1 - First the entity creates Alastria Token artifact +console.log('\t 1 - Creating Alastria Token (AT)\n') const alastriaToken = tokensFactory.tokens.createAlastriaToken( - configData.didEntity1, - configData.providerURL, - configData.callbackURL, - configData.networkId, - configData.tokenExpTime, - configData.kidCredential, - configData.entity1Pubk, - configData.tokenActivationDate, - configData.jsonTokenId + issAT, + gwu, + cbu, + ani, + exp, + kidAT, + jwkAT, + nbf, + jtiAT ) -console.log('\tThe Alastria token is: \n', alastriaToken) +console.log('\nThe Alastria token is: \n', alastriaToken) -// Signing the AlastriaToken +//2 - The entity sign the Alastria Token +console.log('\t 2 - Signing Alastria Token (AT)\n') const signedAT = tokensFactory.tokens.signJWT(alastriaToken, entity1PrivateKey) +console.log('\nThe Alastria token signed is: \n', signedAT) +//3 - To other communication channel (QR, Deeplink...) the entity sends to the subject AT artifact +// and the subject with the publicKey of the entity verifies it. // '04' means uncompressed key (more info at https://github.com/indutny/elliptic/issues/138) +console.log('\t 3 - Subject verifies the Alastria Token (AT)\n') const verifyAT = tokensFactory.tokens.verifyJWT( signedAT, '04' + configData.entity1Pubk.substr(2) ) -console.log('\tIs the signedJWT verified?', verifyAT) +console.log('\nIs the Alastria Token verified?', verifyAT) +//4 - Subject creates the Alastria Session to respond to the challenge set by the entity. +console.log('\t 4 - Creating Alastria Session (AS)\n') const alastriaSession = tokensFactory.tokens.createAlastriaSession( - configData.context, - configData.didSubject1, - configData.subject1Pubk, + context, + issAS, + kidAS, + type, signedAT, - configData.tokenExpTime, - configData.tokenActivationDate, - configData.jsonTokenId + exp, + jwkAS, + nbf, + jtiAS ) -console.log('\tThe Alastria session is:\n', alastriaSession) +console.log('\nThe Alastria session is:\n', alastriaSession) -const signedAS = tokensFactory.tokens.signJWT( - alastriaSession, - subject1PrivateKey -) -console.log('\tThe signedAS is:\n', signedAS) +//5 - The subject sign the Alastria Session +console.log('\t 5 - Signing Alastria Session (AS)\n') +const signedAS = tokensFactory.tokens.signJWT(alastriaSession, subject1PrivateKey) +console.log('\nThe Alastria Session signed is:\n', signedAS) +//6 - Answer to the cbu of the AT, the subject sends the AS to the entity // '04' means uncompressed key (more info at https://github.com/indutny/elliptic/issues/138) +console.log('\t 6 - Entity verifies the Alastria Session (AS)\n') const verifyAS = tokensFactory.tokens.verifyJWT( signedAS, '04' + configData.subject1Pubk.substr(2) ) -console.log('\tIs the signedJWT verified?', verifyAS) +console.log('\nIs the signedJWT verified?', verifyAS) diff --git a/exampleTokens/1.alastriaToken.js b/exampleTokens/1.alastriaToken.js new file mode 100644 index 0000000..178258e --- /dev/null +++ b/exampleTokens/1.alastriaToken.js @@ -0,0 +1,83 @@ +const { tokensFactory } = require('alastria-identity-lib') +const { tests } = require('alastria-identity-JSON-objects/tests') +const fs = require('fs') +const keythereum = require('keythereum') + +//Preparing to read configuration.json +const rawdata = fs.readFileSync('../configuration.json') +const configData = JSON.parse(rawdata) + +const rawDataSignedObjects = fs.readFileSync('./SignedObjects.json') +const configDataSignedObjects = JSON.parse(rawDataSignedObjects) + +//FirstIdentity = Entity1 +const keyDataFirstIdentity = fs.readFileSync( + '../keystores/firstIdentity-643266eb3105f4bf8b4f4fec50886e453f0da9ad.json' + ) + const keystoreDataFirstIdentity = JSON.parse(keyDataFirstIdentity) + + let firstIdentityPrivateKey + try { + firstIdentityPrivateKey = keythereum.recover(configData.addressPassword, keystoreDataFirstIdentity) + } catch (error) { + console.error('ERROR: ', error) + process.exit(1) + } + +// ************************************************************************************************** +// Starting reading/calculating DATA declared in configuration.json used to create the Alastria Token +const randomCharacters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" +const iss = configData.didEntity1 +const gwu = configData.providerURL +const cbu = configData.callbackURL +const ani = configData.networkId +const exp = Math.round(Date.now() / 1000) + 600 // 10 min = 600 seconds +const nbf = Math.round(Date.now() / 1000) - 600 // 10 min before +const kid = iss + "#keys-1" //header.KID +const jwk = configData.firstIdentityPubk //header.JWK +let jti = "" +const jtiVariableLength = 20 //length of the variable part of the jti +// IAT does not need to be passed, the library calculates it. + +//Neeed to added in the librery first to Alastria Token artifact +const type = ["US12"] // the type "AlastriaToken" is setted in the library +const context = configData.context +const mfau = configData.mfau + +//Generating a random JTI +for (let i = 0; i < jtiVariableLength; i++) { + jti += randomCharacters.charAt(Math.floor(Math.random() * randomCharacters.length)); +} +jti = "nameEntity/alastria/alastria-token/" + jti +// Ending DATA reading/calculating +// ************************************************************************************************** + +//Creating Alastria Token +console.log('\t 1 - Creating Alastria Token (AT)\n') +const alastriaToken = tokensFactory.tokens.createAlastriaToken( + iss, + gwu, + cbu, + ani, + exp, + kid, + jwk, + nbf, + jti +) +console.log('\nThe Alastria Token (AT) is: \n', alastriaToken) + +// Signing the AlastriaToken +console.log('\t 2 - Signing the Alastria Token (AT)\n') +const signedAT = tokensFactory.tokens.signJWT(alastriaToken, firstIdentityPrivateKey) +console.log('\nThe Alastria Token (AT) signed is: \n', signedAT) + +// Validating the AlastriaToken +console.log('\t 3 - Validating the Alastria Token (AT)\n') +tests.tokens.validateToken(signedAT) + +configDataSignedObjects.signedAT = signedAT +fs.writeFileSync( + './SignedObjects.json', + JSON.stringify(configDataSignedObjects, null, 4) + ) \ No newline at end of file diff --git a/exampleTokens/1.tokenFactory.js b/exampleTokens/1.tokenFactory.js deleted file mode 100644 index 4fc4e30..0000000 --- a/exampleTokens/1.tokenFactory.js +++ /dev/null @@ -1,216 +0,0 @@ -const { tokensFactory } = require('alastria-identity-lib') -const { tests } = require('alastria-identity-JSON-objects/tests') -const Web3 = require('web3') -const fs = require('fs') -const keythereum = require('keythereum') - -const rawdata = fs.readFileSync('../configuration.json') -const config = JSON.parse(rawdata) - -// Data -const tokenPayload = config.tokenPayload -// End data - -const keyDataFirstIdentity = fs.readFileSync( - '../keystores/firstIdentity-643266eb3105f4bf8b4f4fec50886e453f0da9ad.json' -) -const keystoreDataFirstIdentity = JSON.parse(keyDataFirstIdentity) - -const firstIdentityKeyStore = keystoreDataFirstIdentity - -let firstIdentityPrivateKey -try { - firstIdentityPrivateKey = keythereum.recover(config.addressPassword, firstIdentityKeyStore) -} catch (error) { - console.error('ERROR: ', error) - process.exit(1) -} - -console.log('---- signJWT ----') - -const signedJWT = tokensFactory.tokens.signJWT(tokenPayload, firstIdentityPrivateKey) -console.log('\tThe signed JWT is: ', signedJWT) -tests.tokens.validateToken(signedJWT) - -console.log('\n---- decodeJWT ----') - -const decodedJWT = tokensFactory.tokens.decodeJWT(signedJWT) -console.log('\tThe decoded token is: \n', decodedJWT) - -console.log('\n---- verifyJWT ----') - -// '04' means uncompressed key (more info at https://github.com/indutny/elliptic/issues/138) -const verifyJWT = tokensFactory.tokens.verifyJWT( - signedJWT, - '04' + config.firstIdentityPubk.substr(2) -) -console.log('\tIs the signedJWT verified?', verifyJWT) - -// Data -const context = config.context -const type = config.type -const didIsssuer = config.didEntity3 -const providerURL = config.providerURL -const callbackURL = config.callbackURL -const alastriaNetId = config.networkId -const tokenExpTime = config.tokenExpTime -const tokenActivationDate = config.tokenActivationDate -const tokenNotBefore = config.tokenNotBefore -const jsonTokenId = config.jsonTokenId -const kidCredential = config.kidCredential -// End data - -console.log('\n---- createAlastriaToken ----') - -const alastriaToken = tokensFactory.tokens.createAlastriaToken( - didIsssuer, - providerURL, - callbackURL, - alastriaNetId, - tokenExpTime, - kidCredential, - config.firstIdentityPubk, - tokenActivationDate, - jsonTokenId -) -console.log('\tThe Alastria token is: \n', alastriaToken) - -// Signing the AlastriaToken -const signedAT = tokensFactory.tokens.signJWT(alastriaToken, firstIdentityPrivateKey) -tests.tokens.validateToken(signedAT) - -console.log('\n---- createAlastriaSession ----') - -const alastriaSession = tokensFactory.tokens.createAlastriaSession( - context, - didIsssuer, - config.kidCredential, - type, - signedAT, - tokenExpTime, - config.firstIdentityPubk, - tokenActivationDate, - jsonTokenId -) -console.log('\tThe Alastria session is:\n', alastriaSession) - -// Data -const jti = config.jti -const subjectAlastriaID = config.subjectAlastriaID -const credentialSubject = {} -const credentialKey = config.credentialKey -const credentialValue = config.credentialValue -credentialSubject[credentialKey] = credentialValue -credentialSubject.levelOfAssurance = 'basic' -// End data - -console.log('\n---- createCredential ----') - -const credential1 = tokensFactory.tokens.createCredential( - config.didEntity1, - context, - credentialSubject, - kidCredential, - subjectAlastriaID, - tokenExpTime, - tokenActivationDate, - jti -) -console.log('\nThe credential1 is: ', credential1) - -console.log('\n---- PSMHash ----') - -// Init your blockchain provider -const myBlockchainServiceIp = config.nodeUrl - -const web3 = new Web3(new Web3.providers.HttpProvider(myBlockchainServiceIp)) - -const psmHashSubject = tokensFactory.tokens.PSMHash( - web3, - signedJWT, - config.didSubject1 -) -console.log('\tThe PSMHash is:', psmHashSubject) - -const psmHashReciever = tokensFactory.tokens.PSMHash( - web3, - signedJWT, - config.didEntity2 -) -console.log('\tThe PSMHashReciever is:', psmHashReciever) - -console.log('\n---- Create AIC ----') -// create AIC - -// Only the createAlastriaID transaction must be signed inside of AIC object -const aic = tokensFactory.tokens.createAIC( - context, - type, - config.signedTxCreateAlastriaID, - signedAT, - config.firstIdentityPubk, - kidCredential, - config.firstIdentityPubk, - jti, - tokenActivationDate, - tokenExpTime, - tokenNotBefore -) -console.log('\tAIC:', aic) - -const signedJWTAIC = tokensFactory.tokens.signJWT(aic, firstIdentityPrivateKey) -console.log('AIC Signed:', signedJWTAIC) -tests.alastriaIdCreations.validateAlastriaIdCreation(signedJWTAIC) - -// Data -const procUrl = config.procUrl -const procHash = config.procHash -const data = config.data -// End data - -console.log('\n---- createPresentationRequest ----') - -const presentationRequest = tokensFactory.tokens.createPresentationRequest( - didIsssuer, - context, - procUrl, - procHash, - data, - callbackURL, - type, - kidCredential, - config.firstIdentityPubk, - tokenExpTime, - tokenActivationDate, - jti -) - -const signedPresentationRequest = tokensFactory.tokens.signJWT( - presentationRequest, - firstIdentityPrivateKey -) -console.log('\nThe presentationRequest is: ', signedPresentationRequest) -tests.presentationRequests.validatePresentationRequest( - signedPresentationRequest -) - -const presentation = tokensFactory.tokens.createPresentation( - didIsssuer, - didIsssuer, - context, - tokensFactory.tokens.signJWT(presentationRequest, firstIdentityPrivateKey), - procUrl, - procHash, - type, - kidCredential, - config.firstIdentityPubk, - tokenExpTime, - tokenActivationDate, - jti -) -const signedPresentation = tokensFactory.tokens.signJWT( - presentation, - firstIdentityPrivateKey -) -console.log('\nThe presentation is: ', signedPresentation) -tests.presentations.validatePresentation(signedPresentation) diff --git a/exampleTokens/2.alastriaIdentityCreation.js b/exampleTokens/2.alastriaIdentityCreation.js new file mode 100644 index 0000000..be15e80 --- /dev/null +++ b/exampleTokens/2.alastriaIdentityCreation.js @@ -0,0 +1,77 @@ +const { tokensFactory } = require('alastria-identity-lib') +const { tests } = require('alastria-identity-JSON-objects/tests') +const fs = require('fs') +const keythereum = require('keythereum') + +//Preparing to read configuration.json +const rawdata = fs.readFileSync('../configuration.json') +const configData = JSON.parse(rawdata) + +const rawDataSignedObjects = fs.readFileSync('./SignedObjects.json') +const configDataSignedObjects = JSON.parse(rawDataSignedObjects) + +//Preparing subject1 keystore (privateKey) +const keyDataSubject1 = fs.readFileSync( + '../keystores/subject1-806bc0d7a47b890383a831634bcb92dd4030b092.json' +) +const subject1Keystore = JSON.parse(keyDataSubject1) +let subject1PrivateKey +try { + subject1PrivateKey = keythereum.recover(configData.addressPassword, subject1Keystore) +} catch (error) { + console.error('ERROR: ', error) + process.exit(1) +} + +// ************************************************************************************************** +// Starting reading/calculating DATA declared in configuration.json used to create the Alastria Token +const randomCharacters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" +const iss = configData.subject1Pubk +const signedAT = configDataSignedObjects.signedAT +const exp = Math.round(Date.now() / 1000) + 600 // 10 min = 600 seconds +const nbf = Math.round(Date.now() / 1000) - 600 // 10 min before +// THIS SHOULD BE OTHER IDENTIFICATION, BECAUSE AT THIS POINT WE DONT HAVE THE DID OF THE SUBJECT +const kid = "did:ala:quor:redT:0000000000000000000000000000000000000000#keys-1" //header.KID +const signedCreateAlastriaIDTX = configData.signedTxCreateAlastriaID +const jwk = configData.subject1Pubk //header.JWK +const context = configData.context +const type = ["US12"] +let jti = "" +const jtiVariableLength = 20 //length of the variable part of the jti +// IAT does not need to be passed, the library calculates it. +const iat = Math.round(Date.now() / 1000) + +//Generating a random JTI +for (let i = 0; i < jtiVariableLength; i++) { + jti += randomCharacters.charAt(Math.floor(Math.random() * randomCharacters.length)); +} +jti = "nameEntity/alastria/alastria-identity-creation/" + jti +// Ending DATA reading/calculating +// ************************************************************************************************** + +//Creating Alastria Identity Creation +console.log('\t 1 - Creating Alastria Identity Creation (AIC)\n') + +const alastriaIdentityCreation = tokensFactory.tokens.createAIC( + context, + type, + signedCreateAlastriaIDTX, + signedAT, + jwk, + kid, + jwk, + jti, + iat, + exp, + nbf +) +console.log('\n The Alastria Identity Creation (AIC) is:', alastriaIdentityCreation) + +// Signing the AlastriaToken +console.log('\t 2 - Signing the Alastria Identity Creation (AIC)\n') +const signedAIC = tokensFactory.tokens.signJWT(alastriaIdentityCreation, subject1PrivateKey) +console.log('\nThe Alastria Identity Creation (AIC) signed is: \n', signedAIC) + +// Validating the AlastriaIdentityCreation +console.log('\t 3 - Validating the Alastria Identity Creation (AIC)\n') +tests.alastriaIdCreations.validateAlastriaIdCreation(signedAIC) diff --git a/exampleTokens/3.alastriaSession.js b/exampleTokens/3.alastriaSession.js new file mode 100644 index 0000000..9a5f0b4 --- /dev/null +++ b/exampleTokens/3.alastriaSession.js @@ -0,0 +1,71 @@ +const { tokensFactory } = require('alastria-identity-lib') +const { tests } = require('alastria-identity-JSON-objects/tests') +const fs = require('fs') +const keythereum = require('keythereum') + +//Preparing to read configuration.json +const rawdata = fs.readFileSync('../configuration.json') +const configData = JSON.parse(rawdata) + +const rawDataSignedObjects = fs.readFileSync('./SignedObjects.json') +const configDataSignedObjects = JSON.parse(rawDataSignedObjects) + +//Preparing subject1 keystore (privateKey) +const keyDataSubject1 = fs.readFileSync( + '../keystores/subject1-806bc0d7a47b890383a831634bcb92dd4030b092.json' +) +const subject1Keystore = JSON.parse(keyDataSubject1) +let subject1PrivateKey +try { + subject1PrivateKey = keythereum.recover(configData.addressPassword, subject1Keystore) +} catch (error) { + console.error('ERROR: ', error) + process.exit(1) +} + +// ************************************************************************************************** +// Starting reading/calculating DATA declared in configuration.json used to create the Alastria Token +const randomCharacters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" +let iss = configData.didSubject1 +const signedAT = configDataSignedObjects.signedAT +const exp = Math.round(Date.now() / 1000) + 600 // 10 min = 600 seconds +const nbf = Math.round(Date.now() / 1000) - 600 // 10 min before +const kid = iss + "#keys-1" //header.KID +const jwk = configData.subject1Pubk //header.JWK +const context = configData.context +let jti = "" +const jtiVariableLength = 20 //length of the variable part of the jti +// IAT does not need to be passed, the library calculates it. +const type =["US211"] // the type "AlastriaSession" is setted in the library + +//Generating a random JTI +for (let i = 0; i < jtiVariableLength; i++) { + jti += randomCharacters.charAt(Math.floor(Math.random() * randomCharacters.length)); +} +jti = "nameEntity/alastria/alastria-session/" + jti +// Ending DATA reading/calculating +// ************************************************************************************************** + +//Creating Alastria Session +console.log('\t 1 - Creating Alastria Session (AS)\n') +const alastriaSession = tokensFactory.tokens.createAlastriaSession( + context, + iss, + kid, + type, + signedAT, + exp, + jwk, + nbf, + jti +) +console.log('\nThe Alastria Session (AS) is:\n', alastriaSession) + +// Signing the AlastriaSession +console.log('\t 2 - Signing the Alastria Session (AS)\n') +const signedAS = tokensFactory.tokens.signJWT(alastriaSession, subject1PrivateKey) +console.log('\nThe Alastria Session (AS) signed is: \n', signedAS) + +// Validating the AlastriaSession +console.log('\t 3 - Validating the Alastria Session (AS)\n') +tests.sessions.validateSession(signedAS) \ No newline at end of file diff --git a/exampleTokens/4.verifiableCredential.js b/exampleTokens/4.verifiableCredential.js new file mode 100644 index 0000000..f859c15 --- /dev/null +++ b/exampleTokens/4.verifiableCredential.js @@ -0,0 +1,119 @@ +const { tokensFactory } = require('alastria-identity-lib') +const { tests } = require('alastria-identity-JSON-objects/tests') +const fs = require('fs') +const Web3 = require('web3') +const keythereum = require('keythereum') + +//Preparing to read configuration.json +const rawdata = fs.readFileSync('../configuration.json') +const configData = JSON.parse(rawdata) + +const rawDataSignedObjects = fs.readFileSync('./SignedObjects.json') +const configDataSignedObjects = JSON.parse(rawDataSignedObjects) + +//FirstIdentity = Entity1 +const keyDataFirstIdentity = fs.readFileSync( + '../keystores/firstIdentity-643266eb3105f4bf8b4f4fec50886e453f0da9ad.json' + ) + const keystoreDataFirstIdentity = JSON.parse(keyDataFirstIdentity) + + let firstIdentityPrivateKey + try { + firstIdentityPrivateKey = keythereum.recover(configData.addressPassword, keystoreDataFirstIdentity) + } catch (error) { + console.error('ERROR: ', error) + process.exit(1) + } +// ************************************************************************************************** +// Starting reading/calculating DATA declared in configuration.json used to create the Alastria Token +const randomCharacters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" +const iss = configData.didEntity1 +const sub = configData.didSubject1 +const exp = Math.round(Date.now() / 1000) + 86400 // 1 day = 86400 seconds +const nbf = Math.round(Date.now() / 1000) - 86400 // 1 day before +const kid = iss + "#keys-1" //header.KID +const jwk = configData.firstIdentityPubk //header.JWK +let jti = "" +const jtiVariableLength = 20 //length of the variable part of the jti +// IAT does not need to be passed, the library calculates it. +const type = ["DrivingLicense"] +const context = [] + +//Generating a random JTI +for (let i = 0; i < jtiVariableLength; i++) { + jti += randomCharacters.charAt(Math.floor(Math.random() * randomCharacters.length)); +} +jti = "nameEntity/alastria/verifiable-credential/" + jti + +// Init your Blockchain provider +const myBlockchainServiceIp = configData.nodeUrl +const web3 = new Web3(new Web3.providers.HttpProvider(myBlockchainServiceIp)) + +// Multivalued Credential Map (key-->value) +const credentialSubject = {} +const credentialKey = configData.credentialKeyFather +const credentialValue = configData.credentialValueFather +const credentialSubKey1 = configData.credentialSubKey1 +const credentialSubKey2 = configData.credentialSubKey2 +const credentialSubKey3 = configData.credentialSubKey3 +const credentialSubKey4 = configData.credentialSubKey4 +const credentialSubValue1 = configData.credentialSubValue1 +const credentialSubValue2 = configData.credentialSubValue2 +const credentialSubValue3 = configData.credentialSubValue3 +const credentialSubValue4 = configData.credentialSubValue4 +credentialSubject[credentialKey] = credentialValue +credentialSubject[credentialKey][credentialSubKey1] = credentialSubValue1 +credentialSubject[credentialKey][credentialSubKey2] = credentialSubValue2 +credentialSubject[credentialKey][credentialSubKey3] = credentialSubValue3 +credentialSubject[credentialKey][credentialSubKey4] = credentialSubValue4 +credentialSubject.levelOfAssurance = 1 +// Ending DATA reading/calculating +// ************************************************************************************************** + +console.log('\t 1 - Creating Verifiable Credential (VC)\n') +const verifiableCredential = tokensFactory.tokens.createCredential( + iss, + context, + credentialSubject, + kid, + sub, + exp, + nbf, + jti, + jwk, + type +) +console.log('\nThe Verifiable Credential (VC) is: \n', verifiableCredential) + +// Signing the VerifiableCredential +console.log('\t 2 - Signing the Verifiable Credential (VC)\n') +const signedVC = tokensFactory.tokens.signJWT(verifiableCredential, firstIdentityPrivateKey) +console.log('\nThe Verifiable Credential (VC) signed is: \n', signedVC) + +// Validating the VerifiableCredential +console.log('\t 3 - Validating the Verifiable Credential (VC)\n') +tests.credentials.validateCredential(signedVC) + +// Creating Issuer PSMHash of the Verifiable Credential +console.log('\t 4 - Creating Issuer PSMHash of the Verifiable Credential\n') +const issuerPSMHash = tokensFactory.tokens.PSMHash( + web3, + signedVC, + iss +) +console.log('\nThe Issuer PSMHash of the Verifiable Credential is:', issuerPSMHash) + +// Creating Subject PSMHash of the Verifiable Credential +console.log('\t 5 - Creating Subject PSMHash of the Verifiable Credential\n') +const subjectPSMHash = tokensFactory.tokens.PSMHash( + web3, + signedVC, + sub +) +console.log('\nThe Subject PSMHash of the Verifiable Credential is:', subjectPSMHash) + +configDataSignedObjects.signedCredential = signedVC +fs.writeFileSync( + './SignedObjects.json', + JSON.stringify(configDataSignedObjects, null, 4) + ) \ No newline at end of file diff --git a/exampleTokens/5.presentationRequest.js b/exampleTokens/5.presentationRequest.js new file mode 100644 index 0000000..f97ceb8 --- /dev/null +++ b/exampleTokens/5.presentationRequest.js @@ -0,0 +1,74 @@ +const { tokensFactory } = require('alastria-identity-lib') +const { tests } = require('alastria-identity-JSON-objects/tests') +const fs = require('fs') +const keythereum = require('keythereum') + +//Preparing to read configuration.json +const rawdata = fs.readFileSync('../configuration.json') +const configData = JSON.parse(rawdata) + +//FirstIdentity = Entity1 +const keyDataFirstIdentity = fs.readFileSync( + '../keystores/firstIdentity-643266eb3105f4bf8b4f4fec50886e453f0da9ad.json' + ) + const keystoreDataFirstIdentity = JSON.parse(keyDataFirstIdentity) + + let firstIdentityPrivateKey + try { + firstIdentityPrivateKey = keythereum.recover(configData.addressPassword, keystoreDataFirstIdentity) + } catch (error) { + console.error('ERROR: ', error) + process.exit(1) + } + +// ************************************************************************************************** +// Starting reading/calculating DATA declared in configuration.json used to create the Alastria Token +const randomCharacters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" +const iss = configData.didEntity1 +const cbu = configData.callbackURL +const exp = Math.round(Date.now() / 1000) + 86400 // 1 day = 86400 seconds +const nbf = Math.round(Date.now() / 1000) - 86400 // 1 day before +const kid = iss + "#keys-1" //header.KID +const jwk = configData.firstIdentityPubk //header.JWK +const procUrl = configData.procUrl +const procHash = configData.procHash +const data = configData.data +const context = [] +const type = [] +let jti = "" +const jtiVariableLength = 20 //length of the variable part of the jti +// IAT does not need to be passed, the library calculates it. + +//Generating a random JTI +for (let i = 0; i < jtiVariableLength; i++) { + jti += randomCharacters.charAt(Math.floor(Math.random() * randomCharacters.length)); +} +jti = "nameEntity/alastria/presentation-request/" + jti +// Ending DATA reading/calculating +// ************************************************************************************************** + +//Creating Presentation Request +console.log('\t 1 - Creating Presentation Request (PR)\n') +const presentationRequest = tokensFactory.tokens.createPresentationRequest( + iss, + context, + procUrl, + procHash, + data, + cbu, + type, + kid, + jwk, + exp, + nbf, + jti +) + +// Signing the Presentation Request +console.log('\t 2 - Signing the Presentation Request (PR)\n') +const signedPresentationRequest = tokensFactory.tokens.signJWT(presentationRequest, firstIdentityPrivateKey) +console.log('\nThe presentationRequest is: ', signedPresentationRequest) + +// Validating the Presentation Request +console.log('\t 3 - Validating the Presentation Request (PR)\n') +tests.presentationRequests.validatePresentationRequest(signedPresentationRequest) \ No newline at end of file diff --git a/exampleTokens/6.verifiablePresentation.js b/exampleTokens/6.verifiablePresentation.js new file mode 100644 index 0000000..09e122d --- /dev/null +++ b/exampleTokens/6.verifiablePresentation.js @@ -0,0 +1,101 @@ +const { tokensFactory } = require('alastria-identity-lib') +const { tests } = require('alastria-identity-JSON-objects/tests') +const fs = require('fs') +const Web3 = require('web3') +const keythereum = require('keythereum') + +//Preparing to read configuration.json +const rawdata = fs.readFileSync('../configuration.json') +const configData = JSON.parse(rawdata) + +const rawDataSignedObjects = fs.readFileSync('./SignedObjects.json') +const configDataSignedObjects = JSON.parse(rawDataSignedObjects) + +//Preparing subject1 keystore (privateKey) to sign Verifiable Presentation +const keyDataSubject1 = fs.readFileSync( + '../keystores/subject1-806bc0d7a47b890383a831634bcb92dd4030b092.json' + ) + const subject1Keystore = JSON.parse(keyDataSubject1) + let subject1PrivateKey + try { + subject1PrivateKey = keythereum.recover(configData.addressPassword, subject1Keystore) + } catch (error) { + console.error('ERROR: ', error) + process.exit(1) + } + +// ************************************************************************************************** +// Starting reading/calculating DATA declared in configuration.json used to create the Alastria Token +const randomCharacters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" +const iss = configData.didSubject1 +const aud = configData.didEntity1 +const exp = Math.round(Date.now() / 1000) + 86400 // 1 day = 86400 seconds +const nbf = Math.round(Date.now() / 1000) - 86400 // 1 day before +const kid = iss + "#keys-1" //header.KID presentation +const jwk = configData.subject1Pubk +const procUrl = configData.procUrl +const procHash = configData.procHash +const signVC = configDataSignedObjects.signedCredential +const context = [] +const type = [] +let jti = "" +const jtiVariableLength = 20 //length of the variable part of the jti +// IAT does not need to be passed, the library calculates it. + +//Generating a random JTI from presentation +for (let i = 0; i < jtiVariableLength; i++) { + jti += randomCharacters.charAt(Math.floor(Math.random() * randomCharacters.length)); +} +jti = "nameEntity/alastria/verifiable-presentation/" + jti + +// Init your Blockchain provider +const myBlockchainServiceIp = configData.nodeUrl +const web3 = new Web3(new Web3.providers.HttpProvider(myBlockchainServiceIp)) + +// Ending DATA reading/calculating +// ************************************************************************************************** + +//Creating Verifiable Presentation +console.log('\t 3 - Creating Verifiable Presentation (VP)\n') +const verifiablePresentation = tokensFactory.tokens.createPresentation( + iss, + aud, + context, + signVC, + procUrl, + procHash, + type, + kid, + jwk, + exp, + nbf, + jti +) +console.log('\nThe Verifiable Presentation (VP) is: \n', verifiablePresentation) + +// Signing the Verifiable Presentation +console.log('\t 2 - Signing the Verifiable Presentation (VP)\n') +const signedVP = tokensFactory.tokens.signJWT(verifiablePresentation, subject1PrivateKey) +console.log('\nThe presentation is: ', signedVP) + +// Validating the Verifiable Presentation +console.log('\t 3 - Validating the Verifiable Presentation (VP)\n') +tests.presentations.validatePresentation(signedVP) + +// Creating Issuer PSMHash of the Verifiable Presentation +console.log('\t 4 - Creating Issuer PSMHash of the Verifiable Credential\n') +const issuerPSMHash = tokensFactory.tokens.PSMHash( + web3, + signedVP, + iss +) +console.log('\nThe Issuer PSMHash of the Verifiable Presentation is:', issuerPSMHash) + +// Creating Receiver PSMHash of the Verifiable Presentation +console.log('\t 5 - Creating Subject PSMHash of the Verifiable Credential\n') +const subjectPSMHash = tokensFactory.tokens.PSMHash( + web3, + signedVP, + aud +) +console.log('\nThe Receiver PSMHash of the Verifiable Presentation is:', subjectPSMHash) diff --git a/exampleTokens/7.decodeVerifyJWT.js b/exampleTokens/7.decodeVerifyJWT.js new file mode 100644 index 0000000..3f580c4 --- /dev/null +++ b/exampleTokens/7.decodeVerifyJWT.js @@ -0,0 +1,32 @@ +const { tokensFactory } = require('alastria-identity-lib') +const { tests } = require('alastria-identity-JSON-objects/tests') +const fs = require('fs') +const keythereum = require('keythereum') + +//Preparing to read configuration.json +const rawdata = fs.readFileSync('../configuration.json') +const configData = JSON.parse(rawdata) + +const rawDataSignedObjects = fs.readFileSync('./SignedObjects.json') +const configDataSignedObjects = JSON.parse(rawDataSignedObjects) + +// ************************************************************************************************** +// Starting reading/calculating DATA declared in configuration.json used to create the Alastria Token +const signedAT = configDataSignedObjects.signedAT +const jwk = configData.firstIdentityPubk //header.JWK +// Ending DATA reading/calculating +// ************************************************************************************************** + +// Decode AlastriaToken +console.log('\t 1 - Decoding the Alastria Token (AT)\n') +const decodedAT = tokensFactory.tokens.decodeJWT(signedAT) +console.log('\nThe decoded token is: \n', decodedAT) + +// Verifying AlastriaToken +console.log('\t 2 - Verifying the Alastria Token (AT)\n') +// '04' means uncompressed key (more info at https://github.com/indutny/elliptic/issues/138) +const verifyAT = tokensFactory.tokens.verifyJWT( + signedAT, + '04' + jwk.substr(2) +) +console.log('\nIs the signedJWT verified? \n', verifyAT) \ No newline at end of file diff --git a/exampleTokens/8.did.js b/exampleTokens/8.did.js new file mode 100644 index 0000000..912bb99 --- /dev/null +++ b/exampleTokens/8.did.js @@ -0,0 +1,36 @@ +const { tokensFactory } = require('alastria-identity-lib') +const { tests } = require('alastria-identity-JSON-objects/tests') +const fs = require('fs') + +//Preparing to read configuration.json +const rawdata = fs.readFileSync('../configuration.json') +const configData = JSON.parse(rawdata) + +// ************************************************************************************************** +// Starting reading/calculating DATA declared in configuration.json used to create the Alastria Token +const network = configData.network +const networkID = configData.networkID +const proxyAddress = configData.entity1 +// Same proxy but other network information +const otherNetwork = "besu" +const otherNetworkId = "redB" +// Ending DATA reading/calculating +// ************************************************************************************************** + +// Creating DID-T +console.log('\t 1 - Creating DID redT\n') +const didT = tokensFactory.tokens.createDID(network, proxyAddress, networkID) +console.log('\nThe DID-T is: \n', didT) + +// Validating DID-T +console.log('\t 2 - Validating DID redT\n') +tests.dids.validateDID(didT) + +// Creating DID-B +console.log('\t 3 - Creating DID redB\n') +const didB = tokensFactory.tokens.createDID(otherNetwork, proxyAddress, otherNetworkId) +console.log('\nThe DID-B is: \n', didB) + +// Validating DID-B +console.log('\t 4 - Validating DID redB\n') +tests.dids.validateDID(didB) diff --git a/exampleTokens/SignedObjects.json b/exampleTokens/SignedObjects.json new file mode 100644 index 0000000..589e1f7 --- /dev/null +++ b/exampleTokens/SignedObjects.json @@ -0,0 +1,4 @@ +{ + "signedCredential": "eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NksiLCJraWQiOiJkaWQ6YWxhOnF1b3I6cmVkVDpkYjA0MGZkNmFjY2EwZTc0Mjg5ODQ4NGJkMjczOGIzOGE4YzU3YWE4I2tleXMtMSIsImp3ayI6IjB4YWNhN2FhNDJlODMxZjQxYjZmMjQ2ZDA4OWZiY2FiZTczZTFjNmNlMDFhMWJiNWQ5NDRmZGE0ZTE0OGUxZDY5MzBmNmViYjUxMzhiMWQxMDNlM2UzODRjODkzOTYyYTIzOGU0MDg4YTU0OWJmZTg0ODY3ZGE5Njk0MjkzMWY2OWEifQ.eyJqdGkiOiJuYW1lRW50aXR5L2FsYXN0cmlhL3ZlcmlmaWFibGUtY3JlZGVudGlhbC9ZV3RMR3FubmRjWDUxZTRUQVExMyIsImlzcyI6ImRpZDphbGE6cXVvcjpyZWRUOmRiMDQwZmQ2YWNjYTBlNzQyODk4NDg0YmQyNzM4YjM4YThjNTdhYTgiLCJzdWIiOiJkaWQ6YWxhOnF1b3I6cmVkVDo0YjdmYTZmNjQ4ZjY3N2VmZWNiNWIxNzhhNjU2MGRiMjM4NTc0MzhiIiwiaWF0IjoxNjc2NjM4NTQ5LCJleHAiOjE2NzY3MjQ5NDksIm5iZiI6MTY3NjU1MjE0OSwidmMiOnsiQGNvbnRleHQiOlsiaHR0cHM6Ly93d3cudzMub3JnLzIwMTgvY3JlZGVudGlhbHMvdjEiLCJodHRwczovL2FsYXN0cmlhLmdpdGh1Yi5pby9pZGVudGl0eS9jcmVkZW50aWFscy92MSJdLCJ0eXBlIjpbIlZlcmlmaWFibGVDcmVkZW50aWFsIiwiQWxhc3RyaWFWZXJpZmlhYmxlQ3JlZGVudGlhbCIsIkRyaXZpbmdMaWNlbnNlIl0sImNyZWRlbnRpYWxTdWJqZWN0Ijp7IkRyaXZpbmcgbGljZW5zZSI6eyJEcml2ZXJgcyBOYW1lIjoiUGVyc29uTmFtZSIsIkRyaXZlcmBzIFN1cm5hbWUiOiJQZXJzb25TdXJuYW1lIiwiVHlwZSBvZiBsaWNlbnNlIjoiQiIsIlllYXJzIHdpdGggdGhlIGxpY2Vuc2UiOiIyMCB5ZWFycyJ9LCJsZXZlbE9mQXNzdXJhbmNlIjoxfX19.KAiufftBHW6ZSON7YayUpNb3F1wa98H275cHR8NokwLK8l1XR8yEDmTBsiY83gmrYNarzFsppXh06g8emYLz0w", + "signedAT": "eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NksiLCJraWQiOiJkaWQ6YWxhOnF1b3I6cmVkVDpkYjA0MGZkNmFjY2EwZTc0Mjg5ODQ4NGJkMjczOGIzOGE4YzU3YWE4I2tleXMtMSIsImp3ayI6IjB4YWNhN2FhNDJlODMxZjQxYjZmMjQ2ZDA4OWZiY2FiZTczZTFjNmNlMDFhMWJiNWQ5NDRmZGE0ZTE0OGUxZDY5MzBmNmViYjUxMzhiMWQxMDNlM2UzODRjODkzOTYyYTIzOGU0MDg4YTU0OWJmZTg0ODY3ZGE5Njk0MjkzMWY2OWEifQ.eyJpc3MiOiJkaWQ6YWxhOnF1b3I6cmVkVDpkYjA0MGZkNmFjY2EwZTc0Mjg5ODQ4NGJkMjczOGIzOGE4YzU3YWE4IiwiZ3d1IjoiaHR0cDovLzYzLjMzLjIwNi4xMTEvcnBjIiwiY2J1IjoiaHR0cHM6Ly9lbnRpdHkuYWxhc3RyaWEvYXBpL3YxL2xvZ2luIiwiaWF0IjoxNjc2NjM4NTI4LCJhbmkiOiJyZWRUIiwibmJmIjoxNjc2NjM3OTI4LCJleHAiOjE2NzY2MzkxMjgsImp0aSI6Im5hbWVFbnRpdHkvYWxhc3RyaWEvYWxhc3RyaWEtdG9rZW4vd0dSRDNPQmRvaHVCQVlyMDR6aDkifQ.uJtkqZFmugrf9whfu8sShPrWwH_vKsdhfVLRajKJcLmcc63oayV9cEUOwfV2k5dJGk8bTsyvTek562HpSqe8pQ" +} \ No newline at end of file