mirror of
https://github.com/TanelOrumaa/Estonian-ID-card-mobile-authenticator-POC.git
synced 2024-12-22 12:30:16 +02:00
MOB-42 Redid the whole frontend part in Vue
This commit is contained in:
parent
7daea4b6c2
commit
da2dbeb0fc
@ -18,6 +18,8 @@
|
|||||||
<kotlin.version>1.5.31</kotlin.version>
|
<kotlin.version>1.5.31</kotlin.version>
|
||||||
<caffeine.version>2.8.5</caffeine.version>
|
<caffeine.version>2.8.5</caffeine.version>
|
||||||
<javaxcache.version>1.1.1</javaxcache.version>
|
<javaxcache.version>1.1.1</javaxcache.version>
|
||||||
|
<node.version>v16.13.0</node.version>
|
||||||
|
<npm.version>8.1.4</npm.version>
|
||||||
</properties>
|
</properties>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<dependency>
|
<dependency>
|
||||||
@ -115,6 +117,91 @@
|
|||||||
</dependency>
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
</plugin>
|
</plugin>
|
||||||
|
<!-- Plugin to install node and npm and then build the vue project -->
|
||||||
|
<plugin>
|
||||||
|
<groupId>com.github.eirslett</groupId>
|
||||||
|
<artifactId>frontend-maven-plugin</artifactId>
|
||||||
|
<version>1.12.0</version>
|
||||||
|
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<id>Install node and npm</id>
|
||||||
|
<goals>
|
||||||
|
<goal>install-node-and-npm</goal>
|
||||||
|
</goals>
|
||||||
|
<phase>generate-resources</phase>
|
||||||
|
<configuration>
|
||||||
|
<nodeVersion>${node.version}</nodeVersion>
|
||||||
|
<npmVersion>${npm.version}</npmVersion>
|
||||||
|
</configuration>
|
||||||
|
</execution>
|
||||||
|
|
||||||
|
<execution>
|
||||||
|
<id>npm install</id>
|
||||||
|
<goals>
|
||||||
|
<goal>npm</goal>
|
||||||
|
</goals>
|
||||||
|
<phase>generate-resources</phase>
|
||||||
|
<configuration>
|
||||||
|
<arguments>install</arguments>
|
||||||
|
</configuration>
|
||||||
|
</execution>
|
||||||
|
|
||||||
|
<execution>
|
||||||
|
<id>npm build</id>
|
||||||
|
<goals>
|
||||||
|
<goal>npm</goal>
|
||||||
|
</goals>
|
||||||
|
<phase>process-resources</phase>
|
||||||
|
<configuration>
|
||||||
|
<arguments>run build</arguments>
|
||||||
|
</configuration>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
<configuration>
|
||||||
|
<nodeVersion>${node.version}</nodeVersion>
|
||||||
|
<workingDirectory>src/demo-website</workingDirectory>
|
||||||
|
<!-- <installDirectory>src/demo-website/dist</installDirectory>-->
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
<!-- Plugin to copy built vue project from src/frontend/dist to target/classes/static -->
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-resources-plugin</artifactId>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<id>Copy web-eid.js file to Vue root folder.</id>
|
||||||
|
<phase>generate-resources</phase>
|
||||||
|
<goals>
|
||||||
|
<goal>copy-resources</goal>
|
||||||
|
</goals>
|
||||||
|
<configuration>
|
||||||
|
<outputDirectory>src/demo-website/src</outputDirectory>
|
||||||
|
<resources>
|
||||||
|
<resource>
|
||||||
|
<directory>src/demo-website/node_modules/@web-eid/web-eid-library/dist/es</directory>
|
||||||
|
</resource>
|
||||||
|
</resources>
|
||||||
|
</configuration>
|
||||||
|
</execution>
|
||||||
|
<execution>
|
||||||
|
<id>Copy Vue frontend into Spring Boot target static folder</id>
|
||||||
|
<phase>process-resources</phase>
|
||||||
|
<goals>
|
||||||
|
<goal>copy-resources</goal>
|
||||||
|
</goals>
|
||||||
|
<configuration>
|
||||||
|
<outputDirectory>target/classes/static</outputDirectory>
|
||||||
|
<resources>
|
||||||
|
<resource>
|
||||||
|
<directory>src/demo-website/dist</directory>
|
||||||
|
<filtering>true</filtering>
|
||||||
|
</resource>
|
||||||
|
</resources>
|
||||||
|
</configuration>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
</plugins>
|
</plugins>
|
||||||
</build>
|
</build>
|
||||||
|
|
||||||
|
Binary file not shown.
132
demoBackend/src/demo-website/package-lock.json
generated
132
demoBackend/src/demo-website/package-lock.json
generated
@ -10,10 +10,15 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@web-eid/web-eid-library": "../../../../web-eid.js/",
|
"@web-eid/web-eid-library": "../../../../web-eid.js/",
|
||||||
"core-js": "^3.6.5",
|
"core-js": "^3.6.5",
|
||||||
"vue": "^3.0.0"
|
"js-cookie": "^3.0.1",
|
||||||
|
"vue": "^3.0.0",
|
||||||
|
"vue-router": "^4.0.0-0",
|
||||||
|
"vuex": "^4.0.2",
|
||||||
|
"vuex-persistedstate": "^4.1.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@vue/cli-plugin-babel": "~4.5.0",
|
"@vue/cli-plugin-babel": "~4.5.0",
|
||||||
|
"@vue/cli-plugin-router": "~4.5.0",
|
||||||
"@vue/cli-service": "~4.5.0",
|
"@vue/cli-service": "~4.5.0",
|
||||||
"@vue/compiler-sfc": "^3.0.0",
|
"@vue/compiler-sfc": "^3.0.0",
|
||||||
"babel-eslint": "^10.1.0",
|
"babel-eslint": "^10.1.0",
|
||||||
@ -1729,17 +1734,6 @@
|
|||||||
"node": ">= 6"
|
"node": ">= 6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@popperjs/core": {
|
|
||||||
"version": "2.11.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.0.tgz",
|
|
||||||
"integrity": "sha512-zrsUxjLOKAzdewIDRWy9nsV1GQsKBCWaGwsZQlCgr6/q+vjyZhFgqedLfFBuI9anTPEUT4APq9Mu0SZBTzIcGQ==",
|
|
||||||
"dev": true,
|
|
||||||
"peer": true,
|
|
||||||
"funding": {
|
|
||||||
"type": "opencollective",
|
|
||||||
"url": "https://opencollective.com/popperjs"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@soda/friendly-errors-webpack-plugin": {
|
"node_modules/@soda/friendly-errors-webpack-plugin": {
|
||||||
"version": "1.8.1",
|
"version": "1.8.1",
|
||||||
"resolved": "https://registry.npmjs.org/@soda/friendly-errors-webpack-plugin/-/friendly-errors-webpack-plugin-1.8.1.tgz",
|
"resolved": "https://registry.npmjs.org/@soda/friendly-errors-webpack-plugin/-/friendly-errors-webpack-plugin-1.8.1.tgz",
|
||||||
@ -2567,6 +2561,11 @@
|
|||||||
"integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=",
|
"integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/@vue/devtools-api": {
|
||||||
|
"version": "6.0.0-beta.20.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-6.0.0-beta.20.1.tgz",
|
||||||
|
"integrity": "sha512-R2rfiRY+kZugzWh9ZyITaovx+jpU4vgivAEAiz80kvh3yviiTU3CBuGuyWpSwGz9/C7TkSWVM/FtQRGlZ16n8Q=="
|
||||||
|
},
|
||||||
"node_modules/@vue/preload-webpack-plugin": {
|
"node_modules/@vue/preload-webpack-plugin": {
|
||||||
"version": "1.1.2",
|
"version": "1.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/@vue/preload-webpack-plugin/-/preload-webpack-plugin-1.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/@vue/preload-webpack-plugin/-/preload-webpack-plugin-1.1.2.tgz",
|
||||||
@ -8669,6 +8668,14 @@
|
|||||||
"integrity": "sha512-JVAfqNPTvNq3sB/VHQJAFxN/sPgKnsKrCwyRt15zwNCdrMMJDdcEOdubuy+DuJYYdm0ox1J4uzEuYKkN+9yhVg==",
|
"integrity": "sha512-JVAfqNPTvNq3sB/VHQJAFxN/sPgKnsKrCwyRt15zwNCdrMMJDdcEOdubuy+DuJYYdm0ox1J4uzEuYKkN+9yhVg==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/js-cookie": {
|
||||||
|
"version": "3.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-3.0.1.tgz",
|
||||||
|
"integrity": "sha512-+0rgsUXZu4ncpPxRL+lNEptWMOWl9etvPHc/koSRp6MPwpRYAhmk0dUG00J4bxVV3r9uUzfo24wW0knS07SKSw==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/js-message": {
|
"node_modules/js-message": {
|
||||||
"version": "1.0.7",
|
"version": "1.0.7",
|
||||||
"resolved": "https://registry.npmjs.org/js-message/-/js-message-1.0.7.tgz",
|
"resolved": "https://registry.npmjs.org/js-message/-/js-message-1.0.7.tgz",
|
||||||
@ -12104,6 +12111,11 @@
|
|||||||
"integrity": "sha512-Vpfqwm4EnqGdlsBFNmHhxhElJYrdfcxPThu+ryKS5J8L/fhAwLazFZtq+S+TWZ9ANj2piSQLGj6NQg+lKPmxrw==",
|
"integrity": "sha512-Vpfqwm4EnqGdlsBFNmHhxhElJYrdfcxPThu+ryKS5J8L/fhAwLazFZtq+S+TWZ9ANj2piSQLGj6NQg+lKPmxrw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/shvl": {
|
||||||
|
"version": "2.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/shvl/-/shvl-2.0.3.tgz",
|
||||||
|
"integrity": "sha512-V7C6S9Hlol6SzOJPnQ7qzOVEWUQImt3BNmmzh40wObhla3XOYMe4gGiYzLrJd5TFa+cI2f9LKIRJTTKZSTbWgw=="
|
||||||
|
},
|
||||||
"node_modules/side-channel": {
|
"node_modules/side-channel": {
|
||||||
"version": "1.0.4",
|
"version": "1.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz",
|
||||||
@ -14151,6 +14163,17 @@
|
|||||||
"integrity": "sha1-M7QHd3VMZDJXPBIMw4CLvRDUfwQ=",
|
"integrity": "sha1-M7QHd3VMZDJXPBIMw4CLvRDUfwQ=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/vue-router": {
|
||||||
|
"version": "4.0.12",
|
||||||
|
"resolved": "https://registry.npmjs.org/vue-router/-/vue-router-4.0.12.tgz",
|
||||||
|
"integrity": "sha512-CPXvfqe+mZLB1kBWssssTiWg4EQERyqJZes7USiqfW9B5N2x+nHlnsM1D3b5CaJ6qgCvMmYJnz+G0iWjNCvXrg==",
|
||||||
|
"dependencies": {
|
||||||
|
"@vue/devtools-api": "^6.0.0-beta.18"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"vue": "^3.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/vue-style-loader": {
|
"node_modules/vue-style-loader": {
|
||||||
"version": "4.1.3",
|
"version": "4.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/vue-style-loader/-/vue-style-loader-4.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/vue-style-loader/-/vue-style-loader-4.1.3.tgz",
|
||||||
@ -14173,6 +14196,37 @@
|
|||||||
"integrity": "sha512-4gDntzrifFnCEvyoO8PqyJDmguXgVPxKiIxrBKjIowvL9l+N66196+72XVYR8BBf1Uv1Fgt3bGevJ+sEmxfZzw==",
|
"integrity": "sha512-4gDntzrifFnCEvyoO8PqyJDmguXgVPxKiIxrBKjIowvL9l+N66196+72XVYR8BBf1Uv1Fgt3bGevJ+sEmxfZzw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/vuex": {
|
||||||
|
"version": "4.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/vuex/-/vuex-4.0.2.tgz",
|
||||||
|
"integrity": "sha512-M6r8uxELjZIK8kTKDGgZTYX/ahzblnzC4isU1tpmEuOIIKmV+TRdc+H4s8ds2NuZ7wpUTdGRzJRtoj+lI+pc0Q==",
|
||||||
|
"dependencies": {
|
||||||
|
"@vue/devtools-api": "^6.0.0-beta.11"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"vue": "^3.0.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/vuex-persistedstate": {
|
||||||
|
"version": "4.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/vuex-persistedstate/-/vuex-persistedstate-4.1.0.tgz",
|
||||||
|
"integrity": "sha512-3SkEj4NqwM69ikJdFVw6gObeB0NHyspRYMYkR/EbhR0hbvAKyR5gksVhtAfY1UYuWUOCCA0QNGwv9pOwdj+XUQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"deepmerge": "^4.2.2",
|
||||||
|
"shvl": "^2.0.3"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"vuex": "^3.0 || ^4.0.0-rc"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/vuex-persistedstate/node_modules/deepmerge": {
|
||||||
|
"version": "4.2.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz",
|
||||||
|
"integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/watchpack": {
|
"node_modules/watchpack": {
|
||||||
"version": "1.7.5",
|
"version": "1.7.5",
|
||||||
"resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.7.5.tgz",
|
"resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.7.5.tgz",
|
||||||
@ -16370,13 +16424,6 @@
|
|||||||
"integrity": "sha512-shAmDyaQC4H92APFoIaVDHCx5bStIocgvbwQyxPRrbUY20V1EYTbSDchWbuwlMG3V17cprZhA6+78JfB+3DTPw==",
|
"integrity": "sha512-shAmDyaQC4H92APFoIaVDHCx5bStIocgvbwQyxPRrbUY20V1EYTbSDchWbuwlMG3V17cprZhA6+78JfB+3DTPw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"@popperjs/core": {
|
|
||||||
"version": "2.11.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.0.tgz",
|
|
||||||
"integrity": "sha512-zrsUxjLOKAzdewIDRWy9nsV1GQsKBCWaGwsZQlCgr6/q+vjyZhFgqedLfFBuI9anTPEUT4APq9Mu0SZBTzIcGQ==",
|
|
||||||
"dev": true,
|
|
||||||
"peer": true
|
|
||||||
},
|
|
||||||
"@soda/friendly-errors-webpack-plugin": {
|
"@soda/friendly-errors-webpack-plugin": {
|
||||||
"version": "1.8.1",
|
"version": "1.8.1",
|
||||||
"resolved": "https://registry.npmjs.org/@soda/friendly-errors-webpack-plugin/-/friendly-errors-webpack-plugin-1.8.1.tgz",
|
"resolved": "https://registry.npmjs.org/@soda/friendly-errors-webpack-plugin/-/friendly-errors-webpack-plugin-1.8.1.tgz",
|
||||||
@ -17081,6 +17128,11 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@vue/devtools-api": {
|
||||||
|
"version": "6.0.0-beta.20.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-6.0.0-beta.20.1.tgz",
|
||||||
|
"integrity": "sha512-R2rfiRY+kZugzWh9ZyITaovx+jpU4vgivAEAiz80kvh3yviiTU3CBuGuyWpSwGz9/C7TkSWVM/FtQRGlZ16n8Q=="
|
||||||
|
},
|
||||||
"@vue/preload-webpack-plugin": {
|
"@vue/preload-webpack-plugin": {
|
||||||
"version": "1.1.2",
|
"version": "1.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/@vue/preload-webpack-plugin/-/preload-webpack-plugin-1.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/@vue/preload-webpack-plugin/-/preload-webpack-plugin-1.1.2.tgz",
|
||||||
@ -21944,6 +21996,11 @@
|
|||||||
"integrity": "sha512-JVAfqNPTvNq3sB/VHQJAFxN/sPgKnsKrCwyRt15zwNCdrMMJDdcEOdubuy+DuJYYdm0ox1J4uzEuYKkN+9yhVg==",
|
"integrity": "sha512-JVAfqNPTvNq3sB/VHQJAFxN/sPgKnsKrCwyRt15zwNCdrMMJDdcEOdubuy+DuJYYdm0ox1J4uzEuYKkN+9yhVg==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"js-cookie": {
|
||||||
|
"version": "3.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-3.0.1.tgz",
|
||||||
|
"integrity": "sha512-+0rgsUXZu4ncpPxRL+lNEptWMOWl9etvPHc/koSRp6MPwpRYAhmk0dUG00J4bxVV3r9uUzfo24wW0knS07SKSw=="
|
||||||
|
},
|
||||||
"js-message": {
|
"js-message": {
|
||||||
"version": "1.0.7",
|
"version": "1.0.7",
|
||||||
"resolved": "https://registry.npmjs.org/js-message/-/js-message-1.0.7.tgz",
|
"resolved": "https://registry.npmjs.org/js-message/-/js-message-1.0.7.tgz",
|
||||||
@ -24809,6 +24866,11 @@
|
|||||||
"integrity": "sha512-Vpfqwm4EnqGdlsBFNmHhxhElJYrdfcxPThu+ryKS5J8L/fhAwLazFZtq+S+TWZ9ANj2piSQLGj6NQg+lKPmxrw==",
|
"integrity": "sha512-Vpfqwm4EnqGdlsBFNmHhxhElJYrdfcxPThu+ryKS5J8L/fhAwLazFZtq+S+TWZ9ANj2piSQLGj6NQg+lKPmxrw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"shvl": {
|
||||||
|
"version": "2.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/shvl/-/shvl-2.0.3.tgz",
|
||||||
|
"integrity": "sha512-V7C6S9Hlol6SzOJPnQ7qzOVEWUQImt3BNmmzh40wObhla3XOYMe4gGiYzLrJd5TFa+cI2f9LKIRJTTKZSTbWgw=="
|
||||||
|
},
|
||||||
"side-channel": {
|
"side-channel": {
|
||||||
"version": "1.0.4",
|
"version": "1.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz",
|
||||||
@ -26491,6 +26553,14 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"vue-router": {
|
||||||
|
"version": "4.0.12",
|
||||||
|
"resolved": "https://registry.npmjs.org/vue-router/-/vue-router-4.0.12.tgz",
|
||||||
|
"integrity": "sha512-CPXvfqe+mZLB1kBWssssTiWg4EQERyqJZes7USiqfW9B5N2x+nHlnsM1D3b5CaJ6qgCvMmYJnz+G0iWjNCvXrg==",
|
||||||
|
"requires": {
|
||||||
|
"@vue/devtools-api": "^6.0.0-beta.18"
|
||||||
|
}
|
||||||
|
},
|
||||||
"vue-style-loader": {
|
"vue-style-loader": {
|
||||||
"version": "4.1.3",
|
"version": "4.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/vue-style-loader/-/vue-style-loader-4.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/vue-style-loader/-/vue-style-loader-4.1.3.tgz",
|
||||||
@ -26515,6 +26585,30 @@
|
|||||||
"integrity": "sha512-4gDntzrifFnCEvyoO8PqyJDmguXgVPxKiIxrBKjIowvL9l+N66196+72XVYR8BBf1Uv1Fgt3bGevJ+sEmxfZzw==",
|
"integrity": "sha512-4gDntzrifFnCEvyoO8PqyJDmguXgVPxKiIxrBKjIowvL9l+N66196+72XVYR8BBf1Uv1Fgt3bGevJ+sEmxfZzw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"vuex": {
|
||||||
|
"version": "4.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/vuex/-/vuex-4.0.2.tgz",
|
||||||
|
"integrity": "sha512-M6r8uxELjZIK8kTKDGgZTYX/ahzblnzC4isU1tpmEuOIIKmV+TRdc+H4s8ds2NuZ7wpUTdGRzJRtoj+lI+pc0Q==",
|
||||||
|
"requires": {
|
||||||
|
"@vue/devtools-api": "^6.0.0-beta.11"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"vuex-persistedstate": {
|
||||||
|
"version": "4.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/vuex-persistedstate/-/vuex-persistedstate-4.1.0.tgz",
|
||||||
|
"integrity": "sha512-3SkEj4NqwM69ikJdFVw6gObeB0NHyspRYMYkR/EbhR0hbvAKyR5gksVhtAfY1UYuWUOCCA0QNGwv9pOwdj+XUQ==",
|
||||||
|
"requires": {
|
||||||
|
"deepmerge": "^4.2.2",
|
||||||
|
"shvl": "^2.0.3"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"deepmerge": {
|
||||||
|
"version": "4.2.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz",
|
||||||
|
"integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg=="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"watchpack": {
|
"watchpack": {
|
||||||
"version": "1.7.5",
|
"version": "1.7.5",
|
||||||
"resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.7.5.tgz",
|
"resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.7.5.tgz",
|
||||||
|
@ -10,10 +10,15 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@web-eid/web-eid-library": "../../../../web-eid.js/",
|
"@web-eid/web-eid-library": "../../../../web-eid.js/",
|
||||||
"core-js": "^3.6.5",
|
"core-js": "^3.6.5",
|
||||||
"vue": "^3.0.0"
|
"js-cookie": "^3.0.1",
|
||||||
|
"vue": "^3.0.0",
|
||||||
|
"vue-router": "^4.0.0-0",
|
||||||
|
"vuex": "^4.0.2",
|
||||||
|
"vuex-persistedstate": "^4.1.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@vue/cli-plugin-babel": "~4.5.0",
|
"@vue/cli-plugin-babel": "~4.5.0",
|
||||||
|
"@vue/cli-plugin-router": "~4.5.0",
|
||||||
"@vue/cli-service": "~4.5.0",
|
"@vue/cli-service": "~4.5.0",
|
||||||
"@vue/compiler-sfc": "^3.0.0",
|
"@vue/compiler-sfc": "^3.0.0",
|
||||||
"babel-eslint": "^10.1.0",
|
"babel-eslint": "^10.1.0",
|
||||||
|
26
demoBackend/src/demo-website/src/App.vue
Normal file
26
demoBackend/src/demo-website/src/App.vue
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
<template>
|
||||||
|
<router-view/>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
#app {
|
||||||
|
font-family: Avenir, Helvetica, Arial, sans-serif;
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
-moz-osx-font-smoothing: grayscale;
|
||||||
|
text-align: center;
|
||||||
|
color: #2c3e50;
|
||||||
|
}
|
||||||
|
|
||||||
|
#nav {
|
||||||
|
padding: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#nav a {
|
||||||
|
font-weight: bold;
|
||||||
|
color: #2c3e50;
|
||||||
|
}
|
||||||
|
|
||||||
|
#nav a.router-link-exact-active {
|
||||||
|
color: #42b983;
|
||||||
|
}
|
||||||
|
</style>
|
@ -8,7 +8,16 @@
|
|||||||
<p class="text-center">Read more from <a href="https://github.com/TanelOrumaa/Estonian-ID-card-mobile-authenticator-POC">here.</a></p>
|
<p class="text-center">Read more from <a href="https://github.com/TanelOrumaa/Estonian-ID-card-mobile-authenticator-POC">here.</a></p>
|
||||||
</div>
|
</div>
|
||||||
<div class="justify-content-center d-flex">
|
<div class="justify-content-center d-flex">
|
||||||
<button type="button" class="btn btn-lg btn-dark" v-on:click="authenticate">Authenticate</button>
|
|
||||||
|
<button type="button" class="btn loginButton btn-dark" v-on:click="authenticate">
|
||||||
|
<div v-if="loading" class="d-flex justify-content-center">
|
||||||
|
<div class="spinner-border text-light spinner-border-sm" role="status">
|
||||||
|
<span class="visually-hidden">Loading...</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<span v-else>Authenticate</span>
|
||||||
|
</button>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<div class="btn-group-sm d-flex justify-content-center" role="group" aria-label="Basic radio toggle button group">
|
<div class="btn-group-sm d-flex justify-content-center" role="group" aria-label="Basic radio toggle button group">
|
||||||
<input type="radio" class="btn-check" name="btnradio" id="btnCardReader" autocomplete="off" v-on:click="useCardReader">
|
<input type="radio" class="btn-check" name="btnradio" id="btnCardReader" autocomplete="off" v-on:click="useCardReader">
|
||||||
@ -18,7 +27,6 @@
|
|||||||
<label class="btn btn-outline-secondary" for="btnApp">using Android App</label>
|
<label class="btn btn-outline-secondary" for="btnApp">using Android App</label>
|
||||||
</div>
|
</div>
|
||||||
<div id="canvas"></div>
|
<div id="canvas"></div>
|
||||||
<p>Token: {{ csrftoken }}</p>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</template>
|
</template>
|
||||||
@ -26,39 +34,41 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
import * as webeid from '../web-eid.js';
|
import * as webeid from '../web-eid.js';
|
||||||
|
import router from "@/router";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'Login',
|
name: 'LoginComponent',
|
||||||
props: {
|
props: {
|
||||||
"csrftoken": String,
|
"csrftoken": String,
|
||||||
|
"csrfHeaderName": String,
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
useApp: true,
|
useAndroidApp: true,
|
||||||
|
loading: false,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
useApp: function() {
|
useApp: function() {
|
||||||
this.useApp = true;
|
this.useAndroidApp = true;
|
||||||
},
|
},
|
||||||
|
|
||||||
useCardReader: function() {
|
useCardReader: function() {
|
||||||
this.useApp = false;
|
this.useAndroidApp = false;
|
||||||
},
|
},
|
||||||
|
|
||||||
authenticate: async function () {
|
authenticate: async function () {
|
||||||
const csrfToken = document.querySelector('#csrftoken').content;
|
this.loading = true;
|
||||||
const csrfHeaderName = document.querySelector('#csrfheadername').content;
|
|
||||||
|
|
||||||
const options = {
|
const options = {
|
||||||
getAuthChallengeUrl: window.location.origin + "/auth/challenge",
|
getAuthChallengeUrl: window.location.origin + "/auth/challenge",
|
||||||
postAuthTokenUrl: window.location.origin + "/auth/login",
|
postAuthTokenUrl: window.location.origin + "/auth/login",
|
||||||
getAuthSuccessUrl: window.location.origin + "/auth/login",
|
getAuthSuccessUrl: window.location.origin + "/auth/login",
|
||||||
useAuthApp: this.useApp,
|
useAuthApp: this.useAndroidApp,
|
||||||
headers: {
|
headers: {
|
||||||
[csrfHeaderName]: csrfToken
|
[this.csrfHeaderName]: this.csrftoken
|
||||||
}
|
},
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
console.log(options);
|
console.log(options);
|
||||||
@ -66,29 +76,42 @@ export default {
|
|||||||
try {
|
try {
|
||||||
const response = await webeid.authenticate(options);
|
const response = await webeid.authenticate(options);
|
||||||
console.log("Authentication successful! Response:", response);
|
console.log("Authentication successful! Response:", response);
|
||||||
|
this.loading = false;
|
||||||
window.location.href = "/welcome";
|
this.$store.commit("setLoggedIn", true);
|
||||||
|
await router.push("welcome");
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log("Authentication failed! Error:", error);
|
console.log("Authentication failed! Error:", error);
|
||||||
|
alert(error.message);
|
||||||
|
this.loading = false;
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted() {
|
computed: {
|
||||||
fetch("/auth/challenge")
|
isLoggedIn() {
|
||||||
.then((response) => response.text()
|
return this.$store.authenticated;
|
||||||
).then((data) => {
|
},
|
||||||
console.log(data)
|
loading() {
|
||||||
this.msg = data
|
return this.loading;
|
||||||
}
|
}
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
div {
|
.container > div {
|
||||||
margin-top: 2vh;
|
margin-top: 2vh;
|
||||||
}
|
}
|
||||||
|
.loginButton {
|
||||||
|
height: 4vh;
|
||||||
|
width: 20vh;
|
||||||
|
line-height: 3vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loginButton > p {
|
||||||
|
font-size: 3vh;
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
@ -1,18 +1,36 @@
|
|||||||
<template>
|
<template>
|
||||||
<!-- As a heading -->
|
<!-- As a heading -->
|
||||||
<nav class="navbar navbar-light bg-light">
|
<nav class="navbar navbar-dark bg-dark container-fluid flex-row">
|
||||||
<div class="container-fluid">
|
<div class="">
|
||||||
<span class="navbar-brand mb-0 h1">Mobile authentication demo</span>
|
<span class="navbar-brand mb-0 h1">Mobile authentication demo</span>
|
||||||
</div>
|
</div>
|
||||||
|
<div v-if="isLoggedIn" class="nav-item">
|
||||||
|
<button type="button" class="btn btn-light" v-on:click="logOut">Log out</button>
|
||||||
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import router from "@/router";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "Navbar"
|
name: "Navbar",
|
||||||
|
computed: {
|
||||||
|
isLoggedIn() {
|
||||||
|
return this.$store.getters.getAuthenticated;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
logOut: function () {
|
||||||
|
this.$store.commit("setLoggedIn", false);
|
||||||
|
router.push("/");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
nav {
|
||||||
|
height: 5vh;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
30
demoBackend/src/demo-website/src/components/Welcome.vue
Normal file
30
demoBackend/src/demo-website/src/components/Welcome.vue
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
<template>
|
||||||
|
<div class="container container-md d-flex flex-column">
|
||||||
|
<div>
|
||||||
|
<h3 class="text-center">Congratulations, you logged into the site. Log out to try again.</h3>
|
||||||
|
<p class="text-center">Read more from <a href="https://github.com/TanelOrumaa/Estonian-ID-card-mobile-authenticator-POC">here.</a></p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</template>
|
||||||
|
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'WelcomeComponent',
|
||||||
|
props: {
|
||||||
|
"csrftoken": String,
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
isLoggedIn() {
|
||||||
|
return this.$store.getters.getAuthenticated;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
div {
|
||||||
|
margin-top: 2vh;
|
||||||
|
}
|
||||||
|
</style>
|
50
demoBackend/src/demo-website/src/main.js
Normal file
50
demoBackend/src/demo-website/src/main.js
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
import {createApp} from 'vue';
|
||||||
|
import App from './App.vue';
|
||||||
|
import {createStore} from 'vuex'
|
||||||
|
import BootstrapVue3 from 'bootstrap-vue-3'
|
||||||
|
import createPersistedState from "vuex-persistedstate";
|
||||||
|
|
||||||
|
import 'bootstrap/dist/css/bootstrap.css'
|
||||||
|
import 'bootstrap-vue-3/dist/bootstrap-vue-3.css'
|
||||||
|
import router from "./router/index";
|
||||||
|
|
||||||
|
// Create a new store instance.
|
||||||
|
const store = createStore({
|
||||||
|
state() {
|
||||||
|
return {
|
||||||
|
authenticated: false,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mutations: {
|
||||||
|
setLoggedIn(state, isLoggedIn) {
|
||||||
|
console.log("Setting logged in: " + isLoggedIn);
|
||||||
|
state.authenticated = isLoggedIn;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
getters: {
|
||||||
|
getAuthenticated: state => {
|
||||||
|
return state.authenticated;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
plugins: [createPersistedState()],
|
||||||
|
})
|
||||||
|
|
||||||
|
router.beforeEach((to, from, next) => {
|
||||||
|
if (to.matched.some(record => record.meta.requiresAuth)) {
|
||||||
|
// this route requires auth, check if logged in
|
||||||
|
// if not, redirect to login page.
|
||||||
|
if (!store.state.authenticated) {
|
||||||
|
next({name: 'Login'})
|
||||||
|
} else {
|
||||||
|
next() // go to wherever I'm going
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
next() // does not require auth, make sure to always call next()!
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const app = createApp(App)
|
||||||
|
app.use(BootstrapVue3)
|
||||||
|
app.use(router)
|
||||||
|
app.use(store)
|
||||||
|
app.mount('#app')
|
@ -1,10 +0,0 @@
|
|||||||
import { createApp } from 'vue';
|
|
||||||
import App from './App.vue';
|
|
||||||
import BootstrapVue3 from 'bootstrap-vue-3'
|
|
||||||
|
|
||||||
import 'bootstrap/dist/css/bootstrap.css'
|
|
||||||
import 'bootstrap-vue-3/dist/bootstrap-vue-3.css'
|
|
||||||
|
|
||||||
const app = createApp(App)
|
|
||||||
app.use(BootstrapVue3)
|
|
||||||
app.mount('#app')
|
|
@ -1,10 +0,0 @@
|
|||||||
import { createApp } from 'vue';
|
|
||||||
import App from './App.vue';
|
|
||||||
import BootstrapVue3 from 'bootstrap-vue-3'
|
|
||||||
|
|
||||||
import 'bootstrap/dist/css/bootstrap.css'
|
|
||||||
import 'bootstrap-vue-3/dist/bootstrap-vue-3.css'
|
|
||||||
|
|
||||||
const app = createApp(App)
|
|
||||||
app.use(BootstrapVue3)
|
|
||||||
app.mount('#app')
|
|
31
demoBackend/src/demo-website/src/router/index.js
Normal file
31
demoBackend/src/demo-website/src/router/index.js
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
import { createRouter, createWebHistory } from 'vue-router'
|
||||||
|
import Login from '@/views/Login.vue'
|
||||||
|
import Welcome from "@/views/Welcome";
|
||||||
|
|
||||||
|
const routes = [
|
||||||
|
{
|
||||||
|
path: '/',
|
||||||
|
name: 'Login',
|
||||||
|
component: Login,
|
||||||
|
meta: {
|
||||||
|
requiresAuth: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/welcome',
|
||||||
|
name: 'Welcome',
|
||||||
|
component: Welcome,
|
||||||
|
meta: {
|
||||||
|
requiresAuth: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
const router = createRouter({
|
||||||
|
history: createWebHistory(process.env.BASE_URL),
|
||||||
|
routes
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
export default router
|
@ -1,16 +1,16 @@
|
|||||||
<template>
|
<template>
|
||||||
<Navbar/>
|
<Navbar/>
|
||||||
<hello-world v-bind:csrftoken="csrf_token()"/>
|
<LoginComponent/>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import HelloWorld from '../../components/Login.vue'
|
import LoginComponent from '@/components/Login'
|
||||||
import Navbar from "@/components/Navbar";
|
import Navbar from "@/components/Navbar";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'App',
|
name: 'Login',
|
||||||
components: {
|
components: {
|
||||||
HelloWorld,
|
LoginComponent,
|
||||||
Navbar
|
Navbar
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
@ -1,16 +1,16 @@
|
|||||||
<template>
|
<template>
|
||||||
<Navbar/>
|
<Navbar/>
|
||||||
<hello-world v-bind:csrftoken="csrf_token()"/>
|
<WelcomeComponent/>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import HelloWorld from '../../components/Login.vue'
|
import WelcomeComponent from '@/components/Welcome'
|
||||||
import Navbar from "@/components/Navbar";
|
import Navbar from "@/components/Navbar";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'App',
|
name: 'Welcome',
|
||||||
components: {
|
components: {
|
||||||
HelloWorld,
|
WelcomeComponent,
|
||||||
Navbar
|
Navbar
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
@ -8776,7 +8776,9 @@ class WebExtensionService {
|
|||||||
onAckTimeout(pending) {
|
onAckTimeout(pending) {
|
||||||
var _a, _b;
|
var _a, _b;
|
||||||
console.log("onAckTimeout", pending.message.action);
|
console.log("onAckTimeout", pending.message.action);
|
||||||
if (pending.message.authApp && pending.message.authApp == true) {
|
console.log("Pending message");
|
||||||
|
console.log(pending.message.authApp);
|
||||||
|
if (pending.message.useAuthApp && pending.message.useAuthApp == true) {
|
||||||
(_a = pending.reject) === null || _a === void 0 ? void 0 : _a.call(pending, new AuthAppNotInstalledError());
|
(_a = pending.reject) === null || _a === void 0 ? void 0 : _a.call(pending, new AuthAppNotInstalledError());
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -17,20 +17,4 @@ module.exports = {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
pages: {
|
|
||||||
index: {
|
|
||||||
entry: "./src/pages/home/main.js",
|
|
||||||
template: "public/index.html",
|
|
||||||
filename: "index.html",
|
|
||||||
title: "Home",
|
|
||||||
chunks: [],
|
|
||||||
},
|
|
||||||
legal: {
|
|
||||||
entry: "./src/pages/legal/main.js",
|
|
||||||
template: "public/index.html",
|
|
||||||
filename: "legal.html",
|
|
||||||
title: "Legal",
|
|
||||||
chunks:,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
@ -13,6 +13,7 @@ import org.webeid.security.validator.AuthTokenValidator
|
|||||||
import org.webeid.security.validator.AuthTokenValidatorBuilder
|
import org.webeid.security.validator.AuthTokenValidatorBuilder
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.net.URI
|
import java.net.URI
|
||||||
|
import java.net.URL
|
||||||
import java.security.KeyStore
|
import java.security.KeyStore
|
||||||
import java.security.KeyStoreException
|
import java.security.KeyStoreException
|
||||||
import java.security.NoSuchAlgorithmException
|
import java.security.NoSuchAlgorithmException
|
||||||
@ -39,7 +40,7 @@ class ValidationConfiguration {
|
|||||||
|
|
||||||
private val NONCE_TTL_MINUTES: Long = 5
|
private val NONCE_TTL_MINUTES: Long = 5
|
||||||
private val CACHE_NAME = "nonceCache"
|
private val CACHE_NAME = "nonceCache"
|
||||||
private val CERTS_RESOURCE_PATH = "/certs/"
|
private val CERTS_RESOURCE_PATH = "/certs"
|
||||||
private val TRUSTED_CERTIFICATES_JKS = "trusted_certificates.jks"
|
private val TRUSTED_CERTIFICATES_JKS = "trusted_certificates.jks"
|
||||||
private val TRUSTSTORE_PASSWORD = "changeit"
|
private val TRUSTSTORE_PASSWORD = "changeit"
|
||||||
companion object {
|
companion object {
|
||||||
@ -65,6 +66,7 @@ class ValidationConfiguration {
|
|||||||
LOG.warn("Creating new cache.")
|
LOG.warn("Creating new cache.")
|
||||||
cache = createNonceCache(cacheManager)
|
cache = createNonceCache(cacheManager)
|
||||||
}
|
}
|
||||||
|
|
||||||
return cache
|
return cache
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,20 +0,0 @@
|
|||||||
package com.tarkvaratehnika.demobackend.web
|
|
||||||
|
|
||||||
import com.tarkvaratehnika.demobackend.config.ApplicationConfiguration
|
|
||||||
import org.springframework.stereotype.Controller
|
|
||||||
import org.springframework.ui.Model
|
|
||||||
import org.springframework.web.bind.annotation.GetMapping
|
|
||||||
|
|
||||||
@Controller
|
|
||||||
class LoginController {
|
|
||||||
|
|
||||||
@GetMapping
|
|
||||||
fun login(model : Model) : String {
|
|
||||||
model.addAttribute("intentUrl", ApplicationConfiguration.AUTH_APP_LAUNCH_INTENT)
|
|
||||||
model.addAttribute("challengeUrl", ApplicationConfiguration.CHALLENGE_ENDPOINT_URL)
|
|
||||||
model.addAttribute("originUrl", ApplicationConfiguration.WEBSITE_ORIGIN_URL)
|
|
||||||
model.addAttribute("loggedInUrl", "/signature")
|
|
||||||
model.addAttribute("authenticationRequestUrl", ApplicationConfiguration.AUTHENTICATION_ENDPOINT_URL)
|
|
||||||
return "index"
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,20 +0,0 @@
|
|||||||
package com.tarkvaratehnika.demobackend.web
|
|
||||||
|
|
||||||
import com.tarkvaratehnika.demobackend.config.ValidationConfiguration.Companion.ROLE_USER
|
|
||||||
import org.springframework.security.access.prepost.PreAuthorize
|
|
||||||
import org.springframework.stereotype.Controller
|
|
||||||
import org.springframework.ui.Model
|
|
||||||
import org.springframework.web.bind.annotation.GetMapping
|
|
||||||
|
|
||||||
@Controller
|
|
||||||
class SignatureController {
|
|
||||||
|
|
||||||
|
|
||||||
@PreAuthorize("hasAuthority('$ROLE_USER')")
|
|
||||||
@GetMapping("signature")
|
|
||||||
fun signature(model : Model) : String {
|
|
||||||
// model.addAttribute("intentUrl", ApplicationConfiguration.AUTH_APP_LAUNCH_INTENT)
|
|
||||||
// model.addAttribute("challengeUrl", ApplicationConfiguration.CHALLENGE_ENDPOINT_URL)
|
|
||||||
return "signature"
|
|
||||||
}
|
|
||||||
}
|
|
@ -30,7 +30,7 @@ class AuthenticationController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("authentication", produces = [MediaType.APPLICATION_JSON_VALUE])
|
@GetMapping("authentication", produces = [MediaType.APPLICATION_JSON_VALUE])
|
||||||
fun getAuthenticated(@RequestParam challenge: String) : Authentication? {
|
fun getAuthenticated(headers: String) : Authentication? {
|
||||||
val auth = WebEidAuthentication.fromChallenge(challenge)
|
val auth = WebEidAuthentication.fromChallenge(challenge)
|
||||||
if (auth == null) {
|
if (auth == null) {
|
||||||
throw ResponseStatusException(HttpStatus.FORBIDDEN, "Not allowed.")
|
throw ResponseStatusException(HttpStatus.FORBIDDEN, "Not allowed.")
|
||||||
|
@ -1,29 +0,0 @@
|
|||||||
html {
|
|
||||||
font-size: 2vh;
|
|
||||||
}
|
|
||||||
|
|
||||||
.navbar {
|
|
||||||
padding-left: 1rem;
|
|
||||||
padding-right: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cont {
|
|
||||||
display: grid;
|
|
||||||
width: 80%;
|
|
||||||
padding-top: 10%;
|
|
||||||
margin-left: auto;
|
|
||||||
margin-right: auto;
|
|
||||||
justify-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
h4 {
|
|
||||||
margin: 10%;
|
|
||||||
}
|
|
||||||
|
|
||||||
#loginButton {
|
|
||||||
width: 40%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cont > * {
|
|
||||||
margin: 1rem;
|
|
||||||
}
|
|
@ -1,14 +0,0 @@
|
|||||||
window.onload = () => {
|
|
||||||
// Add event listener for login button.
|
|
||||||
let loginButton = document.getElementById("loginButton");
|
|
||||||
|
|
||||||
if (loginButton != null) {
|
|
||||||
loginButton.addEventListener("click", () => {
|
|
||||||
let action = loginButton.getAttribute("data-action");
|
|
||||||
loginButton.setAttribute("disabled", "true");
|
|
||||||
loginButton.textContent = "Logging in";
|
|
||||||
launchAuthApp(action);
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,71 +0,0 @@
|
|||||||
const POLLING_INTERVAL = 1000;
|
|
||||||
const POLLING_RETRIES = 120;
|
|
||||||
|
|
||||||
function launchAuthApp(action) {
|
|
||||||
if (!isAndroid()) {
|
|
||||||
alert("Functionality only available for Android devices.")
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fetch challenge.
|
|
||||||
httpGetAsync(originUrl + challengeUrl, (body) => {
|
|
||||||
let data = JSON.parse(body);
|
|
||||||
let challenge = data.nonce;
|
|
||||||
let intent = createParametrizedIntentUrl(challenge, action, originUrl); // TODO: Error handling.
|
|
||||||
console.log(intent);
|
|
||||||
window.location.href = intent;
|
|
||||||
pollForAuth(POLLING_INTERVAL, challenge);
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
function pollForAuth(timeout, challenge) {
|
|
||||||
console.log("Polling for auth");
|
|
||||||
let encodedChallenge = encodeURIComponent(challenge);
|
|
||||||
let requestUrl = originUrl + authenticationRequestUrl + "?challenge=" + encodedChallenge;
|
|
||||||
let counter = 0;
|
|
||||||
let timer = setInterval(() => {
|
|
||||||
// Fetch authentication object.
|
|
||||||
httpGetAsync(requestUrl, (body) => {
|
|
||||||
console.log(body);
|
|
||||||
// If this is a successful request, stop the polling.
|
|
||||||
clearInterval(timer);
|
|
||||||
window.location.href = originUrl + loggedInUrl;
|
|
||||||
});
|
|
||||||
counter++;
|
|
||||||
if (counter > POLLING_RETRIES) {
|
|
||||||
clearInterval(timer); // Stop polling after some time.
|
|
||||||
let loginErrorAlert = document.getElementById("loginErrorAlert");
|
|
||||||
loginErrorAlert.classList.remove("d-none")
|
|
||||||
}
|
|
||||||
}, timeout)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
function createParametrizedIntentUrl(challenge, action) {
|
|
||||||
if (action == null) {
|
|
||||||
console.error("There has to be an action for intent.")
|
|
||||||
}
|
|
||||||
else if (challenge == null) {
|
|
||||||
console.error("Challenge missing, can't authenticate without it.")
|
|
||||||
} else {
|
|
||||||
return intentUrl + "?" + "action=" + action + "&challenge=" + encodeURIComponent(challenge) + "&authUrl=" + authenticationRequestUrl + "&originUrl=" + originUrl;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function isAndroid() {
|
|
||||||
// Check if using Android device.
|
|
||||||
const ua = navigator.userAgent.toLowerCase();
|
|
||||||
return ua.indexOf("android") > -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
function httpGetAsync(theUrl, callback) {
|
|
||||||
console.log("Sending a request.")
|
|
||||||
const xmlHttp = new XMLHttpRequest();
|
|
||||||
xmlHttp.onreadystatechange = function () {
|
|
||||||
if (xmlHttp.readyState === 4 && xmlHttp.status === 200) {
|
|
||||||
callback(xmlHttp.responseText);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
xmlHttp.open("GET", theUrl, true); // true for asynchronous
|
|
||||||
xmlHttp.send(null);
|
|
||||||
}
|
|
@ -1,38 +0,0 @@
|
|||||||
<!DOCTYPE HTML>
|
|
||||||
<html xmlns:th="http://www.thymeleaf.org">
|
|
||||||
<head>
|
|
||||||
<title>Login</title>
|
|
||||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
|
|
||||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet"
|
|
||||||
integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous">
|
|
||||||
<link th:href="@{/css/main.css}" rel="stylesheet">
|
|
||||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"
|
|
||||||
integrity="sha384-ka7Sk0Gln4gmtz2MlQnikT1wXgYsOg+OMhuP+IlRH9sENBO0LRn5q+8nbTov4+1p"
|
|
||||||
crossorigin="anonymous"></script>
|
|
||||||
<script type="text/javascript" th:src="@{/js/index.js}"></script>
|
|
||||||
<script type="text/javascript" th:src="@{/js/main.js}"></script>
|
|
||||||
<script th:inline="javascript">const originUrl = [[${originUrl}]];
|
|
||||||
const intentUrl = [[${intentUrl}]];
|
|
||||||
const challengeUrl = [[${challengeUrl}]];
|
|
||||||
const loggedInUrl = [[${loggedInUrl}]];
|
|
||||||
const authenticationRequestUrl = [[${authenticationRequestUrl}]]</script> <!-- Pass some values to JS -->
|
|
||||||
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<nav class="navbar navbar-dark bg-dark">
|
|
||||||
<div class="container-fluid">
|
|
||||||
<a class="navbar-brand" href="#">Auth demo webapp</a>
|
|
||||||
</div>
|
|
||||||
</nav>
|
|
||||||
<div class="cont">
|
|
||||||
<h4>Welcome to Estonian ID card mobile authentication demo website. When using a mobile phone, you can log in to the
|
|
||||||
website using your ID card by using the button below.</h4>
|
|
||||||
<h5>Make sure you've installed the authentication app from: <a
|
|
||||||
href="https://github.com/TanelOrumaa/Estonian-ID-card-mobile-authenticator-POC">GitHub</a></h5>
|
|
||||||
<button type="button" class="btn btn-lg btn-secondary" id="loginButton" data-action="auth">Log in</button>
|
|
||||||
<div class="alert alert-danger d-none" role="alert" id="loginErrorAlert">
|
|
||||||
Login failed. Refresh the page to try again.
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
@ -1,35 +0,0 @@
|
|||||||
<!DOCTYPE HTML>
|
|
||||||
<html xmlns:th="http://www.thymeleaf.org">
|
|
||||||
<head>
|
|
||||||
<title>Login</title>
|
|
||||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
|
|
||||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet"
|
|
||||||
integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous">
|
|
||||||
<link th:href="@{/css/main.css}" rel="stylesheet">
|
|
||||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"
|
|
||||||
integrity="sha384-ka7Sk0Gln4gmtz2MlQnikT1wXgYsOg+OMhuP+IlRH9sENBO0LRn5q+8nbTov4+1p"
|
|
||||||
crossorigin="anonymous"></script>
|
|
||||||
<script type="text/javascript" th:src="@{/js/signature.js}"></script>
|
|
||||||
<script type="text/javascript" th:src="@{/js/main.js}"></script>
|
|
||||||
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<nav class="navbar navbar-dark bg-dark">
|
|
||||||
<a class="navbar-brand" href="#">Auth demo web application</a>
|
|
||||||
<ul class="navbar-nav mr-auto">
|
|
||||||
<li class="nav-item">
|
|
||||||
<a href="/" class="btn btn-danger">Log out</a>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</nav>
|
|
||||||
<div class="cont">
|
|
||||||
<h4>Congratulations! You have just authenticated yourself using your mobile phone and your ID-card. You can try to
|
|
||||||
give a signature to a file now.</h4>
|
|
||||||
<h5>This page is still WIP, signing a document feature will be implemented later.</h5>
|
|
||||||
<div class="custom-file">
|
|
||||||
<input type="file" class="custom-file-input" id="customFile">
|
|
||||||
</div>
|
|
||||||
<button type="button" class="btn btn-secondary" id="signFile" data-action="auth">Sign</button>
|
|
||||||
</div>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
Loading…
Reference in New Issue
Block a user