mirror of
https://github.com/TanelOrumaa/Estonian-ID-card-mobile-authenticator-POC.git
synced 2024-12-22 12:30:16 +02:00
MOB-42 Added backend server, two frontend webpages and rest endpoints for getting challenge, submitting authentication token and getting authentication object. MOB-21 Added JWT creation, but whole process still needs some work.
This commit is contained in:
parent
44469b8533
commit
f9cd30922e
@ -62,4 +62,9 @@ dependencies {
|
||||
//SecureDataStoring
|
||||
implementation("androidx.security:security-crypto:1.0.0")
|
||||
|
||||
implementation 'io.jsonwebtoken:jjwt-api:0.11.2'
|
||||
runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.11.2',
|
||||
'org.bouncycastle:bcprov-jdk15on:1.60',
|
||||
'io.jsonwebtoken:jjwt-gson:0.11.2'
|
||||
|
||||
}
|
@ -14,6 +14,7 @@ import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.activityViewModels
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import com.tarkvaraprojekt.mobileauthapp.NFC.Comms
|
||||
import com.tarkvaraprojekt.mobileauthapp.auth.Authenticator
|
||||
import com.tarkvaraprojekt.mobileauthapp.databinding.FragmentAuthBinding
|
||||
import com.tarkvaraprojekt.mobileauthapp.model.SmartCardViewModel
|
||||
import java.lang.Exception
|
||||
|
@ -419,6 +419,7 @@ public class Comms {
|
||||
InternalAuthenticate[4] = (byte) (0x1d + 16 * (token.length / 16));
|
||||
InternalAuthenticate[6] = (byte) (0x11 + 16 * (token.length / 16));
|
||||
response = getResponse(token, InternalAuthenticate, "Internal Authenticate");
|
||||
|
||||
if (response[response.length - 2] != (byte) 0x90 || response[response.length - 1] != 0x00) {
|
||||
throw new RuntimeException("Signing the token failed.");
|
||||
}
|
||||
@ -429,6 +430,7 @@ public class Comms {
|
||||
return Arrays.copyOf(signature, indexOfTerminator);
|
||||
}
|
||||
|
||||
|
||||
private byte[] getResponse(byte[] data, byte[] command, String log) throws NoSuchPaddingException, InvalidKeyException, NoSuchAlgorithmException, IllegalBlockSizeException, BadPaddingException, InvalidAlgorithmParameterException, IOException {
|
||||
byte[] response = idCard.transceive(createSecureAPDU(data, command));
|
||||
Log.i(log, Hex.toHexString(response));
|
||||
|
@ -1,22 +1,61 @@
|
||||
package com.tarkvaraprojekt.mobileauthapp.auth
|
||||
|
||||
import android.nfc.tech.IsoDep
|
||||
import android.os.Message
|
||||
import android.util.Log
|
||||
import com.tarkvaraprojekt.mobileauthapp.NFC.Comms
|
||||
import java.math.BigInteger
|
||||
import io.jsonwebtoken.SignatureAlgorithm
|
||||
import org.bouncycastle.util.encoders.Base64
|
||||
import java.nio.charset.StandardCharsets
|
||||
import java.security.MessageDigest
|
||||
import java.time.LocalDateTime
|
||||
import java.time.ZoneOffset
|
||||
import javax.crypto.Mac
|
||||
import kotlin.experimental.and
|
||||
|
||||
class Authenticator(val comms : Comms) {
|
||||
|
||||
public fun authenticate(nonce: BigInteger, challengeUrl: String, pin1: String) {
|
||||
val type = "JWT"
|
||||
val algorithm = "ES384"
|
||||
var iss = "https://self-issued.me" // Will be specified at a later date.
|
||||
val algorithmUsedForSigning = SignatureAlgorithm.ES384
|
||||
|
||||
fun authenticate(challenge: String, originUrl: String, pin1: String) : String {
|
||||
|
||||
// Ask PIN 1 from the user and get the authentication certificate from the ID card.
|
||||
val authenticationCertificate : ByteArray = comms.getCertificate(true);
|
||||
|
||||
// Create the authentication token (OpenID X509)
|
||||
// Encode the certificate in base64.
|
||||
val base64cert = String(Base64.encode(authenticationCertificate))
|
||||
|
||||
// Hash the authentication token.
|
||||
// Get current epoch time.
|
||||
val epoch = LocalDateTime.now(ZoneOffset.UTC).atZone(ZoneOffset.UTC).toEpochSecond()
|
||||
|
||||
// Get expiration time.
|
||||
val exp = LocalDateTime.now(ZoneOffset.UTC).plusSeconds(5 * 60L).atZone(ZoneOffset.UTC).toEpochSecond()
|
||||
|
||||
// Get subject value.
|
||||
val sub = authenticationCertificate[0] // TODO:
|
||||
|
||||
// Get header and claims.
|
||||
val header = """{"typ":"$type","alg":"$algorithm","x5c":"$base64cert"}"""
|
||||
val claims = """{"iat":"$epoch","exp":"$exp","aud":"$originUrl","iss":"$iss","sub":"$sub","nonce":"$challenge","cnf":{"tbh":""}}"""
|
||||
|
||||
var jwt = String(Base64.encode(header.toByteArray(Charsets.UTF_8))) + "." + String(Base64.encode(claims.toByteArray(Charsets.UTF_8)))
|
||||
jwt = jwt.replace("=", "")
|
||||
|
||||
Log.v("JWT", jwt)
|
||||
|
||||
// Send the authentication token hash to the ID card for signing and get signed authentication token as response.
|
||||
val encoded = MessageDigest.getInstance("SHA-384").digest(jwt.toByteArray())
|
||||
val signed = comms.authenticate(pin1, encoded)
|
||||
|
||||
val jws = jwt + "." + String(Base64.encode(signed))
|
||||
|
||||
Log.v("Token", jws)
|
||||
|
||||
// Return the signed authentication token.
|
||||
return jws
|
||||
}
|
||||
|
||||
|
||||
}
|
33
demoBackend/.gitignore
vendored
Normal file
33
demoBackend/.gitignore
vendored
Normal file
@ -0,0 +1,33 @@
|
||||
HELP.md
|
||||
target/
|
||||
!.mvn/wrapper/maven-wrapper.jar
|
||||
!**/src/main/**/target/
|
||||
!**/src/test/**/target/
|
||||
|
||||
### STS ###
|
||||
.apt_generated
|
||||
.classpath
|
||||
.factorypath
|
||||
.project
|
||||
.settings
|
||||
.springBeans
|
||||
.sts4-cache
|
||||
|
||||
### IntelliJ IDEA ###
|
||||
.idea
|
||||
*.iws
|
||||
*.iml
|
||||
*.ipr
|
||||
|
||||
### NetBeans ###
|
||||
/nbproject/private/
|
||||
/nbbuild/
|
||||
/dist/
|
||||
/nbdist/
|
||||
/.nb-gradle/
|
||||
build/
|
||||
!**/src/main/**/build/
|
||||
!**/src/test/**/build/
|
||||
|
||||
### VS Code ###
|
||||
.vscode/
|
118
demoBackend/.mvn/wrapper/MavenWrapperDownloader.java
vendored
Normal file
118
demoBackend/.mvn/wrapper/MavenWrapperDownloader.java
vendored
Normal file
@ -0,0 +1,118 @@
|
||||
/*
|
||||
* Copyright 2007-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import java.net.*;
|
||||
import java.io.*;
|
||||
import java.nio.channels.*;
|
||||
import java.util.Properties;
|
||||
|
||||
public class MavenWrapperDownloader {
|
||||
|
||||
private static final String WRAPPER_VERSION = "0.5.6";
|
||||
/**
|
||||
* Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is provided.
|
||||
*/
|
||||
private static final String DEFAULT_DOWNLOAD_URL = "https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/"
|
||||
+ WRAPPER_VERSION + "/maven-wrapper-" + WRAPPER_VERSION + ".jar";
|
||||
|
||||
/**
|
||||
* Path to the maven-wrapper.properties file, which might contain a downloadUrl property to
|
||||
* use instead of the default one.
|
||||
*/
|
||||
private static final String MAVEN_WRAPPER_PROPERTIES_PATH =
|
||||
".mvn/wrapper/maven-wrapper.properties";
|
||||
|
||||
/**
|
||||
* Path where the maven-wrapper.jar will be saved to.
|
||||
*/
|
||||
private static final String MAVEN_WRAPPER_JAR_PATH =
|
||||
".mvn/wrapper/maven-wrapper.jar";
|
||||
|
||||
/**
|
||||
* Name of the property which should be used to override the default download url for the wrapper.
|
||||
*/
|
||||
private static final String PROPERTY_NAME_WRAPPER_URL = "wrapperUrl";
|
||||
|
||||
public static void main(String args[]) {
|
||||
System.out.println("- Downloader started");
|
||||
File baseDirectory = new File(args[0]);
|
||||
System.out.println("- Using base directory: " + baseDirectory.getAbsolutePath());
|
||||
|
||||
// If the maven-wrapper.properties exists, read it and check if it contains a custom
|
||||
// wrapperUrl parameter.
|
||||
File mavenWrapperPropertyFile = new File(baseDirectory, MAVEN_WRAPPER_PROPERTIES_PATH);
|
||||
String url = DEFAULT_DOWNLOAD_URL;
|
||||
if (mavenWrapperPropertyFile.exists()) {
|
||||
FileInputStream mavenWrapperPropertyFileInputStream = null;
|
||||
try {
|
||||
mavenWrapperPropertyFileInputStream = new FileInputStream(mavenWrapperPropertyFile);
|
||||
Properties mavenWrapperProperties = new Properties();
|
||||
mavenWrapperProperties.load(mavenWrapperPropertyFileInputStream);
|
||||
url = mavenWrapperProperties.getProperty(PROPERTY_NAME_WRAPPER_URL, url);
|
||||
} catch (IOException e) {
|
||||
System.out.println("- ERROR loading '" + MAVEN_WRAPPER_PROPERTIES_PATH + "'");
|
||||
} finally {
|
||||
try {
|
||||
if (mavenWrapperPropertyFileInputStream != null) {
|
||||
mavenWrapperPropertyFileInputStream.close();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
// Ignore ...
|
||||
}
|
||||
}
|
||||
}
|
||||
System.out.println("- Downloading from: " + url);
|
||||
|
||||
File outputFile = new File(baseDirectory.getAbsolutePath(), MAVEN_WRAPPER_JAR_PATH);
|
||||
if (!outputFile.getParentFile().exists()) {
|
||||
if (!outputFile.getParentFile().mkdirs()) {
|
||||
System.out.println(
|
||||
"- ERROR creating output directory '" + outputFile.getParentFile().getAbsolutePath() + "'");
|
||||
}
|
||||
}
|
||||
System.out.println("- Downloading to: " + outputFile.getAbsolutePath());
|
||||
try {
|
||||
downloadFileFromURL(url, outputFile);
|
||||
System.out.println("Done");
|
||||
System.exit(0);
|
||||
} catch (Throwable e) {
|
||||
System.out.println("- Error downloading");
|
||||
e.printStackTrace();
|
||||
System.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
private static void downloadFileFromURL(String urlString, File destination) throws Exception {
|
||||
if (System.getenv("MVNW_USERNAME") != null && System.getenv("MVNW_PASSWORD") != null) {
|
||||
String username = System.getenv("MVNW_USERNAME");
|
||||
char[] password = System.getenv("MVNW_PASSWORD").toCharArray();
|
||||
Authenticator.setDefault(new Authenticator() {
|
||||
@Override
|
||||
protected PasswordAuthentication getPasswordAuthentication() {
|
||||
return new PasswordAuthentication(username, password);
|
||||
}
|
||||
});
|
||||
}
|
||||
URL website = new URL(urlString);
|
||||
ReadableByteChannel rbc;
|
||||
rbc = Channels.newChannel(website.openStream());
|
||||
FileOutputStream fos = new FileOutputStream(destination);
|
||||
fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE);
|
||||
fos.close();
|
||||
rbc.close();
|
||||
}
|
||||
|
||||
}
|
BIN
demoBackend/.mvn/wrapper/maven-wrapper.jar
vendored
Normal file
BIN
demoBackend/.mvn/wrapper/maven-wrapper.jar
vendored
Normal file
Binary file not shown.
2
demoBackend/.mvn/wrapper/maven-wrapper.properties
vendored
Normal file
2
demoBackend/.mvn/wrapper/maven-wrapper.properties
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.3/apache-maven-3.8.3-bin.zip
|
||||
wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar
|
310
demoBackend/mvnw
vendored
Normal file
310
demoBackend/mvnw
vendored
Normal file
@ -0,0 +1,310 @@
|
||||
#!/bin/sh
|
||||
# ----------------------------------------------------------------------------
|
||||
# Licensed to the Apache Software Foundation (ASF) under one
|
||||
# or more contributor license agreements. See the NOTICE file
|
||||
# distributed with this work for additional information
|
||||
# regarding copyright ownership. The ASF licenses this file
|
||||
# to you under the Apache License, Version 2.0 (the
|
||||
# "License"); you may not use this file except in compliance
|
||||
# with the License. You may obtain a copy of the License at
|
||||
#
|
||||
# https://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
# KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# Maven Start Up Batch script
|
||||
#
|
||||
# Required ENV vars:
|
||||
# ------------------
|
||||
# JAVA_HOME - location of a JDK home dir
|
||||
#
|
||||
# Optional ENV vars
|
||||
# -----------------
|
||||
# M2_HOME - location of maven2's installed home dir
|
||||
# MAVEN_OPTS - parameters passed to the Java VM when running Maven
|
||||
# e.g. to debug Maven itself, use
|
||||
# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
|
||||
# MAVEN_SKIP_RC - flag to disable loading of mavenrc files
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
if [ -z "$MAVEN_SKIP_RC" ] ; then
|
||||
|
||||
if [ -f /etc/mavenrc ] ; then
|
||||
. /etc/mavenrc
|
||||
fi
|
||||
|
||||
if [ -f "$HOME/.mavenrc" ] ; then
|
||||
. "$HOME/.mavenrc"
|
||||
fi
|
||||
|
||||
fi
|
||||
|
||||
# OS specific support. $var _must_ be set to either true or false.
|
||||
cygwin=false;
|
||||
darwin=false;
|
||||
mingw=false
|
||||
case "`uname`" in
|
||||
CYGWIN*) cygwin=true ;;
|
||||
MINGW*) mingw=true;;
|
||||
Darwin*) darwin=true
|
||||
# Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home
|
||||
# See https://developer.apple.com/library/mac/qa/qa1170/_index.html
|
||||
if [ -z "$JAVA_HOME" ]; then
|
||||
if [ -x "/usr/libexec/java_home" ]; then
|
||||
export JAVA_HOME="`/usr/libexec/java_home`"
|
||||
else
|
||||
export JAVA_HOME="/Library/Java/Home"
|
||||
fi
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
if [ -z "$JAVA_HOME" ] ; then
|
||||
if [ -r /etc/gentoo-release ] ; then
|
||||
JAVA_HOME=`java-config --jre-home`
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ -z "$M2_HOME" ] ; then
|
||||
## resolve links - $0 may be a link to maven's home
|
||||
PRG="$0"
|
||||
|
||||
# need this for relative symlinks
|
||||
while [ -h "$PRG" ] ; do
|
||||
ls=`ls -ld "$PRG"`
|
||||
link=`expr "$ls" : '.*-> \(.*\)$'`
|
||||
if expr "$link" : '/.*' > /dev/null; then
|
||||
PRG="$link"
|
||||
else
|
||||
PRG="`dirname "$PRG"`/$link"
|
||||
fi
|
||||
done
|
||||
|
||||
saveddir=`pwd`
|
||||
|
||||
M2_HOME=`dirname "$PRG"`/..
|
||||
|
||||
# make it fully qualified
|
||||
M2_HOME=`cd "$M2_HOME" && pwd`
|
||||
|
||||
cd "$saveddir"
|
||||
# echo Using m2 at $M2_HOME
|
||||
fi
|
||||
|
||||
# For Cygwin, ensure paths are in UNIX format before anything is touched
|
||||
if $cygwin ; then
|
||||
[ -n "$M2_HOME" ] &&
|
||||
M2_HOME=`cygpath --unix "$M2_HOME"`
|
||||
[ -n "$JAVA_HOME" ] &&
|
||||
JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
|
||||
[ -n "$CLASSPATH" ] &&
|
||||
CLASSPATH=`cygpath --path --unix "$CLASSPATH"`
|
||||
fi
|
||||
|
||||
# For Mingw, ensure paths are in UNIX format before anything is touched
|
||||
if $mingw ; then
|
||||
[ -n "$M2_HOME" ] &&
|
||||
M2_HOME="`(cd "$M2_HOME"; pwd)`"
|
||||
[ -n "$JAVA_HOME" ] &&
|
||||
JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`"
|
||||
fi
|
||||
|
||||
if [ -z "$JAVA_HOME" ]; then
|
||||
javaExecutable="`which javac`"
|
||||
if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then
|
||||
# readlink(1) is not available as standard on Solaris 10.
|
||||
readLink=`which readlink`
|
||||
if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then
|
||||
if $darwin ; then
|
||||
javaHome="`dirname \"$javaExecutable\"`"
|
||||
javaExecutable="`cd \"$javaHome\" && pwd -P`/javac"
|
||||
else
|
||||
javaExecutable="`readlink -f \"$javaExecutable\"`"
|
||||
fi
|
||||
javaHome="`dirname \"$javaExecutable\"`"
|
||||
javaHome=`expr "$javaHome" : '\(.*\)/bin'`
|
||||
JAVA_HOME="$javaHome"
|
||||
export JAVA_HOME
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ -z "$JAVACMD" ] ; then
|
||||
if [ -n "$JAVA_HOME" ] ; then
|
||||
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||
# IBM's JDK on AIX uses strange locations for the executables
|
||||
JAVACMD="$JAVA_HOME/jre/sh/java"
|
||||
else
|
||||
JAVACMD="$JAVA_HOME/bin/java"
|
||||
fi
|
||||
else
|
||||
JAVACMD="`which java`"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ ! -x "$JAVACMD" ] ; then
|
||||
echo "Error: JAVA_HOME is not defined correctly." >&2
|
||||
echo " We cannot execute $JAVACMD" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -z "$JAVA_HOME" ] ; then
|
||||
echo "Warning: JAVA_HOME environment variable is not set."
|
||||
fi
|
||||
|
||||
CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher
|
||||
|
||||
# traverses directory structure from process work directory to filesystem root
|
||||
# first directory with .mvn subdirectory is considered project base directory
|
||||
find_maven_basedir() {
|
||||
|
||||
if [ -z "$1" ]
|
||||
then
|
||||
echo "Path not specified to find_maven_basedir"
|
||||
return 1
|
||||
fi
|
||||
|
||||
basedir="$1"
|
||||
wdir="$1"
|
||||
while [ "$wdir" != '/' ] ; do
|
||||
if [ -d "$wdir"/.mvn ] ; then
|
||||
basedir=$wdir
|
||||
break
|
||||
fi
|
||||
# workaround for JBEAP-8937 (on Solaris 10/Sparc)
|
||||
if [ -d "${wdir}" ]; then
|
||||
wdir=`cd "$wdir/.."; pwd`
|
||||
fi
|
||||
# end of workaround
|
||||
done
|
||||
echo "${basedir}"
|
||||
}
|
||||
|
||||
# concatenates all lines of a file
|
||||
concat_lines() {
|
||||
if [ -f "$1" ]; then
|
||||
echo "$(tr -s '\n' ' ' < "$1")"
|
||||
fi
|
||||
}
|
||||
|
||||
BASE_DIR=`find_maven_basedir "$(pwd)"`
|
||||
if [ -z "$BASE_DIR" ]; then
|
||||
exit 1;
|
||||
fi
|
||||
|
||||
##########################################################################################
|
||||
# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
|
||||
# This allows using the maven wrapper in projects that prohibit checking in binary data.
|
||||
##########################################################################################
|
||||
if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then
|
||||
if [ "$MVNW_VERBOSE" = true ]; then
|
||||
echo "Found .mvn/wrapper/maven-wrapper.jar"
|
||||
fi
|
||||
else
|
||||
if [ "$MVNW_VERBOSE" = true ]; then
|
||||
echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..."
|
||||
fi
|
||||
if [ -n "$MVNW_REPOURL" ]; then
|
||||
jarUrl="$MVNW_REPOURL/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar"
|
||||
else
|
||||
jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar"
|
||||
fi
|
||||
while IFS="=" read key value; do
|
||||
case "$key" in (wrapperUrl) jarUrl="$value"; break ;;
|
||||
esac
|
||||
done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties"
|
||||
if [ "$MVNW_VERBOSE" = true ]; then
|
||||
echo "Downloading from: $jarUrl"
|
||||
fi
|
||||
wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar"
|
||||
if $cygwin; then
|
||||
wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"`
|
||||
fi
|
||||
|
||||
if command -v wget > /dev/null; then
|
||||
if [ "$MVNW_VERBOSE" = true ]; then
|
||||
echo "Found wget ... using wget"
|
||||
fi
|
||||
if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
|
||||
wget "$jarUrl" -O "$wrapperJarPath"
|
||||
else
|
||||
wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath"
|
||||
fi
|
||||
elif command -v curl > /dev/null; then
|
||||
if [ "$MVNW_VERBOSE" = true ]; then
|
||||
echo "Found curl ... using curl"
|
||||
fi
|
||||
if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
|
||||
curl -o "$wrapperJarPath" "$jarUrl" -f
|
||||
else
|
||||
curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f
|
||||
fi
|
||||
|
||||
else
|
||||
if [ "$MVNW_VERBOSE" = true ]; then
|
||||
echo "Falling back to using Java to download"
|
||||
fi
|
||||
javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java"
|
||||
# For Cygwin, switch paths to Windows format before running javac
|
||||
if $cygwin; then
|
||||
javaClass=`cygpath --path --windows "$javaClass"`
|
||||
fi
|
||||
if [ -e "$javaClass" ]; then
|
||||
if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then
|
||||
if [ "$MVNW_VERBOSE" = true ]; then
|
||||
echo " - Compiling MavenWrapperDownloader.java ..."
|
||||
fi
|
||||
# Compiling the Java class
|
||||
("$JAVA_HOME/bin/javac" "$javaClass")
|
||||
fi
|
||||
if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then
|
||||
# Running the downloader
|
||||
if [ "$MVNW_VERBOSE" = true ]; then
|
||||
echo " - Running MavenWrapperDownloader.java ..."
|
||||
fi
|
||||
("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR")
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
##########################################################################################
|
||||
# End of extension
|
||||
##########################################################################################
|
||||
|
||||
export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"}
|
||||
if [ "$MVNW_VERBOSE" = true ]; then
|
||||
echo $MAVEN_PROJECTBASEDIR
|
||||
fi
|
||||
MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS"
|
||||
|
||||
# For Cygwin, switch paths to Windows format before running java
|
||||
if $cygwin; then
|
||||
[ -n "$M2_HOME" ] &&
|
||||
M2_HOME=`cygpath --path --windows "$M2_HOME"`
|
||||
[ -n "$JAVA_HOME" ] &&
|
||||
JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"`
|
||||
[ -n "$CLASSPATH" ] &&
|
||||
CLASSPATH=`cygpath --path --windows "$CLASSPATH"`
|
||||
[ -n "$MAVEN_PROJECTBASEDIR" ] &&
|
||||
MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"`
|
||||
fi
|
||||
|
||||
# Provide a "standardized" way to retrieve the CLI args that will
|
||||
# work with both Windows and non-Windows executions.
|
||||
MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@"
|
||||
export MAVEN_CMD_LINE_ARGS
|
||||
|
||||
WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
|
||||
|
||||
exec "$JAVACMD" \
|
||||
$MAVEN_OPTS \
|
||||
-classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \
|
||||
"-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \
|
||||
${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@"
|
182
demoBackend/mvnw.cmd
vendored
Normal file
182
demoBackend/mvnw.cmd
vendored
Normal file
@ -0,0 +1,182 @@
|
||||
@REM ----------------------------------------------------------------------------
|
||||
@REM Licensed to the Apache Software Foundation (ASF) under one
|
||||
@REM or more contributor license agreements. See the NOTICE file
|
||||
@REM distributed with this work for additional information
|
||||
@REM regarding copyright ownership. The ASF licenses this file
|
||||
@REM to you under the Apache License, Version 2.0 (the
|
||||
@REM "License"); you may not use this file except in compliance
|
||||
@REM with the License. You may obtain a copy of the License at
|
||||
@REM
|
||||
@REM https://www.apache.org/licenses/LICENSE-2.0
|
||||
@REM
|
||||
@REM Unless required by applicable law or agreed to in writing,
|
||||
@REM software distributed under the License is distributed on an
|
||||
@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
@REM KIND, either express or implied. See the License for the
|
||||
@REM specific language governing permissions and limitations
|
||||
@REM under the License.
|
||||
@REM ----------------------------------------------------------------------------
|
||||
|
||||
@REM ----------------------------------------------------------------------------
|
||||
@REM Maven Start Up Batch script
|
||||
@REM
|
||||
@REM Required ENV vars:
|
||||
@REM JAVA_HOME - location of a JDK home dir
|
||||
@REM
|
||||
@REM Optional ENV vars
|
||||
@REM M2_HOME - location of maven2's installed home dir
|
||||
@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands
|
||||
@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending
|
||||
@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven
|
||||
@REM e.g. to debug Maven itself, use
|
||||
@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
|
||||
@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files
|
||||
@REM ----------------------------------------------------------------------------
|
||||
|
||||
@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on'
|
||||
@echo off
|
||||
@REM set title of command window
|
||||
title %0
|
||||
@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on'
|
||||
@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO%
|
||||
|
||||
@REM set %HOME% to equivalent of $HOME
|
||||
if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%")
|
||||
|
||||
@REM Execute a user defined script before this one
|
||||
if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre
|
||||
@REM check for pre script, once with legacy .bat ending and once with .cmd ending
|
||||
if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat"
|
||||
if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd"
|
||||
:skipRcPre
|
||||
|
||||
@setlocal
|
||||
|
||||
set ERROR_CODE=0
|
||||
|
||||
@REM To isolate internal variables from possible post scripts, we use another setlocal
|
||||
@setlocal
|
||||
|
||||
@REM ==== START VALIDATION ====
|
||||
if not "%JAVA_HOME%" == "" goto OkJHome
|
||||
|
||||
echo.
|
||||
echo Error: JAVA_HOME not found in your environment. >&2
|
||||
echo Please set the JAVA_HOME variable in your environment to match the >&2
|
||||
echo location of your Java installation. >&2
|
||||
echo.
|
||||
goto error
|
||||
|
||||
:OkJHome
|
||||
if exist "%JAVA_HOME%\bin\java.exe" goto init
|
||||
|
||||
echo.
|
||||
echo Error: JAVA_HOME is set to an invalid directory. >&2
|
||||
echo JAVA_HOME = "%JAVA_HOME%" >&2
|
||||
echo Please set the JAVA_HOME variable in your environment to match the >&2
|
||||
echo location of your Java installation. >&2
|
||||
echo.
|
||||
goto error
|
||||
|
||||
@REM ==== END VALIDATION ====
|
||||
|
||||
:init
|
||||
|
||||
@REM Find the project base dir, i.e. the directory that contains the folder ".mvn".
|
||||
@REM Fallback to current working directory if not found.
|
||||
|
||||
set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR%
|
||||
IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir
|
||||
|
||||
set EXEC_DIR=%CD%
|
||||
set WDIR=%EXEC_DIR%
|
||||
:findBaseDir
|
||||
IF EXIST "%WDIR%"\.mvn goto baseDirFound
|
||||
cd ..
|
||||
IF "%WDIR%"=="%CD%" goto baseDirNotFound
|
||||
set WDIR=%CD%
|
||||
goto findBaseDir
|
||||
|
||||
:baseDirFound
|
||||
set MAVEN_PROJECTBASEDIR=%WDIR%
|
||||
cd "%EXEC_DIR%"
|
||||
goto endDetectBaseDir
|
||||
|
||||
:baseDirNotFound
|
||||
set MAVEN_PROJECTBASEDIR=%EXEC_DIR%
|
||||
cd "%EXEC_DIR%"
|
||||
|
||||
:endDetectBaseDir
|
||||
|
||||
IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig
|
||||
|
||||
@setlocal EnableExtensions EnableDelayedExpansion
|
||||
for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a
|
||||
@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS%
|
||||
|
||||
:endReadAdditionalConfig
|
||||
|
||||
SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe"
|
||||
set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar"
|
||||
set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
|
||||
|
||||
set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar"
|
||||
|
||||
FOR /F "tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO (
|
||||
IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B
|
||||
)
|
||||
|
||||
@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
|
||||
@REM This allows using the maven wrapper in projects that prohibit checking in binary data.
|
||||
if exist %WRAPPER_JAR% (
|
||||
if "%MVNW_VERBOSE%" == "true" (
|
||||
echo Found %WRAPPER_JAR%
|
||||
)
|
||||
) else (
|
||||
if not "%MVNW_REPOURL%" == "" (
|
||||
SET DOWNLOAD_URL="%MVNW_REPOURL%/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar"
|
||||
)
|
||||
if "%MVNW_VERBOSE%" == "true" (
|
||||
echo Couldn't find %WRAPPER_JAR%, downloading it ...
|
||||
echo Downloading from: %DOWNLOAD_URL%
|
||||
)
|
||||
|
||||
powershell -Command "&{"^
|
||||
"$webclient = new-object System.Net.WebClient;"^
|
||||
"if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^
|
||||
"$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^
|
||||
"}"^
|
||||
"[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^
|
||||
"}"
|
||||
if "%MVNW_VERBOSE%" == "true" (
|
||||
echo Finished downloading %WRAPPER_JAR%
|
||||
)
|
||||
)
|
||||
@REM End of extension
|
||||
|
||||
@REM Provide a "standardized" way to retrieve the CLI args that will
|
||||
@REM work with both Windows and non-Windows executions.
|
||||
set MAVEN_CMD_LINE_ARGS=%*
|
||||
|
||||
%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %*
|
||||
if ERRORLEVEL 1 goto error
|
||||
goto end
|
||||
|
||||
:error
|
||||
set ERROR_CODE=1
|
||||
|
||||
:end
|
||||
@endlocal & set ERROR_CODE=%ERROR_CODE%
|
||||
|
||||
if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost
|
||||
@REM check for post script, once with legacy .bat ending and once with .cmd ending
|
||||
if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat"
|
||||
if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd"
|
||||
:skipRcPost
|
||||
|
||||
@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on'
|
||||
if "%MAVEN_BATCH_PAUSE%" == "on" pause
|
||||
|
||||
if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE%
|
||||
|
||||
exit /B %ERROR_CODE%
|
116
demoBackend/pom.xml
Normal file
116
demoBackend/pom.xml
Normal file
@ -0,0 +1,116 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>2.5.6</version>
|
||||
<relativePath/> <!-- lookup parent from repository -->
|
||||
</parent>
|
||||
<groupId>com.tarkvaratehnika</groupId>
|
||||
<artifactId>demoBackend</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<name>demoBackend</name>
|
||||
<description>demoBackend</description>
|
||||
<properties>
|
||||
<java.version>11</java.version>
|
||||
<kotlin.version>1.5.31</kotlin.version>
|
||||
<caffeine.version>2.8.5</caffeine.version>
|
||||
<javaxcache.version>1.1.1</javaxcache.version>
|
||||
</properties>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.module</groupId>
|
||||
<artifactId>jackson-module-kotlin</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jetbrains.kotlin</groupId>
|
||||
<artifactId>kotlin-reflect</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jetbrains.kotlin</groupId>
|
||||
<artifactId>kotlin-stdlib-jdk8</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.webeid.security</groupId>
|
||||
<artifactId>authtoken-validation</artifactId>
|
||||
<version>1.2.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>javax.cache</groupId>
|
||||
<artifactId>cache-api</artifactId>
|
||||
<version>${javaxcache.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.github.ben-manes.caffeine</groupId>
|
||||
<artifactId>caffeine</artifactId>
|
||||
<version>${caffeine.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.github.ben-manes.caffeine</groupId>
|
||||
<artifactId>jcache</artifactId>
|
||||
<version>${caffeine.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-thymeleaf</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-security</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.security</groupId>
|
||||
<artifactId>spring-security-config</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>gitlab</id>
|
||||
<url>https://gitlab.com/api/v4/projects/19948337/packages/maven</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
|
||||
<build>
|
||||
<sourceDirectory>${project.basedir}/src/main/kotlin</sourceDirectory>
|
||||
<testSourceDirectory>${project.basedir}/src/test/kotlin</testSourceDirectory>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.jetbrains.kotlin</groupId>
|
||||
<artifactId>kotlin-maven-plugin</artifactId>
|
||||
<configuration>
|
||||
<args>
|
||||
<arg>-Xjsr305=strict</arg>
|
||||
</args>
|
||||
<compilerPlugins>
|
||||
<plugin>spring</plugin>
|
||||
</compilerPlugins>
|
||||
</configuration>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.jetbrains.kotlin</groupId>
|
||||
<artifactId>kotlin-maven-allopen</artifactId>
|
||||
<version>${kotlin.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
@ -0,0 +1,13 @@
|
||||
package com.tarkvaratehnika.demobackend
|
||||
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication
|
||||
import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration
|
||||
import org.springframework.boot.runApplication
|
||||
|
||||
@SpringBootApplication(exclude=[SecurityAutoConfiguration::class])
|
||||
class DemoBackendApplication
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
runApplication<DemoBackendApplication>(*args)
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
package com.tarkvaratehnika.demobackend.config
|
||||
|
||||
class ApplicationConfiguration {
|
||||
|
||||
companion object {
|
||||
// URL for intent, do not edit.
|
||||
val AUTH_APP_LAUNCH_INTENT = "authapp://start/"
|
||||
// Endpoint for challenge.
|
||||
val CHALLENGE_ENDPOINT_URL = "/auth/challenge"
|
||||
// Endpoint for authentication
|
||||
val AUTHENTICATION_ENDPOINT_URL = "/auth/authentication"
|
||||
// URL for application. Use ngrok for HTTPS (or a tool of your own choice) and put the HTTPS link here.
|
||||
val WEBSITE_ORIGIN_URL = "https://6bb0-85-253-195-252.ngrok.io"
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,152 @@
|
||||
package com.tarkvaratehnika.demobackend.config
|
||||
|
||||
import com.github.benmanes.caffeine.jcache.spi.CaffeineCachingProvider
|
||||
import org.springframework.context.annotation.Bean
|
||||
import org.springframework.context.annotation.Configuration
|
||||
import org.springframework.core.io.support.PathMatchingResourcePatternResolver
|
||||
import org.webeid.security.exceptions.JceException
|
||||
import org.webeid.security.nonce.NonceGenerator
|
||||
import org.webeid.security.nonce.NonceGeneratorBuilder
|
||||
import org.webeid.security.validator.AuthTokenValidator
|
||||
import org.webeid.security.validator.AuthTokenValidatorBuilder
|
||||
import java.io.IOException
|
||||
import java.net.URI
|
||||
import java.security.KeyStore
|
||||
import java.security.KeyStoreException
|
||||
import java.security.NoSuchAlgorithmException
|
||||
import java.security.cert.CertificateException
|
||||
import java.security.cert.CertificateFactory
|
||||
import java.security.cert.X509Certificate
|
||||
import java.time.ZonedDateTime
|
||||
import java.util.concurrent.TimeUnit
|
||||
import javax.cache.Cache
|
||||
import javax.cache.CacheManager
|
||||
import javax.cache.Caching
|
||||
import javax.cache.configuration.CompleteConfiguration
|
||||
import javax.cache.configuration.FactoryBuilder
|
||||
import javax.cache.configuration.MutableConfiguration
|
||||
import javax.cache.expiry.CreatedExpiryPolicy
|
||||
import javax.cache.expiry.Duration
|
||||
|
||||
@Configuration
|
||||
class ValidationConfiguration {
|
||||
|
||||
private val NONCE_TTL_MINUTES: Long = 5
|
||||
private val CACHE_NAME = "nonceCache"
|
||||
private val CERTS_RESOURCE_PATH = "/certs/"
|
||||
private val TRUSTED_CERTIFICATES_JKS = "trusted_certificates.jks"
|
||||
private val TRUSTSTORE_PASSWORD = "changeit"
|
||||
|
||||
@Bean
|
||||
fun cacheManager(): CacheManager {
|
||||
return Caching.getCachingProvider(CaffeineCachingProvider::class.java.name).cacheManager
|
||||
}
|
||||
|
||||
@Bean
|
||||
fun nonceCache(): Cache<String, ZonedDateTime>? {
|
||||
val cacheManager: CacheManager = cacheManager()
|
||||
var cache =
|
||||
cacheManager.getCache<String?, ZonedDateTime?>(CACHE_NAME)
|
||||
if (cache == null) {
|
||||
cache = createNonceCache(cacheManager)
|
||||
}
|
||||
return cache
|
||||
}
|
||||
|
||||
@Bean
|
||||
fun generator(): NonceGenerator? {
|
||||
return NonceGeneratorBuilder()
|
||||
.withNonceTtl(java.time.Duration.ofMinutes(NONCE_TTL_MINUTES))
|
||||
.withNonceCache(nonceCache())
|
||||
.build()
|
||||
}
|
||||
|
||||
private fun createNonceCache(cacheManager: CacheManager): Cache<String?, ZonedDateTime?>? {
|
||||
val cacheConfig: CompleteConfiguration<String, ZonedDateTime> = MutableConfiguration<String, ZonedDateTime>()
|
||||
.setTypes(String::class.java, ZonedDateTime::class.java)
|
||||
.setExpiryPolicyFactory(
|
||||
FactoryBuilder.factoryOf(
|
||||
CreatedExpiryPolicy(
|
||||
Duration(
|
||||
TimeUnit.MINUTES,
|
||||
NONCE_TTL_MINUTES + 1
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
return cacheManager.createCache(CACHE_NAME, cacheConfig)
|
||||
}
|
||||
|
||||
@Bean
|
||||
fun loadTrustedCACertificatesFromCerFiles() : Array<X509Certificate> {
|
||||
val caCertificates = ArrayList<X509Certificate>()
|
||||
|
||||
try {
|
||||
val certFactory = CertificateFactory.getInstance("X.509")
|
||||
val resolver = PathMatchingResourcePatternResolver()
|
||||
val resources = resolver.getResources("$CERTS_RESOURCE_PATH/*.cer")
|
||||
|
||||
resources.forEach { resource ->
|
||||
val caCertificate = certFactory.generateCertificate(resource.inputStream) as X509Certificate
|
||||
caCertificates.add(caCertificate)
|
||||
}
|
||||
} catch (e : Exception) {
|
||||
when (e){
|
||||
is CertificateException, is IOException -> {
|
||||
throw RuntimeException("Error initializing trusted CA certificates. $e")
|
||||
}
|
||||
}
|
||||
}
|
||||
return caCertificates.toTypedArray()
|
||||
}
|
||||
|
||||
@Bean
|
||||
fun loadTrustedCACertificatesFromTrustStore() : Array<X509Certificate> {
|
||||
val caCertificates = ArrayList<X509Certificate>()
|
||||
|
||||
ValidationConfiguration::class.java.getResourceAsStream("$CERTS_RESOURCE_PATH/$TRUSTED_CERTIFICATES_JKS").use { inputStream ->
|
||||
try {
|
||||
if (inputStream == null) {
|
||||
// No truststore files found.
|
||||
return arrayOf()
|
||||
}
|
||||
|
||||
val keyStore = KeyStore.getInstance(KeyStore.getDefaultType())
|
||||
keyStore.load(inputStream, TRUSTSTORE_PASSWORD.toCharArray())
|
||||
val aliases = keyStore.aliases()
|
||||
|
||||
while (aliases.hasMoreElements()) {
|
||||
val alias = aliases.nextElement()
|
||||
val certificate = keyStore.getCertificate(alias) as X509Certificate
|
||||
caCertificates.add(certificate)
|
||||
}
|
||||
|
||||
|
||||
} catch (e : Exception) {
|
||||
when (e) {
|
||||
is IOException, is CertificateException, is KeyStoreException, is NoSuchAlgorithmException -> {
|
||||
throw RuntimeException("Error initializing trusted CA certificates from trust store. $e")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return caCertificates.toTypedArray()
|
||||
}
|
||||
|
||||
@Bean
|
||||
fun validator() : AuthTokenValidator {
|
||||
try {
|
||||
return AuthTokenValidatorBuilder()
|
||||
.withSiteOrigin(URI.create(ApplicationConfiguration.WEBSITE_ORIGIN_URL))
|
||||
.withNonceCache(nonceCache())
|
||||
.withTrustedCertificateAuthorities(*loadTrustedCACertificatesFromCerFiles())
|
||||
.withTrustedCertificateAuthorities(*loadTrustedCACertificatesFromTrustStore())
|
||||
.build()
|
||||
} catch (e : JceException) {
|
||||
throw RuntimeException("Error building the Web eID auth token validator.", e)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,3 @@
|
||||
package com.tarkvaratehnika.demobackend.dto
|
||||
|
||||
data class ChallengeDto(val nonce : String)
|
@ -0,0 +1,6 @@
|
||||
package com.tarkvaratehnika.demobackend.security
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty
|
||||
|
||||
class AuthTokenDTO (val token : String, val challenge : String) {
|
||||
}
|
@ -0,0 +1,71 @@
|
||||
/*
|
||||
* Copyright (c) 2020, 2021 The Web eID Project
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package com.tarkvaratehnika.demobackend.security
|
||||
|
||||
import com.tarkvaratehnika.demobackend.config.ValidationConfiguration
|
||||
import org.springframework.security.authentication.AuthenticationServiceException
|
||||
import org.springframework.security.core.Authentication
|
||||
import org.springframework.security.core.AuthenticationException
|
||||
import org.springframework.security.core.GrantedAuthority
|
||||
import org.springframework.security.core.authority.SimpleGrantedAuthority
|
||||
import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken
|
||||
import org.springframework.stereotype.Component
|
||||
import org.webeid.security.exceptions.TokenValidationException
|
||||
import org.webeid.security.validator.AuthTokenValidator
|
||||
import java.security.cert.CertificateEncodingException
|
||||
import java.security.cert.X509Certificate
|
||||
|
||||
|
||||
@Component
|
||||
class AuthTokenDTOAuthenticationProvider {
|
||||
|
||||
companion object {
|
||||
const val ROLE_USER : String = "ROLE_USER"
|
||||
}
|
||||
private val USER_ROLE: GrantedAuthority = SimpleGrantedAuthority(ROLE_USER)
|
||||
|
||||
|
||||
val tokenValidator: AuthTokenValidator = ValidationConfiguration().validator()
|
||||
|
||||
@Throws(AuthenticationException::class)
|
||||
fun authenticate(auth : Authentication) : Authentication {
|
||||
val authentication = auth as PreAuthenticatedAuthenticationToken
|
||||
val token = (authentication.credentials as AuthTokenDTO).token
|
||||
val challenge = (authentication.credentials as AuthTokenDTO).challenge
|
||||
|
||||
val authorities = arrayListOf<GrantedAuthority>()
|
||||
authorities.add(USER_ROLE)
|
||||
|
||||
try {
|
||||
val userCertificate: X509Certificate = tokenValidator.validate(token)
|
||||
return WebEidAuthentication.fromCertificate(userCertificate, authorities, challenge)
|
||||
} catch (e : TokenValidationException) {
|
||||
// Validation failed.
|
||||
throw AuthenticationServiceException("Token validation failed. " + e.message)
|
||||
} catch (e : CertificateEncodingException) {
|
||||
// Failed to extract subject fields from the certificate.
|
||||
throw AuthenticationServiceException("Incorrect certificate subject fields: " + e.message)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,100 @@
|
||||
/*
|
||||
* Copyright (c) 2020, 2021 The Web eID Project
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package com.tarkvaratehnika.demobackend.security
|
||||
|
||||
import org.webeid.security.certificate.CertificateData
|
||||
|
||||
import org.springframework.security.core.Authentication
|
||||
import org.springframework.security.core.GrantedAuthority
|
||||
import org.springframework.security.core.authority.SimpleGrantedAuthority
|
||||
import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken
|
||||
import java.security.cert.X509Certificate
|
||||
import java.util.*
|
||||
import java.util.concurrent.ThreadLocalRandom
|
||||
import kotlin.collections.ArrayList
|
||||
import kotlin.math.log
|
||||
|
||||
class WebEidAuthentication(
|
||||
private val principalName: String,
|
||||
private val idCode: String,
|
||||
private val authorities: ArrayList<GrantedAuthority>
|
||||
) : PreAuthenticatedAuthenticationToken(principalName, idCode, authorities), Authentication {
|
||||
|
||||
// Companion object is for static functions.
|
||||
companion object {
|
||||
|
||||
private val loggedInUsers = HashMap<String, Authentication>()
|
||||
|
||||
fun fromCertificate(
|
||||
userCertificate: X509Certificate,
|
||||
authorities: ArrayList<GrantedAuthority>,
|
||||
challenge: String
|
||||
): Authentication {
|
||||
val principalName = getPrincipalNameFromCertificate(userCertificate)
|
||||
val idCode = Objects.requireNonNull(CertificateData.getSubjectIdCode(userCertificate))
|
||||
val authentication = WebEidAuthentication(principalName, idCode, authorities)
|
||||
loggedInUsers[challenge] = authentication
|
||||
return authentication
|
||||
}
|
||||
|
||||
/**
|
||||
* Function for getting a Spring authentication object by supplying a challenge.
|
||||
* TODO: Figure out a more secure solution in the future.
|
||||
*/
|
||||
fun fromChallenge(challenge: String): Authentication? {
|
||||
// if (ThreadLocalRandom.current().nextFloat() < 0.5f) { // TODO: For testing.
|
||||
// return null
|
||||
// }
|
||||
val auth = loggedInUsers[challenge]
|
||||
if (auth != null) {
|
||||
// If challenge is valid, delete the authentication object from the map (so this can only be fetched once).
|
||||
loggedInUsers.remove(challenge)
|
||||
} else {
|
||||
return null
|
||||
}
|
||||
return auth
|
||||
}
|
||||
|
||||
// // TODO: DELETE
|
||||
//
|
||||
// const val ROLE_USER: String = "ROLE_USER"
|
||||
// private val USER_ROLE: GrantedAuthority = SimpleGrantedAuthority(ROLE_USER)
|
||||
//
|
||||
// fun addAuth(challenge: String) {
|
||||
// val authorities = arrayListOf<GrantedAuthority>()
|
||||
// authorities.add(USER_ROLE)
|
||||
// val auth = WebEidAuthentication("Somename", "11111111111", authorities)
|
||||
// loggedInUsers[challenge] = auth
|
||||
// }
|
||||
//
|
||||
//
|
||||
// // TODO: DELETE UNTIL
|
||||
|
||||
private fun getPrincipalNameFromCertificate(userCertificate: X509Certificate): String {
|
||||
return Objects.requireNonNull(CertificateData.getSubjectGivenName(userCertificate)) + " " +
|
||||
Objects.requireNonNull(CertificateData.getSubjectSurname(userCertificate))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
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"
|
||||
}
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
package com.tarkvaratehnika.demobackend.web
|
||||
|
||||
import com.tarkvaratehnika.demobackend.security.AuthTokenDTOAuthenticationProvider.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"
|
||||
}
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
package com.tarkvaratehnika.demobackend.web.rest
|
||||
|
||||
import com.tarkvaratehnika.demobackend.security.AuthTokenDTO
|
||||
import com.tarkvaratehnika.demobackend.security.AuthTokenDTOAuthenticationProvider
|
||||
import com.tarkvaratehnika.demobackend.security.WebEidAuthentication
|
||||
import org.slf4j.LoggerFactory
|
||||
import org.springframework.http.HttpStatus
|
||||
import org.springframework.http.MediaType
|
||||
import org.springframework.security.core.Authentication
|
||||
import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken
|
||||
import org.springframework.web.bind.annotation.*
|
||||
import org.springframework.web.server.ResponseStatusException
|
||||
|
||||
@RestController
|
||||
@RequestMapping("auth")
|
||||
class AuthenticationController {
|
||||
|
||||
private val LOG = LoggerFactory.getLogger(AuthenticationController::class.java)
|
||||
|
||||
|
||||
@PostMapping("authentication", consumes = [MediaType.APPLICATION_JSON_VALUE], produces = [MediaType.APPLICATION_JSON_VALUE])
|
||||
fun authenticate(@RequestBody authToken : AuthTokenDTO): Authentication {
|
||||
// Create Spring Security Authentication object with supplied token as credentials.
|
||||
val auth = PreAuthenticatedAuthenticationToken(null, authToken)
|
||||
|
||||
// Return authentication object if success.
|
||||
return AuthTokenDTOAuthenticationProvider().authenticate(auth)
|
||||
}
|
||||
|
||||
@GetMapping("authentication", produces = [MediaType.APPLICATION_JSON_VALUE])
|
||||
fun getAuthenticated(@RequestParam challenge: String) : Authentication? {
|
||||
val auth = WebEidAuthentication.fromChallenge(challenge)
|
||||
if (auth == null) {
|
||||
throw ResponseStatusException(HttpStatus.FORBIDDEN, "Not allowed.")
|
||||
}
|
||||
return auth
|
||||
}
|
||||
}
|
@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright (c) 2020, 2021 The Web eID Project
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package com.tarkvaratehnika.demobackend.web.rest
|
||||
|
||||
import com.tarkvaratehnika.demobackend.dto.ChallengeDto
|
||||
import com.tarkvaratehnika.demobackend.security.WebEidAuthentication
|
||||
import org.springframework.web.bind.annotation.GetMapping
|
||||
import org.springframework.web.bind.annotation.RequestMapping
|
||||
import org.springframework.web.bind.annotation.RestController
|
||||
import org.webeid.security.nonce.NonceGenerator
|
||||
|
||||
@RestController
|
||||
@RequestMapping("auth")
|
||||
class ChallengeController (val nonceGenerator: NonceGenerator) {
|
||||
|
||||
|
||||
@GetMapping("challenge")
|
||||
fun challenge(): ChallengeDto {
|
||||
val challengeDto = ChallengeDto(nonceGenerator.generateAndStoreNonce())
|
||||
// WebEidAuthentication.addAuth(challengeDto.nonce) // For testing.
|
||||
return challengeDto
|
||||
}
|
||||
|
||||
}
|
||||
|
1
demoBackend/src/main/resources/application.properties
Normal file
1
demoBackend/src/main/resources/application.properties
Normal file
@ -0,0 +1 @@
|
||||
|
BIN
demoBackend/src/main/resources/certs/ESTEID-SK_2015.cer
Normal file
BIN
demoBackend/src/main/resources/certs/ESTEID-SK_2015.cer
Normal file
Binary file not shown.
BIN
demoBackend/src/main/resources/certs/ESTEID2018.cer
Normal file
BIN
demoBackend/src/main/resources/certs/ESTEID2018.cer
Normal file
Binary file not shown.
BIN
demoBackend/src/main/resources/certs/trusted_certificates.jks
Normal file
BIN
demoBackend/src/main/resources/certs/trusted_certificates.jks
Normal file
Binary file not shown.
20
demoBackend/src/main/resources/static/css/main.css
Normal file
20
demoBackend/src/main/resources/static/css/main.css
Normal file
@ -0,0 +1,20 @@
|
||||
.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;
|
||||
}
|
14
demoBackend/src/main/resources/static/js/index.js
Normal file
14
demoBackend/src/main/resources/static/js/index.js
Normal file
@ -0,0 +1,14 @@
|
||||
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);
|
||||
})
|
||||
}
|
||||
}
|
||||
|
67
demoBackend/src/main/resources/static/js/main.js
Normal file
67
demoBackend/src/main/resources/static/js/main.js
Normal file
@ -0,0 +1,67 @@
|
||||
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); // TODO: Error handling.
|
||||
console.log(intent);
|
||||
window.location.href = intent;
|
||||
pollForAuth(POLLING_INTERVAL, challenge);
|
||||
})
|
||||
}
|
||||
|
||||
function pollForAuth(timeout, challenge) {
|
||||
console.log("Polling for auth");
|
||||
let requestUrl = originUrl + authenticationRequestUrl + "?challenge=" + challenge;
|
||||
|
||||
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.")
|
||||
}
|
||||
return intentUrl + "?" + "action=" + action + (challenge != null ? "&challenge=" + challenge : "");
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
38
demoBackend/src/main/resources/templates/index.html
Normal file
38
demoBackend/src/main/resources/templates/index.html
Normal file
@ -0,0 +1,38 @@
|
||||
<!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 web application</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-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>
|
42
demoBackend/src/main/resources/templates/signature.html
Normal file
42
demoBackend/src/main/resources/templates/signature.html
Normal file
@ -0,0 +1,42 @@
|
||||
<!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">
|
||||
<div class="container-fluid">
|
||||
<a class="navbar-brand" href="#">Auth demo web application</a>
|
||||
</div>
|
||||
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
|
||||
<div class="collapse navbar-collapse" id="navbarSupportedContent">
|
||||
<ul class="navbar-nav mr-auto">
|
||||
<li class="nav-item active">
|
||||
<a class="nav-link" href="#">Log out<span class="sr-only">(current)</span></a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</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="input-group mb-3">
|
||||
<div class="custom-file">
|
||||
<input type="file" class="custom-file-input" id="inputGroupFile01">
|
||||
<label class="custom-file-label" for="inputGroupFile01">Choose file</label>
|
||||
</div>
|
||||
</div>
|
||||
<button type="button" class="btn btn-secondary" id="signFile" data-action="auth">Sign</button>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
@ -0,0 +1,13 @@
|
||||
package com.tarkvaratehnika.demobackend
|
||||
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.springframework.boot.test.context.SpringBootTest
|
||||
|
||||
@SpringBootTest
|
||||
class DemoBackendApplicationTests {
|
||||
|
||||
@Test
|
||||
fun contextLoads() {
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue
Block a user