diff --git a/build.gradle.kts b/build.gradle.kts index d2ff177..ae08f0c 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -62,7 +62,7 @@ dependencies { testImplementation("io.kotest:kotest-assertions-json:5.5.0") // NftKit - implementation("id.walt:waltid-nftkit:1.0.0") + implementation("id.walt:waltid-nftkit:1.2304191004.0") // HTTP / Client: ktor implementation("io.ktor:ktor-client-core:2.0.0") diff --git a/config/idp-config.json b/config/idp-config.json index 471e931..cf19f76 100644 --- a/config/idp-config.json +++ b/config/idp-config.json @@ -69,7 +69,7 @@ } ], "default_nft_token_claim": { - "ecosystems": [ "EVM", "TEZOS", "NEAR" ], + "ecosystems": [ "EVM", "TEZOS", "NEAR" ,"POLKADOT"], "nftTokenContraints": { "EVM": { "chain": "POLYGON", @@ -85,6 +85,12 @@ "chain": "TESTNET", "factorySmartContractAddress": "", "smartContractAddress": "demo.khaled_lightency1.testnet" + }, + "POLKADOT": { + "chain": "OPAL", + "factorySmartContractAddress": "", + "smartContractAddress": "1062" + } } }, diff --git a/src/main/kotlin/id/walt/idp/config/ClaimConfig.kt b/src/main/kotlin/id/walt/idp/config/ClaimConfig.kt index b5cff44..44bf459 100644 --- a/src/main/kotlin/id/walt/idp/config/ClaimConfig.kt +++ b/src/main/kotlin/id/walt/idp/config/ClaimConfig.kt @@ -57,7 +57,9 @@ class NFTClaimMapping( val claimValue = when(verificationResult.nftresponseVerificationResult.ecosystem) { ChainEcosystem.EVM -> verificationResult.nftresponseVerificationResult.metadata?.evmNftMetadata?.attributes?.firstOrNull { a -> a.trait_type == mappingDefinition.trait }?.value ChainEcosystem.TEZOS -> verificationResult.nftresponseVerificationResult.metadata?.tezosNftMetadata?.attributes?.firstOrNull { a -> a.name == mappingDefinition.trait }?.value - ChainEcosystem.NEAR -> verificationResult.nftresponseVerificationResult.metadata?.nearNftMetadata?.metadata?.let { NFTManager.getNearNftAttributeValue(it, mappingDefinition.trait) } + ChainEcosystem.NEAR -> verificationResult.nftresponseVerificationResult.metadata?.nearNftMetadata?.metadata?.let { NFTManager.getNearNftAttributeValue(it, mappingDefinition.trait) + } + ChainEcosystem.POLKADOT -> verificationResult.nftresponseVerificationResult.metadata?.uniqueNftMetadata?.attributes?.let { a -> a.firstOrNull { a -> a.name == mappingDefinition.trait }?.value } }?: throw BadRequestResponse("Requested nft metadata trait not found in verification response") claimBuilder.claim(mappingDefinition.trait, claimValue) diff --git a/src/main/kotlin/id/walt/idp/nfts/NFTClaims.kt b/src/main/kotlin/id/walt/idp/nfts/NFTClaims.kt index 63fed0c..f01f64c 100644 --- a/src/main/kotlin/id/walt/idp/nfts/NFTClaims.kt +++ b/src/main/kotlin/id/walt/idp/nfts/NFTClaims.kt @@ -17,7 +17,7 @@ data class NftTokenConstraint( ) enum class ChainEcosystem { - EVM, TEZOS, NEAR + EVM, TEZOS, NEAR , POLKADOT } data class NftTokenClaim( diff --git a/src/main/kotlin/id/walt/idp/nfts/NFTController.kt b/src/main/kotlin/id/walt/idp/nfts/NFTController.kt index 817c110..4405d2d 100644 --- a/src/main/kotlin/id/walt/idp/nfts/NFTController.kt +++ b/src/main/kotlin/id/walt/idp/nfts/NFTController.kt @@ -6,6 +6,7 @@ import id.walt.idp.oidc.OIDCManager import id.walt.idp.oidc.ResponseVerificationResult import id.walt.idp.siwe.SiweManager import id.walt.idp.siwe.SiwnManager +import id.walt.idp.siwe.SiwpManager import id.walt.idp.siwe.SiwtManager import id.walt.siwe.SiweRequest import id.walt.siwe.eip4361.Eip4361Message @@ -39,12 +40,15 @@ object NFTController { } fun nftVerification(ctx: Context) { - val test = "TESTNET"; val sessionId = ctx.queryParam("session") ?: throw BadRequestResponse("Session not specified") + print("Session ID: $sessionId") val message = ctx.queryParam("message") ?: throw BadRequestResponse("Message not specified") + print("Message: $message") + val ecosystem = ctx.queryParam("ecosystem")?.let { ChainEcosystem.valueOf(it.uppercase()) } ?: throw BadRequestResponse("Ecosystem not specified") + print("Ecosystem: $ecosystem") val signature = ctx.queryParam("signature") ?: throw BadRequestResponse("Signature not specified") - val ecosystem = ctx.queryParam("ecosystem")?.let { ChainEcosystem.valueOf(it.toUpperCase()) } ?: throw BadRequestResponse("Ecosystem not specified") + print("Signature: $signature") val session = OIDCManager.getOIDCSession(sessionId) @@ -72,11 +76,19 @@ object NFTController { } ChainEcosystem.NEAR -> { val publicKey = SiwnManager.getPublicKey(message) - print( "is the public key" + publicKey ) + print("Public Key: $publicKey") address = SiwnManager.getAddress(message) - print("is address " + address) + print("Address: $address") SiwnManager.verifySignature(session!!, message, publicKey, signature) } + ChainEcosystem.POLKADOT -> { + val publicKey = SiwpManager.getPublicKey(message) + address = SiwpManager.getPublicKey(message) + + + SiwpManager.verifySignature(session!!, message, publicKey, signature) + } + } if(!siwxResult) { diff --git a/src/main/kotlin/id/walt/idp/nfts/NFTManager.kt b/src/main/kotlin/id/walt/idp/nfts/NFTManager.kt index 5f28703..acc981a 100644 --- a/src/main/kotlin/id/walt/idp/nfts/NFTManager.kt +++ b/src/main/kotlin/id/walt/idp/nfts/NFTManager.kt @@ -79,7 +79,7 @@ object NFTManager { Common.getEVMChain(tokenConstraint.chain!!.toString()), tokenConstraint.smartContractAddress!!, account.trim() )?.compareTo(BigInteger("0")) == 1 - ChainEcosystem.TEZOS, ChainEcosystem.NEAR -> VerificationService.verifyNftOwnershipWithinCollection( + ChainEcosystem.TEZOS, ChainEcosystem.NEAR , ChainEcosystem.POLKADOT-> VerificationService.verifyNftOwnershipWithinCollection( tokenConstraint.chain!!, tokenConstraint.smartContractAddress!!,account) } @@ -117,7 +117,8 @@ object NFTManager { NearNftService.getNFTforAccount( account, it, NearChain.valueOf( tokenConstraint.chain!!.toString() ))}?.get(0) - else null + else null, + ) } diff --git a/src/main/kotlin/id/walt/idp/siwe/SiwnManager.kt b/src/main/kotlin/id/walt/idp/siwe/SiwnManager.kt index 99ffb18..ed7e97c 100644 --- a/src/main/kotlin/id/walt/idp/siwe/SiwnManager.kt +++ b/src/main/kotlin/id/walt/idp/siwe/SiwnManager.kt @@ -33,9 +33,7 @@ object SiwnManager { expectSuccess = false } fun verifySignature(session: OIDCSession, message: String, publicKey: String, signature: String): Boolean{ - println("this is the message: $message") - println("this is the public key: $publicKey") - println("this is the signature: $signature") + val nonce= getNonce(message) if (session.siweSession?.nonce != nonce) { diff --git a/src/main/kotlin/id/walt/idp/siwe/siwpManager.kt b/src/main/kotlin/id/walt/idp/siwe/siwpManager.kt new file mode 100644 index 0000000..babd0e7 --- /dev/null +++ b/src/main/kotlin/id/walt/idp/siwe/siwpManager.kt @@ -0,0 +1,70 @@ +package id.walt.idp.siwe + +import id.walt.idp.config.IDPConfig +import id.walt.idp.oidc.OIDCSession +import io.ktor.client.* +import io.ktor.client.call.* +import io.ktor.client.engine.cio.* +import io.ktor.client.plugins.contentnegotiation.* +import io.ktor.client.plugins.logging.* +import io.ktor.client.request.* +import io.ktor.serialization.kotlinx.json.* +import kotlinx.coroutines.runBlocking +import kotlinx.serialization.json.Json +import java.net.URLEncoder +import java.nio.charset.StandardCharsets + +object SiwpManager { + val client = HttpClient(CIO.create { requestTimeout = 0 }) { + install(ContentNegotiation) { + json(Json { + ignoreUnknownKeys = true + }) + } + install(Logging) { + logger = Logger.SIMPLE + level = LogLevel.ALL + } + expectSuccess = false + } + fun verifySignature(session: OIDCSession, message: String, publicKey: String, signature: String): Boolean{ + val nonce= getNonce(message) + if (session.siweSession?.nonce != nonce) { + return false; + } + if (SiweManager.nonceBlacklists.contains(nonce)) { + return false + } + SiweManager.nonceBlacklists.add(nonce) + + + return runBlocking { + val result = client.get("${IDPConfig.config.jsProjectExternalUrl}/Polkadot/test?publicKey=${publicKey}&signature=${signature}&message=${URLEncoder.encode(message, StandardCharsets.UTF_8)}") { + }.body() + return@runBlocking result + } + } + + + fun getAddress(message:String): String{ + val address= message.split(" .").get(0).split(":").last().trim() + return address + } + + fun getNonce(message: String): String{ + val nonce= message.split(".").last().split(":").last().trim() + + return nonce + } + + fun getPublicKey(message: String): String{ + val startIndex = message.indexOf("Public Key: ") + + + val endIndex = message.indexOf(" ", startIndex + 12) + + return message.substring(startIndex + 12, endIndex) + } + + +} diff --git a/src/main/resources/walt-default.yaml b/src/main/resources/walt-default.yaml index 305a852..77a8088 100644 --- a/src/main/resources/walt-default.yaml +++ b/src/main/resources/walt-default.yaml @@ -28,6 +28,10 @@ keys: 0xc8ca7c4f2dc014d7e4fc6973052da1517b4c54da: "9b9c46d0873a9982ade718ab3f4cfc57172b2f07c21b9901ddf239e931d2d6dc" +indexersUrl: + uniqueUrl: "https://api-unique.uniquescan.io/v1/graphql" + opalUrl: "https://api-opal.uniquescan.io/v1/graphql" + apiKeys: ethereumBlockExplorer: "JGD5ZUUBHE8CUXPNKZASVQPHRGBMB7A5XV" polygonBlockExplorer: "DZ3PFVWGJE5B8DMDQPRZ5JFBR6U484B82G" diff --git a/web/waltid-idpkit-ui/nuxt.config.js b/web/waltid-idpkit-ui/nuxt.config.js index c47c9e6..6f146db 100644 --- a/web/waltid-idpkit-ui/nuxt.config.js +++ b/web/waltid-idpkit-ui/nuxt.config.js @@ -42,7 +42,45 @@ export default { ], // Build Configuration: https://go.nuxtjs.dev/config-build - build: {}, + build: { + babel: { + compact: true, + }, + extend(config, { isClient }) { + // Extend only webpack config for client-bundle + if (isClient) { + config.devtool = "source-map"; + } + config.module.rules.push( + { + test: /\.js$/, + loader: require.resolve( + "@open-wc/webpack-import-meta-loader" + ), + exclude: /\.vue$/, + }, + { + test: /\.m?js$/, + include: /node_modules[/\\|]@polkadot/i, + use: { + loader: "babel-loader", + options: { + presets: [ + "@babel/preset-env", + "@vue/cli-plugin-babel/preset", + ], + plugins: [ + "@babel/plugin-proposal-private-methods", + "@babel/plugin-proposal-class-properties", + "@babel/plugin-proposal-object-rest-spread", + ], + }, + }, + } + ); + }, + }, + generate: { dir: 'dist' diff --git a/web/waltid-idpkit-ui/package.json b/web/waltid-idpkit-ui/package.json index 867fe03..0555506 100644 --- a/web/waltid-idpkit-ui/package.json +++ b/web/waltid-idpkit-ui/package.json @@ -1,53 +1,57 @@ { - "name": "waltid-idpkit-ui", - "version": "1.0.0", - "private": true, - "scripts": { - "dev": "nuxt", - "build": "nuxt build", - "start": "nuxt start", - "generate": "nuxt generate" - }, - "dependencies": { - "@airgap/beacon-sdk": "^3.3.0", - "@nuxtjs/axios": "^5.13.6", - "@taquito/beacon-wallet": "^15.0.0", - "@taquito/taquito": "^15.0.0", - "@walletconnect/web3-provider": "^1.8.0", - "bootstrap": "^4.6.2", - "bootstrap-vue": "^2.23.1", - "core-js": "^3.26.0", - "ethers": "^5.7.2", - "nuxt": "^2.15.8", - "qrious": "^4.0.2", - "vue": "2.7.14", - "vue-server-renderer": "2.7.14", - "vue-template-compiler": "2.7.14", - "web3modal": "^1.9.9", - "webpack": "^4.46.0", - "@near-wallet-selector/coin98-wallet": "^7.6.0", - "@near-wallet-selector/core": "^7.6.0", - "@near-wallet-selector/default-wallets": "^7.6.0", - "@near-wallet-selector/here-wallet": "^7.6.0", - "@near-wallet-selector/ledger": "^7.6.0", - "@near-wallet-selector/math-wallet": "^7.6.0", - "@near-wallet-selector/meteor-wallet": "^7.6.0", - "@near-wallet-selector/modal-ui": "^7.6.0", - "@near-wallet-selector/my-near-wallet": "^7.6.0", - "@near-wallet-selector/narwallets": "^7.6.0", - "@near-wallet-selector/near-wallet": "^7.6.0", - "@near-wallet-selector/nearfi": "^7.6.0", - "@near-wallet-selector/neth": "^7.6.0", - "@near-wallet-selector/nightly": "^7.6.0", - "@near-wallet-selector/nightly-connect": "^7.6.0", - "@near-wallet-selector/opto-wallet": "^7.6.0", - "@near-wallet-selector/sender": "^7.6.0", - "@near-wallet-selector/wallet-connect": "^7.6.0", - "@near-wallet-selector/welldone-wallet": "^7.6.0", - "@near-wallet-selector/xdefi": "^7.6.0", - "near-api-js": "^1.1.0" - }, - "devDependencies": { - "@nuxt/types": "~2.15.8" - } + "name": "waltid-idpkit-ui", + "version": "1.0.0", + "private": true, + "scripts": { + "dev": "nuxt", + "build": "nuxt build", + "start": "nuxt start", + "generate": "nuxt generate" + }, + "dependencies": { + "@airgap/beacon-sdk": "^3.3.0", + "@near-wallet-selector/coin98-wallet": "^7.6.0", + "@near-wallet-selector/core": "^7.6.0", + "@near-wallet-selector/default-wallets": "^7.6.0", + "@near-wallet-selector/here-wallet": "^7.6.0", + "@near-wallet-selector/ledger": "^7.6.0", + "@near-wallet-selector/math-wallet": "^7.6.0", + "@near-wallet-selector/meteor-wallet": "^7.6.0", + "@near-wallet-selector/modal-ui": "^7.6.0", + "@near-wallet-selector/my-near-wallet": "^7.6.0", + "@near-wallet-selector/narwallets": "^7.6.0", + "@near-wallet-selector/near-wallet": "^7.6.0", + "@near-wallet-selector/nearfi": "^7.6.0", + "@near-wallet-selector/neth": "^7.6.0", + "@near-wallet-selector/nightly": "^7.6.0", + "@near-wallet-selector/nightly-connect": "^7.6.0", + "@near-wallet-selector/opto-wallet": "^7.6.0", + "@near-wallet-selector/sender": "^7.6.0", + "@near-wallet-selector/wallet-connect": "^7.6.0", + "@near-wallet-selector/welldone-wallet": "^7.6.0", + "@near-wallet-selector/xdefi": "^7.6.0", + "@nuxtjs/axios": "^5.13.6", + "@open-wc/webpack-import-meta-loader": "^0.4.7", + "@polkadot/api": "^10.3.2", + "@polkadot/extension-dapp": "^0.45.5", + "@taquito/beacon-wallet": "^15.0.0", + "@taquito/taquito": "^15.0.0", + "@vue/cli-plugin-babel": "^5.0.8", + "@walletconnect/web3-provider": "^1.8.0", + "bootstrap": "^4.6.2", + "bootstrap-vue": "^2.23.1", + "core-js": "^3.26.0", + "ethers": "^5.7.2", + "near-api-js": "^1.1.0", + "nuxt": "^2.15.8", + "qrious": "^4.0.2", + "vue": "2.7.14", + "vue-server-renderer": "2.7.14", + "vue-template-compiler": "2.7.14", + "web3modal": "^1.9.9", + "webpack": "^4.46.0" + }, + "devDependencies": { + "@nuxt/types": "~2.15.8" + } } diff --git a/web/waltid-idpkit-ui/pages/connect-wallet.vue b/web/waltid-idpkit-ui/pages/connect-wallet.vue index 55d7d37..2d18e6d 100644 --- a/web/waltid-idpkit-ui/pages/connect-wallet.vue +++ b/web/waltid-idpkit-ui/pages/connect-wallet.vue @@ -1,239 +1,296 @@