Commit 84f3bb29 authored by Josh Ji's avatar Josh Ji

init commit

parents
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/
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.6/apache-maven-3.8.6-bin.zip
wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar
FROM openjdk:11
COPY target/*.jar app.jar
EXPOSE 8080
ENTRYPOINT java -jar app.jar
\ No newline at end of file
#!/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 /usr/local/etc/mavenrc ] ; then
. /usr/local/etc/mavenrc
fi
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="`\\unset -f command; \\command -v 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/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar"
else
jarUrl="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.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" || rm -f "$wrapperJarPath"
else
wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath" || rm -f "$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 \
$MAVEN_DEBUG_OPTS \
-classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \
"-Dmaven.home=${M2_HOME}" \
"-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \
${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@"
@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 fidoUser 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 "%USERPROFILE%\mavenrc_pre.bat" call "%USERPROFILE%\mavenrc_pre.bat" %*
if exist "%USERPROFILE%\mavenrc_pre.cmd" call "%USERPROFILE%\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/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar"
FOR /F "usebackq 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%/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.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 "%USERPROFILE%\mavenrc_post.bat" call "%USERPROFILE%\mavenrc_post.bat"
if exist "%USERPROFILE%\mavenrc_post.cmd" call "%USERPROFILE%\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%
cmd /C exit /B %ERROR_CODE%
<?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.7.4</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>RPServer</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>RPServer</name>
<description>RPServer</description>
<properties>
<java.version>11</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
<version>2.7.5</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.9.1</version>
</dependency>
<dependency>
<!-- https://mvnrepository.com/artifact/com.webauthn4j/webauthn4j-core -->
<groupId>com.webauthn4j</groupId>
<artifactId>webauthn4j-core</artifactId>
<version>0.20.5.RELEASE</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
package com.example.rpserver;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class RpServerApplication {
public static void main(String[] args) {
SpringApplication.run(RpServerApplication.class, args);
}
}
package com.example.rpserver.controller;
import com.example.rpserver.model.Credential;
import com.example.rpserver.model.Response;
import com.example.rpserver.repository.CredentialRepository;
import com.webauthn4j.data.PublicKeyCredentialDescriptor;
import com.webauthn4j.data.PublicKeyCredentialRequestOptions;
import com.webauthn4j.data.PublicKeyCredentialType;
import com.webauthn4j.data.client.challenge.Challenge;
import com.webauthn4j.data.client.challenge.DefaultChallenge;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import java.util.LinkedList;
import java.util.List;
@RestController
public class GetAssertion {
private static Logger logger = LoggerFactory.getLogger(GetAssertion.class);
@Autowired
private CredentialRepository credentialRepository;
@ResponseBody
@PostMapping("/BeginGetAssertion")
public ResponseEntity<PublicKeyCredentialRequestOptions> beginGetAssertion(@RequestParam("username") String username){
Challenge challenge = new DefaultChallenge();
List<Credential> credentials = credentialRepository.getCredentialsByUserName(username);
LinkedList<PublicKeyCredentialDescriptor> publicKeyCredentialDescriptors = new LinkedList<>();
for(Credential credential : credentials){
PublicKeyCredentialDescriptor publicKeyCredentialDescriptor = new PublicKeyCredentialDescriptor(PublicKeyCredentialType.PUBLIC_KEY, credential.getRawId(), null);
publicKeyCredentialDescriptors.add(publicKeyCredentialDescriptor);
}
PublicKeyCredentialRequestOptions options = new PublicKeyCredentialRequestOptions(challenge, null, "localhost", publicKeyCredentialDescriptors, null, null);
return ResponseEntity.ok(options);
}
@ResponseBody
@PostMapping("/FinishGetAssertion")
public ResponseEntity<Response> finishMakeCredential(@RequestParam("data") String data){
logger.info("data : {}", data);
logger.info("Finish Get Assertion");
return ResponseEntity.ok(new Response(true, "Login Successfully"));
}
}
package com.example.rpserver.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
@Controller
public class Home {
@GetMapping("/")
public String home(){
return "index.html";
}
}
package com.example.rpserver.controller;
import com.example.rpserver.model.Credential;
import com.example.rpserver.model.FidoUser;
import com.example.rpserver.model.Response;
import com.example.rpserver.repository.CredentialRepository;
import com.example.rpserver.repository.UserRepository;
import com.webauthn4j.WebAuthnManager;
import com.webauthn4j.data.*;
import com.webauthn4j.data.attestation.statement.COSEAlgorithmIdentifier;
import com.webauthn4j.data.client.challenge.Challenge;
import com.webauthn4j.data.client.challenge.DefaultChallenge;
import com.webauthn4j.util.Base64Util;
import com.webauthn4j.util.HexUtil;
import com.webauthn4j.validator.attestation.statement.AttestationStatementValidator;
import com.webauthn4j.validator.attestation.trustworthiness.certpath.NullCertPathTrustworthinessValidator;
import com.webauthn4j.validator.attestation.trustworthiness.self.NullSelfAttestationTrustworthinessValidator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.security.SecureRandom;
import java.util.Date;
import java.util.LinkedList;
@RestController
public class MakeCredential {
private final Logger logger = LoggerFactory.getLogger(MakeCredential.class);
private final SecureRandom secureRandom = new SecureRandom();
@Autowired
private UserRepository userRepository;
@Autowired
private CredentialRepository credentialRepository;
@ResponseBody
@PostMapping("/BeginMakeCredential")
public ResponseEntity<PublicKeyCredentialCreationOptions> beginMakeCredential(@RequestParam("username") String username,
@RequestParam("advanced") String advanced,
@RequestParam("advancedOptions") String advancedOptions,
@RequestHeader(HttpHeaders.HOST) String host){
byte[] userIdBytes = new byte[32];
secureRandom.nextBytes(userIdBytes);
PublicKeyCredentialUserEntity userEntity = new PublicKeyCredentialUserEntity(userIdBytes, username, username);
PublicKeyCredentialRpEntity rp = new PublicKeyCredentialRpEntity("localhost", "localhost");
Challenge challenge = new DefaultChallenge();
LinkedList<PublicKeyCredentialParameters> parameters = new LinkedList<>();
parameters.add(new PublicKeyCredentialParameters(PublicKeyCredentialType.PUBLIC_KEY, COSEAlgorithmIdentifier.RS256));
parameters.add(new PublicKeyCredentialParameters(PublicKeyCredentialType.PUBLIC_KEY, COSEAlgorithmIdentifier.ES256));
PublicKeyCredentialCreationOptions options = new PublicKeyCredentialCreationOptions(rp, userEntity, challenge, parameters);
FidoUser fidoUser = new FidoUser(userIdBytes, challenge.toString(), username);
userRepository.save(fidoUser);
return ResponseEntity.ok(options);
}
@ResponseBody
@PostMapping("/FinishMakeCredential")
public ResponseEntity<Response> finishMakeCredential(@RequestParam("identifier") String identifier,
@RequestParam("rawId") byte[] rawId,
@RequestParam("clientDataJSON") byte[] clientDataJSONBytes,
@RequestParam("attestationObject") byte[] attestationObjectBytes){
// apply base64 decoding
clientDataJSONBytes = Base64Util.decode(clientDataJSONBytes);
attestationObjectBytes = Base64Util.decode(attestationObjectBytes);
rawId = Base64Util.decode(rawId);
// logger
logger.info("clientDataJson : {}", new String(clientDataJSONBytes));
logger.info("attestationObject hex String : {}", HexUtil.encodeToString(attestationObjectBytes));
logger.info("rawId hex String : {}", HexUtil.encodeToString(rawId));
RegistrationRequest registrationRequest = new RegistrationRequest(attestationObjectBytes, clientDataJSONBytes);
LinkedList<AttestationStatementValidator> attestationStatementValidators = new LinkedList<>();
WebAuthnManager webAuthnManager = new WebAuthnManager(attestationStatementValidators, new NullCertPathTrustworthinessValidator(), new NullSelfAttestationTrustworthinessValidator());
RegistrationData registrationData = webAuthnManager.parse(registrationRequest);
logger.info(registrationData.toString());
FidoUser user = userRepository.findByChallenge(registrationData.getCollectedClientData().getChallenge().toString());
credentialRepository.save(new Credential(
new Date(),
registrationData.getAttestationObject().getAuthenticatorData().getSignCount(),
identifier,
null,
user.getUserName(),
rawId));
return ResponseEntity.ok().body(new Response(true, "Successfully created credential"));
}
}
package com.example.rpserver.controller;
import com.example.rpserver.model.Credential;
import com.example.rpserver.model.CredentialItem;
import com.example.rpserver.repository.CredentialRepository;
import com.google.gson.JsonArray;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import java.util.LinkedList;
import java.util.List;
@RestController
public class RegisteredKeys {
@Autowired
CredentialRepository credentialRepository;
@ResponseBody
@PostMapping("/RegisteredKeys")
public ResponseEntity<List<CredentialItem>> registeredKeys(){
List<Credential> list = credentialRepository.getAllByIdIsNotNull();
LinkedList<CredentialItem> items = new LinkedList<>();
for(Credential credential : list){
CredentialItem item = new CredentialItem();
item.handle = credential.getIdentifier();
item.base64handle = credential.getIdentifier();
item.userVerificationMethod = credential.getUvMethod();
item.date = credential.getDate().toString();
items.add(item);
}
return ResponseEntity.ok(items);
}
}
package com.example.rpserver.model;
import com.webauthn4j.data.PublicKeyCredentialEntity;
import javax.persistence.*;
import java.util.Date;
@Entity
public class Credential {
public Credential(Date date, long signCount, String identifier, String uvMethod, String userName, byte[] rawId) {
this.date = date;
this.signCount = signCount;
this.identifier = identifier;
this.uvMethod = uvMethod;
this.userName = userName;
this.rawId = rawId;
}
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
public Credential() {
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
@Column(name = "date")
private Date date;
public Date getDate() {
return date;
}
public void setDate(Date date) {
this.date = date;
}
@Column(name = "sign_count", nullable = false)
private long signCount;
public long getSignCount() {
return signCount;
}
public void setSignCount(int signCount) {
this.signCount = signCount;
}
@Column(name = "identifier")
private String identifier;
public String getIdentifier() {
return identifier;
}
public void setIdentifier(String credential) {
this.identifier = credential;
}
@Column(name = "uv_method")
private String uvMethod;
public String getUvMethod() {
return uvMethod;
}
public void setUvMethod(String uvMethod) {
this.uvMethod = uvMethod;
}
@Column(name = "user_name")
private String userName;
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
@Lob
@Column(name = "raw_id")
private byte[] rawId;
public byte[] getRawId() {
return rawId;
}
public void setRawId(byte[] rawId) {
this.rawId = rawId;
}
}
package com.example.rpserver.model;
import java.util.List;
public class CredentialItem {
public String base64handle;
public String date;
public String handle;
public String hasCable;
public String id;
public String name;
public String publicKey;
public List<String> transports;
public String userVerificationMethod;
}
package com.example.rpserver.model;
import javax.persistence.*;
@Entity
@Table
public class FidoUser {
protected FidoUser(){}
public FidoUser(byte[] rawId, String challenge, String userName){
this.rawId = rawId;
this.challenge = challenge;
this.userName = userName;
}
@Column(name = "user_name")
private String userName;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
@Column(name = "challenge", nullable = true)
private String challenge;
public String getChallenge() {
return challenge;
}
public void setChallenge(String challenge) {
this.challenge = challenge;
}
@Column(name = "raw_id")
private byte[] rawId;
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public byte[] getRawId() {
return rawId;
}
public void setRawId(byte[] rawId) {
this.rawId = rawId;
}
}
package com.example.rpserver.model;
public class Response {
private boolean success;
private String message;
public Response(boolean success, String message){
this.success = success;
this.message = message;
}
public boolean isSuccess() {
return success;
}
public void setSuccess(boolean success) {
this.success = success;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
package com.example.rpserver.repository;
import com.example.rpserver.model.Credential;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.List;
public interface CredentialRepository extends JpaRepository<Credential, Long> {
List<Credential> getAllByIdIsNotNull();
List<Credential> getCredentialsByUserName(String username);
}
package com.example.rpserver.repository;
import com.example.rpserver.model.FidoUser;
import org.springframework.data.jpa.repository.JpaRepository;
public interface UserRepository extends JpaRepository<FidoUser, Long> {
public FidoUser findByChallenge(String challenge);
}
\ No newline at end of file
server:
address: 0.0.0.0
port: 8080
spring:
thymeleaf:
cache: false
prefix: classpath:/templates/
suffix:
encoding: UTF-8
mode: HTML
web:
resources:
static-locations: classpath:/static/
cache:
cache control:
no-cache: true
h2:
console:
enabled: true
path: /h2
datasource:
url: jdbc:h2:mem:RPdb
username: sa
password: password
driver-class-name: org.h2.Driver
sql:
init:
mode: always
jpa:
hibernate:
ddl-auto: update
properties:
hibernate:
dialect: org.hibernate.dialect.H2Dialect
\ No newline at end of file
/*
* Copyright 2017 Google Inc.
*
* 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
*
* http://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.
*/
.flex-layout {
display: flex;
height: 100%;
flex-direction: column;
}
.mdl-snackbar {
line-height: 1;
}
.flex-content {
flex: 1 1 auto;
}
.mdl-mini-footer {
padding-top: 3px;
padding-bottom: 3px;
}
#header-buttons {
padding: 0 16px;
flex-wrap: wrap;
min-height: 56px;
height: auto;
}
#header-buttons > button {
flex: 1 1 auto;
margin-bottom: 8px;
}
#header-buttons > .mdl-textfield {
flex: 1 1 auto;
margin-bottom: 8px;
}
#username-input{
min-width: 64px;
width: 100%;
margin-right: 12px;
margin-left: 12px;
}
.fingerprint {
width: 36px;
height: 36px;
background-image: url('data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%20viewBox%3D%220%200%2024%2024%22%3E%3Cpath%20d%3D%22M17.81%204.47c-.08%200-.16-.02-.23-.06C15.66%203.42%2014%203%2012.01%203c-1.98%200-3.86.47-5.57%201.41-.24.13-.54.04-.68-.2-.13-.24-.04-.55.2-.68C7.82%202.52%209.86%202%2012.01%202c2.13%200%203.99.47%206.03%201.52.25.13.34.43.21.67-.09.18-.26.28-.44.28zM3.5%209.72c-.1%200-.2-.03-.29-.09-.23-.16-.28-.47-.12-.7.99-1.4%202.25-2.5%203.75-3.27C9.98%204.04%2014%204.03%2017.15%205.65c1.5.77%202.76%201.86%203.75%203.25.16.22.11.54-.12.7-.23.16-.54.11-.7-.12-.9-1.26-2.04-2.25-3.39-2.94-2.87-1.47-6.54-1.47-9.4.01-1.36.7-2.5%201.7-3.4%202.96-.08.14-.23.21-.39.21zm6.25%2012.07c-.13%200-.26-.05-.35-.15-.87-.87-1.34-1.43-2.01-2.64-.69-1.23-1.05-2.73-1.05-4.34%200-2.97%202.54-5.39%205.66-5.39s5.66%202.42%205.66%205.39c0%20.28-.22.5-.5.5s-.5-.22-.5-.5c0-2.42-2.09-4.39-4.66-4.39-2.57%200-4.66%201.97-4.66%204.39%200%201.44.32%202.77.93%203.85.64%201.15%201.08%201.64%201.85%202.42.19.2.19.51%200%20.71-.11.1-.24.15-.37.15zm7.17-1.85c-1.19%200-2.24-.3-3.1-.89-1.49-1.01-2.38-2.65-2.38-4.39%200-.28.22-.5.5-.5s.5.22.5.5c0%201.41.72%202.74%201.94%203.56.71.48%201.54.71%202.54.71.24%200%20.64-.03%201.04-.1.27-.05.53.13.58.41.05.27-.13.53-.41.58-.57.11-1.07.12-1.21.12zM14.91%2022c-.04%200-.09-.01-.13-.02-1.59-.44-2.63-1.03-3.72-2.1-1.4-1.39-2.17-3.24-2.17-5.22%200-1.62%201.38-2.94%203.08-2.94%201.7%200%203.08%201.32%203.08%202.94%200%201.07.93%201.94%202.08%201.94s2.08-.87%202.08-1.94c0-3.77-3.25-6.83-7.25-6.83-2.84%200-5.44%201.58-6.61%204.03-.39.81-.59%201.76-.59%202.8%200%20.78.07%202.01.67%203.61.1.26-.03.55-.29.64-.26.1-.55-.04-.64-.29-.49-1.31-.73-2.61-.73-3.96%200-1.2.23-2.29.68-3.24%201.33-2.79%204.28-4.6%207.51-4.6%204.55%200%208.25%203.51%208.25%207.83%200%201.62-1.38%202.94-3.08%202.94s-3.08-1.32-3.08-2.94c0-1.07-.93-1.94-2.08-1.94s-2.08.87-2.08%201.94c0%201.71.66%203.31%201.87%204.51.95.94%201.86%201.46%203.27%201.85.27.07.42.35.35.61-.05.23-.26.38-.47.38z%22%20fill%3D%22white%22%2F%3E%3Cpath%20fill%3D%22none%22%20d%3D%22M0%200h24v24H0z%22%2F%3E%3C%2Fsvg%3E%20');
background-size: contain;
background-repeat: no-repeat;
cursor: pointer;
}
.mdl-layout__drawer .mdl-checkbox__label {
vertical-align: top;
}
.mdl-cell {
padding: 30px;
text-align: left;
}
.mdl-card__subtitle-text {
padding-left: .2cm;
font-weight: bold;
}
.mdl-card__supporting-text {
font-style: italic;
overflow-wrap: break-word;
word-wrap: break-word;
padding-bottom: 8px;
padding-top: 8px;
}
.mdl-switch {
width: auto;
}
.mdl-checkbox{
word-wrap:break-word;
word-break: keep-all;
height: auto;
}
.mdl-textfield__input{
padding: 5px;
}
.login-card.mdl-card {
width: 350px;
height: 350px;
margin-left: auto;
margin-right: auto;
margin-top: 20px;
}
.outer-next {
position: absolute;
text-align: right;
bottom: .5cm;
right: .5cm;
}
.login-card.mdl-card__title {
margin-bottom: 2cm;
}
.username-card {
position: relative;
display: block;
margin : 0 auto;
}
.boxes {
position: relative;
height: 50px;
}
.top-box {
position: absolute;
top: 5px;
}
.bottom-box {
position: absolute;
top: 5px;
}
.instructions {
height: 200px;
}
.auth-spinner {
position: relative;
display: block;
margin : 0 auto;
}
.mdl-spinner {
width: 84px;
height: 84px;
}
.mdl-spinner__circle {
border-width: 6px;
}
.signin-title {
height: 75px;
}
.instruction-text {
transition: opacity 0.8s;
}
.gone {
opacity: 0;
}
.next-button.mdl-button {
width: 100px;
}
.advanced-switch {
background-color: white;
padding-top: .2cm;
padding-bottom: .2cm;
padding-left: .2cm;
}
.advanced {
background-color: white;
padding-top: .2cm;
padding-bottom: .2cm;
padding-left: .2cm;
}
.attachment {
font-size: medium;
}
.auth-button {
margin-left: .25cm;
margin-right: .25cm;
}
.make-button {
margin-left: .25cm;
margin-right: .25cm;
}
.isuvpaa-button {
margin-left: .25cm;
margin-right: .25cm;
}
.logout-button {
margin-left: .25cm;
margin-right: .25cm;
color: white;
}
.hidden {
display: none;
}
.activity-bar {
padding-top: 0;
padding-bottom: .2cm;
margin: 0;
}
.page-width {
width: 100%;
}
.active-text {
margin: 0;
background-color: white;
text-align: center;
font-size: medium;
}
/*
* Copyright 2017 Google Inc. All Rights Reserved.
*
* 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
*
* http://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.
*
*/
const $ = q => {
return document.querySelector(q);
};
const show = q => {
$(q).style.display = 'block';
};
const hide = q => {
$(q).style.display = 'none';
};
const isChecked = q => {
return $(q).checked;
};
const onClick = (q, func) => {
$(q).addEventListener('click', func);
};
const onCheck = (q, on, off) => {
$(q).addEventListener('change', () => {
if ($(q).checked) {
on();
} else {
off();
}
});
}
function showErrorMsg(msg) {
// $('#snack-bar').MaterialSnackbar.showSnackbar({
// message: msg,
// timeout: 5000
// });
document.querySelector('#toast-body').textContent=msg;
new bootstrap.Toast(document.querySelector('#toast')).show();
};
function showSuccessMsg(msg) {
// $('#snack-bar').MaterialSnackbar.showSnackbar({
// message: msg,
// timeout: 5000
// });
document.querySelector('#toast-body').textContent=msg.replace('assertion','Authentication');
new bootstrap.Toast(document.querySelector('#toast')).show();
};
function _fetch(url, obj) {
let headers = new Headers({
'Content-Type': 'application/x-www-form-urlencoded'
});
let body;
if (typeof URLSearchParams === "function") {
body = new URLSearchParams();
for (let key in obj) {
body.append(key, obj[key]);
}
// Set body to string value to handle an Edge case
body = body.toString();
} else {
// Add parameters to body manually if browser doesn't support URLSearchParams
body = "";
for (let key in obj) {
body += encodeURIComponent(key) + "=" + encodeURIComponent(obj[key]) + "&";
}
}
return fetch(url, {
method: 'POST',
headers: headers,
credentials: 'include',
body: body
}).then(response => {
if (response.status === 200) {
return response.json();
} else {
throw response.statusText;
}
});
};
function fetchCredentials() {
_fetch('/RegisteredKeys').then(response => {
let credentials = '';
for (let i in response) {
let {handle, base64handle, publicKey, name, date, id, transports, userVerificationMethod} = response[i];
const trimmedHandle = base64handle.replace(/=/g, '');
let buttonId = `delete${i}`;
credentials +=
`<div class="mdl-cell mdl-cell--1-offset-desktop mdl-cell-4-col">
<div class="mdl-card mdl-shadow--4dp" id="${handle}">
<div class="mdl-card__title mdl-card--border">
<label class="mdl-switch mdl-js-switch mdl-js-ripple-effect" for="switch-${trimmedHandle}">
<input type="checkbox" id="switch-${trimmedHandle}" class="mdl-switch__input" checked>
<span class="mdl-switch__label">${name}</span>
</label>
</div>
<div class="mdl-card__supporting-text">Enrolled ${date}</div>
<div class="mdl-card__subtitle-text">Public Key</div>
<div class="mdl-card__supporting-text">${publicKey}</div>
<div class="mdl-card__subtitle-text">Key Handle</div>
<div class="mdl-card__supporting-text">${handle}</div>`;
if (userVerificationMethod) {
credentials +=
`<div class="mdl-card__subtitle-text">User Verification Method</div>
<div class="mdl-card__supporting-text">${userVerificationMethod}</div>`;
}
if (transports) {
credentials +=
`<div class="mdl-card__subtitle-text">Transports</div>
<div class="mdl-card__supporting-text">`;
for (const transport of transports) {
credentials += `<input type="checkbox" id="${transport}${trimmedHandle}" value="${transport}${trimmedHandle}" checked>${transport} &nbsp;`;
}
credentials += `</div>`;
}
credentials +=
`<div class="mdl-card__menu">
<button id="${buttonId}"
class="mdl-button mdl-button--icon mdl-js-button mdl-js-ripple-effect"
title="Removes this credential registration from the server"
>
<i class="material-icons">delete_forever</i>
</button>
</div>
</div>
</div>
`;
}
$('#credentials').innerHTML = credentials;
componentHandler.upgradeAllRegistered();
for (let i in response) {
let {handle, base64handle, publicKey, name, date, id} = response[i];
const trimmedHandle = base64handle.replace(/=/g, '');
onClick(`#delete${i}`, removeCredential(id));
onCheck(`#switch-${trimmedHandle}`, brightenCard(handle), dimCard(handle));
}
});
}
function brightenCard(id) {
return () => {
let card = document.getElementById(id);
card.animate([{
backgroundColor: '#ADADAD'
}, {
backgroundColor: 'white'
}], {
duration: 200,
easing: 'ease-out',
fill: 'forwards'
});
}
}
function dimCard(id) {
return () => {
let card = document.getElementById(id);
card.animate([{
backgroundColor: 'white'
}, {
backgroundColor: '#ADADAD'
}], {
duration: 200,
easing: 'ease-out',
fill: 'forwards'
});
}
}
function removeCredential(id) {
return () => {
_fetch('/RemoveCredential', {
credentialId: id
}).then(() => {
fetchCredentials();
}).catch(err => {
showErrorMsg(`An error occurred during removal [${err.toString()}]`);
});
}
}
function credentialListConversion(list) {
// Filter unchecked credentials
const filteredList = list.filter((element) => {
try {
const base64Id = '#switch-'.concat(element.id.replace(/\+/g, '-')
.replace(/\//g, '_').replace(/=/g, '')).replace(/=/g, '');
if (isChecked(base64Id)) {
return true;
} else {
return false;
}
} catch (e) {
return true;
}
});
return filteredList.map(item => {
const cred = {
type: item.type,
id: strToBin(item.id)
};
if (item.transports) {
const newTransportList = [];
// Filter out unchecked transports
let transport;
for (transport of item.transports) {
try {
// The transport id is the transport name concatenated with the
// corresponding key handle
const base64Id = '#'.concat(transport, item.id.replace(/\+/g, '-')
.replace(/\//g, '_').replace(/=/g, ''));
if (isChecked(base64Id)) {
newTransportList.push(transport);
}
} catch (e) {
}
;
}
if (newTransportList.length) {
cred.transports = newTransportList;
}
}
return cred;
});
}
function serializeUvm(uvm) {
var uvmJson = new Array();
for (var i = 0; i < uvm.length; i++) {
const uvmEntry = {};
uvmEntry.userVerificationMethod = uvm[i][0];
uvmEntry.keyProtectionType = uvm[i][1];
uvmEntry.atchuvmJsonerProtectionType = uvm[i][2];
uvmJson.push(uvmEntry);
}
return uvmJson;
}
function registerNewCredential() {
const advancedOptions = {};
if (isChecked('#switch-rr')) {
advancedOptions.excludeCredentials = isChecked('#switch-rr');
}
if ($('#attachment').value != "none") {
advancedOptions.authenticatorAttachment = $('#attachment').value;
}
if ($('#conveyance').value != "NA") {
advancedOptions.attestationConveyancePreference = $('#conveyance').value;
}
if (isChecked('#switch-rk')) {
advancedOptions.requireResidentKey = isChecked('#switch-rk');
}
if ($('#userVerification').value != "none") {
advancedOptions.userVerification = $('#userVerification').value;
}
makeCredential(advancedOptions);
}
function registerPlatformAuthenticator() {
const advancedOptions = {};
if (isChecked('#switch-rk')) {
advancedOptions.requireResidentKey = isChecked('#switch-rk');
}
if (isChecked('#switch-rr')) {
advancedOptions.excludeCredentials = isChecked('#switch-rr');
}
advancedOptions.userVerification = 'required';
advancedOptions.authenticatorAttachment = 'platform';
if ($('#conveyance').value != "NA") {
advancedOptions.attestationConveyancePreference = $('#conveyance').value;
}
makeCredential(advancedOptions);
}
function makeCredential(advancedOptions) {
show('#active');
let _options;
// Tyler -----------------
let username = document.querySelector('#username').value;
return _fetch('/BeginMakeCredential', {
advanced: true,
advancedOptions: JSON.stringify(advancedOptions),
// Tyler -----------------
username: username
})
.then(options => {
const makeCredentialOptions = {};
_options = options;
makeCredentialOptions.rp = options.rp;
makeCredentialOptions.user = options.user;
makeCredentialOptions.user.id = strToBin(options.user.id);
makeCredentialOptions.challenge = strToBin(options.challenge.value);
makeCredentialOptions.pubKeyCredParams = options.pubKeyCredParams;
// Optional parameters
if ($('#customTimeout').value != '') {
makeCredentialOptions.timeout = $('#customTimeout').value;
}
if ('excludeCredentials' in options && makeCredentialOptions.excludeCredentials != null) {
makeCredentialOptions.excludeCredentials = credentialListConversion(options.excludeCredentials);
}
if ('authenticatorSelection' in options && options.authenticatorSelection != null) {
makeCredentialOptions.authenticatorSelection = options.authenticatorSelection;
}
if ('attestation' in options && options.attestation != null) {
makeCredentialOptions.attestation = options.attestation;
}
if ('extensions' in options && options.extensions != null) {
makeCredentialOptions.extensions = options.extensions;
if (makeCredentialOptions.extensions.cableRegistration) {
makeCredentialOptions.extensions.cableRegistration.rpPublicKey =
strToBin(makeCredentialOptions.extensions.cableRegistration.rpPublicKey);
}
}
if (isChecked('#switch-uvm')) {
makeCredentialOptions.extensions.uvm = true;
}
console.log('sending attestation request:');
console.log(makeCredentialOptions);
if ($('#abortTimeout').value != '') {
authAbortController = new AbortController();
authAbortSignal = authAbortController.signal;
setTimeout(function () {
authAbortController.abort();
}, $('#abortTimeout').value);
return navigator.credentials.create({
"publicKey": makeCredentialOptions,
"signal": authAbortSignal
});
}
return navigator.credentials.create({
publicKey : makeCredentialOptions
});
})
.then(attestation => {
hide('#active');
console.log('received attestation response:');
console.log(attestation);
if ($('#abortTimeout').value != '') {
clearTimeout();
}
const publicKeyCredential = {};
if ('id' in attestation && attestation.id != null) {
publicKeyCredential.id = attestation.id;
}
if ('type' in attestation && attestation.type != null) {
publicKeyCredential.type = attestation.type;
}
if ('rawId' in attestation && attestation.rawId != null ) {
publicKeyCredential.rawId = binToStr(attestation.rawId);
}
if (!attestation.response) {
showErrorMsg("Make Credential response lacking 'response' attribute");
}
const response = {};
response.clientDataJSON = binToStr(attestation.response.clientDataJSON);
response.attestationObject = binToStr(attestation.response.attestationObject);
// Check for included extensions
if (attestation.getClientExtensionResults) {
publicKeyCredential.extensions = attestation.getClientExtensionResults();
if (attestation.getClientExtensionResults().uvm != null) {
publicKeyCredential.uvm = serializeUvm(attestation.getClientExtensionResults().uvm);
}
}
// Check if transports are included in the registration response.
if (attestation.response.getTransports) {
response.transports = attestation.response.getTransports();
}
publicKeyCredential.response = response;
console.log(attestation.rawId)
return _fetch('/FinishMakeCredential', {
identifier: attestation.id,
rawId: binToStr(attestation.rawId),
clientDataJSON: response.clientDataJSON,
attestationObject: response.attestationObject,
});
})
.then(parameters => {
console.log(parameters);
if (parameters && parameters.success) {
showSuccessMsg(parameters.message);
fetchCredentials();
} else {
throw 'Unexpected response received.';
}
})
.catch(err => {
hide('#active');
console.log(err);
console.log(err.toString());
showErrorMsg(`An error occurred during Make Credential operation [${err.toString()}]`);
});
}
function isUVPAA() {
try {
eval(PublicKeyCredential);
} catch (err) {
showErrorMsg(`UVPAA failed: [${err.toString()}]`);
return;
}
if (PublicKeyCredential &&
PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable) {
PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable().then(response => {
if (response === true) {
showSuccessMsg(`User verifying platform authenticator is available.`);
} else {
showErrorMsg(`User verifying platform authenticator is NOT available.`);
}
}).catch(err => {
showErrorMsg(`UVPAA failed: [${err.toString()}]`);
});
} else {
showErrorMsg(`User verifying platform authenticator is not available on this browser.`);
}
}
function getAssertion() {
show('#active');
let _parameters;
let username = document.querySelector('#username').value;
_fetch('/BeginGetAssertion', {username: username})
.then(parameters => {
const requestOptions = {};
_parameters = parameters;
requestOptions.challenge = strToBin(parameters.challenge.value);
if ($('#customTimeout').value !== '') {
requestOptions.timeout = $('#customTimeout').value;
}
if ('rpId' in parameters) {
requestOptions.rpId = parameters.rpId;
}
if ('allowCredentials' in parameters && parameters.allowCredentials != null) {
requestOptions.allowCredentials = credentialListConversion(parameters.allowCredentials);
}
if ($('#userVerification').value !== "none") {
requestOptions.userVerification = $('#userVerification').value;
}
if ('extensions' in parameters && parameters.extensions != null) {
requestOptions.extensions = {};
let cableData = [];
if ('cableAuthentication' in parameters.extensions) {
for (cableElement of parameters.extensions.cableAuthentication) {
let cableExtension = {
'version': cableElement.version,
'clientEid': strToBin(cableElement.clientEid),
'authenticatorEid': strToBin(cableElement.authenticatorEid),
'sessionPreKey': strToBin(cableElement.sessionPreKey),
};
cableData.push(cableExtension);
}
requestOptions.extensions['cableAuthentication'] = cableData;
}
}
if (isChecked('#switch-uvm')) {
if (requestOptions.extensions == null) {
requestOptions.extensions = {};
}
requestOptions.extensions.uvm = true;
}
console.log('sending assertion request:');
console.log(requestOptions);
if ($('#abortTimeout').value !== '') {
authAbortController = new AbortController();
authAbortSignal = authAbortController.signal;
setTimeout(function () {
authAbortController.abort();
}, $('#abortTimeout').value);
return navigator.credentials.get({
"publicKey": requestOptions,
"signal": authAbortSignal
});
}
return navigator.credentials.get({
"publicKey": requestOptions
});
})
.then(assertion => {
hide('#active');
if ($('#abortTimeout').value !== '') {
clearTimeout();
}
console.log('received assertion response:');
console.log(assertion);
const publicKeyCredential = {};
if ('id' in assertion && assertion.id != null) {
publicKeyCredential.id = assertion.id;
}
if ('type' in assertion && assertion.type != null) {
publicKeyCredential.type = assertion.type;
}
if ('rawId' in assertion && assertion.rawId != null) {
publicKeyCredential.rawId = binToStr(assertion.rawId);
}
if (!assertion.response) {
throw "Get assertion response lacking 'response' attribute";
}
if (assertion.getClientExtensionResults) {
if (assertion.getClientExtensionResults().uvm != null) {
publicKeyCredential.uvm = serializeUvm(assertion.getClientExtensionResults().uvm);
}
}
const _response = assertion.response;
publicKeyCredential.response = {
clientDataJSON: binToStr(_response.clientDataJSON),
authenticatorData: binToStr(_response.authenticatorData),
signature: binToStr(_response.signature),
userHandle: binToStr(_response.userHandle)
};
return _fetch('/FinishGetAssertion', {
data: JSON.stringify(publicKeyCredential)
});
})
.then(result => {
console.log(result);
if (result && result.success) {
showSuccessMsg(result.message);
if ('handle' in result) {
setTimeout(function () {
fetchCredentials();
}, 2000);
let card = document.getElementById(result.handle);
let prevColor =
getComputedStyle(card).backgroundColor;
card.animate([{
backgroundColor: '#009688'
}, {
backgroundColor: prevColor
}], {
duration: 2000,
easing: 'ease-out'
});
}
}
})
.catch(err => {
hide('#active');
console.log(err.toString());
showErrorMsg(`An error occurred during Assertion request [${err.toString()}]`);
});
}
function strToBin(str) {
return Uint8Array.from(atob(str), c => c.charCodeAt(0));
}
function binToStr(bin) {
return btoa(new Uint8Array(bin).reduce(
(s, byte) => s + String.fromCharCode(byte), ''
));
}
document.addEventListener('DOMContentLoaded', () => {
let hiddens = Array.from(document.querySelectorAll('.hidden'));
for (let hidden of hiddens) {
hidden.style.display = 'none';
hidden.classList.remove('hidden');
}
if (navigator.credentials && navigator.credentials.create) {
fetchCredentials();
} else {
showErrorMsg('Your browser doesn\'t support WebAuthn');
fetchCredentials();
}
});
window.addEventListener('load', () => {
onClick('#credential-button', registerNewCredential);
// Tyler disable annotation
// onClick('#platform-button', registerPlatformAuthenticator);
onClick('#authenticate-button', getAssertion);
// Tyler disable annotation
// onClick('#isuvpaa-button', isUVPAA);
});
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="shortcut icon" href="favicon.ico">
<link rel="stylesheet"
href="https://code.getmdl.io/1.3.0/material.teal-pink.min.css"/>
<link href="https://fonts.googleapis.com/icon?family=Material+Icons"
rel="stylesheet">
<link href="css/webauthn.css" rel="stylesheet">
<script src="//code.getmdl.io/1.3.0/material.min.js"></script>
<!-- Web Animations polyfill for Edge. -->
<script src="https://rawgit.com/web-animations/web-animations-js/master/web-animations.min.js"></script>
<title>Relying Party Server</title>
</head>
<body>
<div class="mdl-layout mdl-js-layout mdl-layout--fixed-header">
<header class="mdl-layout__header">
<div class="mdl-layout__header-row">
<span class="mdl-layout__title">WebAuthn Demo</span>
</div>
<div id="header-buttons" class="mdl-layout__header-row">
<div id="username-input" class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label">
<input class="mdl-textfield__input" id="username"/>
<label class="mdl-textfield__label" for="username">Username</label>
</div>
<button id="credential-button"
class="mdl-button mdl-js-button mdl-button--raised mdl-button--accent mdl-js-ripple-effect make-button"
title="Calls MakeCredential to register a new credential from a FIDO device"
>
Register
</button>
<button id="authenticate-button"
class="mdl-button mdl-js-button mdl-button--raised mdl-button--accent mdl-js-ripple-effect auth-button"
title="Calls GetAssertion to request an assertion from a previously-registered FIDO device"
>
Login
</button>
</div>
</header>
<div class="mdl-layout__drawer">
<span class="mdl-layout-title">Advanced Options</span>
<div class="mdl-list">
<div class="mdl-list__item">
<label class="mdl-checkbox mdl-js-checkbox" for="switch-rr">
<input type="checkbox" id="switch-rr" class="mdl-checkbox__input">
<span class="mdl-checkbox__label">Prevent Reregistration</span>
</label>
</div>
<div class="mdl-list__item">
<label for="attachment" class="attachment">Attachment Type</label>
<select id="attachment" class="attachment">
<option value="none">N/A</option>
<option value="platform">Platform</option>
<option value="cross-platform">Cross-Platform</option>
</select>
</div>
<div class="mdl-list__item">
<label for="conveyance" class="attachment">Conveyance Preference</label>
<select id="conveyance" class="attachment">
<option value="NA">N/A</option>
<option value="none">None</option>
<option value="indirect">Indirect</option>
<option value="direct">Direct</option>
</select>
</div>
<div class="mdl-list__item">
<label class="mdl-checkbox mdl-js-checkbox mdl-js-ripple-effect" for="switch-rk">
<input type="checkbox" id="switch-rk" class="mdl-checkbox__input">
<span class="mdl-checkbox__label">Require resident key</span>
</label>
</div>
<div class="mdl-list__item">
<label for="userVerification" class="attachment">User Verification</label>
<select id="userVerification" class="attachment">
<option value="none">None</option>
<option value="required">Required</option>
<option value="preferred">Preferred</option>
<option value="discouraged">Discouraged</option>
</select>
</div>
<div class="mdl-list__item">
<label class="mdl-checkbox mdl-js-checkbox mdl-js-ripple-effect" for="switch-uvm">
<input type="checkbox" id="switch-uvm" class="mdl-checkbox__input">
<span class="mdl-checkbox__label">User Verification Method</span>
</label>
</div>
<div class="mdl-list__item">
<div class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label">
<input class="mdl-textfield__input" type="text" pattern="-?[0-9]*(\.[0-9]+)?" id="customTimeout">
<label class="mdl-textfield__label" for="customTimeout">Timeout (milliseconds)</label>
<span class="mdl-textfield__error">Input is not a number!</span>
</div>
</div>
<div class="mdl-list__item">
<div class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label">
<input class="mdl-textfield__input" type="text" pattern="-?[0-9]*(\.[0-9]+)?" id="abortTimeout">
<label class="mdl-textfield__label" for="abortTimeout">AbortTimeout (milliseconds)</label>
<span class="mdl-textfield__error">Input is not a number!</span>
</div>
</div>
</div>
</div>
<main class="flex-layout">
<div class="mdl-layout__content mdl-color--grey-100 flex-content">
<div id="active" class="hidden activity-bar">
<h3 class="active-text">Waiting for fidoUser touch</h3>
<div class="mdl-progress mdl-js-progress mdl-progress__indeterminate page-width"></div>
</div>
<div id="credentials" class="mdl-grid mdl-grid--no-spacing"></div>
</div>
<footer id="github" class="mdl-mini-footer">
<div class="mdl-mini-footer__left-section">
<ul class="mdl-mini-footer__link-list">
</ul>
</div>
<div class="mdl-mini-footer__right-section">
<ul class="mdl-mini-footer__link-list">
<li>
<a href="https://github.com/google/webauthndemo">GitHub</a>
</li>
</ul>
</div>
</footer>
<div id="snack-bar" class="mdl-js-snackbar mdl-snackbar">
<div class="mdl-snackbar__text"></div>
<button class="mdl-snackbar__action" type="button"></button>
</div>
</main>
</div>
<script src="js/webauthn.js"></script>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="shortcut icon" href="favicon.ico">
<link rel="stylesheet" href="https://code.getmdl.io/1.3.0/material.teal-pink.min.css" />
<script src="//code.getmdl.io/1.3.0/material.min.js"></script>
<!-- CSS only -->
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-iYQeCzEYFbKjA/T2uDLTpkwGzCiq6soy8tYaI1GyVh/UjpbCx/TYkiZhlZB6+fzT" crossorigin="anonymous">
<!-- Web Animations polyfill for Edge. -->
<script src="https://rawgit.com/web-animations/web-animations-js/master/web-animations.min.js"></script>
<title>Webauthn Demo</title>
<!-- css -->
<style>
.btn-color {
background-image: linear-gradient(60deg, #64b3f4 0%, #c2e59c 100%);
border-radius: 50px;
color:white;
height: 50px;
border: 0px;
font-size: 16px;
text-align: center;
}
.ipt-text {
border: none;
border-bottom: 2px solid gray;
height: 30px;
width: 100%;
}
label {
color: #718598;
cursor: default;
font-weight: bold;
font-size: 14px;
}
</style>
</head>
<body>
<!-- navbar -->
<nav class="navbar bg-light navbar-light">
<div class="container-fluid">
<!-- <a href="#" class="navbar-brand"><img src="img/logo1.png" alt="Taiwan Tech Logo" class="navbar-brand" style="height: 40pt"></a>-->
<a href="#" class="navbar-brand">FIDO DEMO</a>
<button class="navbar-toggler"
type="button"
data-bs-toggle="collapse"
data-bs-target="#menu">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="menu">
<ul class="navbar-nav">
<li class="nav-item">
<label for="switch-rk" class="nav-link">Resident Key</label>
<input type="checkbox" id="switch-rk">
</li>
<li class="nav-item">
<label for="switch-rr" class="nav-link">Prevent Registration</label>
<input type="checkbox" id="switch-rr">
</li>
<li class="nav-item">
<label for="switch-uvm" class="nav-link">User Verification Method</label>
<input type="checkbox" id="switch-uvm">
</li>
<li class="nav-item">
<label for="attachment" class="nav-link">Attachment Type</label>
<select id="attachment" class="attachment">
<option value="none">N/A</option>
<option value="cross-platform">Cross-Platform</option>
<option value="platform">Platform</option>
</select>
</li>
<li class="nav-item">
<label for="userVerification" class="nav-link">User Verification</label>
<select id="userVerification" class="attachment">
<option value="none">None</option>
<option value="required">Required</option>
<option value="preferred">Preferred</option>
<option value="discouraged">Discouraged</option>
</select>
</li>
<li class="nav-item">
<label for="conveyance" class="nav-link">Conveyance Preference</label>
<select id="conveyance" class="attachment">
<option value="NA">N/A</option>
<option value="none">None</option>
<option value="indirect">Indirect</option>
<option value="direct">Direct</option>
</select>
</li>
<li class="nav-item">
<label class="nav-link" for="customTimeout">Timeout (milliseconds)</label>
<input type="number" id="customTimeout">
</li>
<li class="nav-item">
<label class="nav-link" for="abortTimeout">AbortTimeout (milliseconds)</label>
<input type="number" id="abortTimeout">
</li>
</ul>
</div>
</div>
</nav>
<!-- body-->
<div class="container">
<div class="row justify-content-center">
<div class="col-4" style="width: 400px; padding-top: 150px;">
<div class="card shadow-lg p-5" style="border: 0px;">
<div class="row justify-content-start">
<!-- <div style="padding-top: 50px; padding-bottom: 40px;">-->
<!-- <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="293px" height="50px" viewBox="0 0 293 75"><title>iThome</title><desc>Created with Sketch.</desc><defs><polygon id="path-1" points="0.111529282 0.0703 47.097518 0.0703 47.097518 41 0.111529282 41"></polygon></defs><g id="&#x9996;&#x9801;" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd"><g id="Group-86" transform="translate(-833.000000, -138.500000)"></g><g id="Group-14" transform="translate(2.000000, 7.000000)"><polygon id="Fill-1" fill="#1686C2" points="6.2046 21.6725 -0.0004 60.8355 19.0246 60.8355 25.2256 21.6725"></polygon><polygon id="Fill-2" fill="#1686C2" points="9.7605 1.0004 6.7825 17.5474 25.8105 17.5474 28.7855 1.0004"></polygon><polygon id="Fill-3" fill="#1686C2" points="33.5124 1.0292 30.6464 19.0962 41.8674 18.5822 35.1934 60.5512 55.4024 60.5512 62.0764 18.5822 73.1314 19.0962 75.9924 1.0292"></polygon><g id="Group-13" transform="translate(71.000000, 0.721600)"><path d="M10.0831,0.3033 L29.1111,0.3033 L26.4931,16.8223 L23.8891,27.6613 L24.1861,27.6613 C28.3911,23.4613 32.5441,20.0703 38.0021,20.0703 C47.7331,20.0703 50.0341,27.4433 48.6641,36.0673 L44.8591,60.1133 L26.2721,60.1133 L29.4751,39.9063 C29.8001,37.8423 29.9141,35.2593 27.1851,35.2593 C24.2351,35.2593 23.2221,37.4723 22.8411,39.9063 L19.6361,60.1133 L0.6101,60.1133 L10.0831,0.3033 Z" id="Fill-4" fill="#1686C2"></path><path d="M97.8838,41.0147 C95.8298,53.9927 83.2098,60.9997 70.9708,60.9997 C58.7298,60.9997 48.3368,53.9927 50.3918,41.0147 C52.5618,27.2967 64.5498,20.0707 77.4538,20.0707 C90.3638,20.0707 100.0588,27.2967 97.8838,41.0147 M68.8758,40.7177 C68.3848,43.8127 70.3808,46.1017 73.3298,46.1017 C76.2828,46.1017 79.0028,43.8127 79.4968,40.7177 C79.9978,37.5487 78.2688,34.9657 75.0958,34.9657 C71.9218,34.9657 69.3768,37.5487 68.8758,40.7177" id="Fill-6" fill="#1686C2"></path><path d="M120.3907,27.8829 L120.9847,27.8829 C123.2877,23.0929 128.5607,20.0669 133.5767,20.0669 C139.1847,20.0669 142.6157,23.0929 143.4707,28.4019 L143.9067,28.4019 C146.9907,22.9429 151.0577,20.0669 157.0997,20.0669 C166.4677,20.0669 169.4537,27.2969 168.1097,35.7779 L164.2607,60.1159 L145.8207,60.1159 L148.8937,40.7149 C149.2507,38.4359 149.7227,35.4809 146.9187,35.4809 C144.1897,35.4809 143.7207,38.4359 143.3637,40.7149 L140.2867,60.1159 L121.8497,60.1159 L124.9227,40.7149 C125.2847,38.4359 125.7487,35.4809 122.9527,35.4809 C120.2227,35.4809 119.7577,38.4359 119.3887,40.7149 L116.3207,60.1159 L97.8837,60.1159 L104.0857,20.9539 L122.0787,20.9539 L120.3907,27.8829 Z" id="Fill-8" fill="#1686C2"></path><g id="Group-12" transform="translate(169.000000, 20.000000)"><mask id="mask-2" fill="white"><use xlink:href="#path-1"></use></mask><g id="Clip-11"></g><path d="M18.7911,23.7403 C18.2901,26.9133 18.2471,30.4553 22.2291,30.4553 C24.2181,30.4553 25.9161,29.0523 26.5821,27.2103 L45.0901,27.2103 C41.0841,36.6503 29.8541,41.0003 20.8541,41.0003 C8.5391,41.0003 -1.7419,34.6583 0.3941,21.1583 C2.4501,8.1763 13.7691,0.0703 26.2311,0.0703 C40.1681,0.0703 49.7331,9.0633 46.4491,23.7403 L18.7911,23.7403 Z M29.0001,15.6273 C29.4221,12.9723 27.8881,10.5413 25.1651,10.5413 C22.2821,10.5413 20.5081,12.8983 20.0791,15.6273 L29.0001,15.6273 Z" id="Fill-10" fill="#1686C2" mask="url(#mask-2)"></path></g></g></g></g></svg>-->
<!-- <p style="padding-top: 10px; text-align: center; color: gray;"> For Demo</p>-->
<!-- </div>-->
<div >
<img src="img/logo4.png" style="width: 250px;display:block; margin:auto;" alt="logo2">
<h1 style="text-align: center">FIDO DEMO</h1>
</div>
<!-- <div class="col">-->
<!-- <label for="username" class="form-label">Name</label>-->
<!-- </div>-->
</div>
<div class="row">
<div class="col">
<input type="text" class="form-control" id="username" required>
</div>
</div>
<!-- <div class="row" style="padding-top: 60px;">-->
<div class="col py-3">
<button class="btn-color" id="credential-button" style="width: 100%">Register</button>
</div>
<!-- <div class="col py-3">-->
<button class="btn-color" id="authenticate-button" style="width: 100%">Login</button>
<!-- </div>-->
<!-- </div>-->
<div>
<input type="hidden" id="active">
<!-- <input type="hidden" id="username" value="Josh Ji">-->
<!-- <input type="hidden" id="credential-button">-->
<!-- <input type="hidden" id="switch-rk" >-->
<!-- <input type="hidden" id="switch-rr" >-->
<!-- <input type="hidden" id="switch-uvm">-->
<!-- <input type="hidden" id="userVerification" value="none">-->
<!-- <input type="hidden" id="attachment" value="none">-->
<!-- <input type="hidden" id="conveyance" value="NA">-->
<!-- <input type="hidden" id="customTimeout">-->
<!-- <input type="hidden" id="abortTimeout">-->
</div>
</div>
</div>
</div>
<div id="credentials" class="row">
</div>
<div id="snack-bar" class="mdl-js-snackbar mdl-snackbar">
<div class="mdl-snackbar__text"></div>
<button class="mdl-snackbar__action" type="button"></button>
</div>
</div>
<!-- toast-->
<div class="toast-container position-fixed top-50 start-50 translate-middle p-3" >
<div id="toast" class="toast" role="alert" aria-live="assertive" aria-atomic="true" data-bs-delay="5000">
<div class="toast-header">
Message
</div>
<div id="toast-body" class="toast-body">
Toast Body
</div>
</div>
</div>
<!-- JavaScript Bundle with Popper -->
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js" integrity="sha384-u1OknCvxWvY5kfmNBILK2hRnQC3Pr17a+RTT6rIHI7NnikvbZlHgTPOOmMi466C8" crossorigin="anonymous"></script>
<script src="js/webauthn.js"></script>
<script>
// override fetchCredentials in "js/webauthn.js"
function fetchCredentials(){
}
</script>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Profile</title>
<!-- CSS only -->
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet"
integrity="sha384-iYQeCzEYFbKjA/T2uDLTpkwGzCiq6soy8tYaI1GyVh/UjpbCx/TYkiZhlZB6+fzT" crossorigin="anonymous">
</head>
<body>
<!--navbar-->
<nav class="navbar bg-light navbar-light">
<div class="container-fluid">
<a href="#" class="navbar-brand">
<!-- <img src="img/logo1.png" alt="Taiwan Tech Logo" class="navbar-brand" style="height: 40pt">-->
FIDO DEMO
</a>
<button class="navbar-toggler"
type="button"
data-bs-toggle="collapse"
data-bs-target="#menu">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="menu">
<ul class="navbar-nav">
<li class="nav-item">
<label for="switch-rk" class="nav-link">Resident Key</label>
<input type="checkbox" id="switch-rk">
</li>
<li class="nav-item">
<label for="switch-rr" class="nav-link">Prevent Registration</label>
<input type="checkbox" id="switch-rr">
</li>
<li class="nav-item">
<label for="switch-uvm" class="nav-link">User Verification Method</label>
<input type="checkbox" id="switch-uvm">
</li>
<li class="nav-item">
<label for="attachment" class="nav-link">Attachment Type</label>
<select id="attachment" class="attachment">
<option value="none">N/A</option>
<option value="cross-platform">Cross-Platform</option>
<option value="platform">Platform</option>
</select>
</li>
<li class="nav-item">
<label for="userVerification" class="nav-link">User Verification</label>
<select id="userVerification" class="attachment">
<option value="none">None</option>
<option value="required">Required</option>
<option value="preferred">Preferred</option>
<option value="discouraged">Discouraged</option>
</select>
</li>
<li class="nav-item">
<label for="conveyance" class="nav-link">Conveyance Preference</label>
<select id="conveyance" class="attachment">
<option value="NA">N/A</option>
<option value="none">None</option>
<option value="indirect">Indirect</option>
<option value="direct">Direct</option>
</select>
</li>
<li class="nav-item">
<label class="nav-link" for="customTimeout">Timeout (milliseconds)</label>
<input type="number" id="customTimeout">
</li>
<li class="nav-item">
<label class="nav-link" for="abortTimeout">AbortTimeout (milliseconds)</label>
<input type="number" id="abortTimeout">
</li>
</ul>
</div>
</div>
</nav>
<!-- body-->
<div class="container">
<div class="row justify-content-center">
<div class="col-4" style="width: 400px; padding-top: 150px;">
<div class="card shadow-lg p-5" style="border: 0px;">
<div class="row justify-content-start">
<h2>
Welcome!
</h2>
<hr>
<img src="img/avatar1.jpg" alt="Avatar1">
<h3 class="p-3">Name: Josh Ji</h3>
</div>
</div>
</div>
</div>
</div>
<!-- JavaScript Bundle with Popper -->
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js"
integrity="sha384-u1OknCvxWvY5kfmNBILK2hRnQC3Pr17a+RTT6rIHI7NnikvbZlHgTPOOmMi466C8"
crossorigin="anonymous"></script>
</body>
</html>
\ No newline at end of file
package com.example.rpserver;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
class RpServerApplicationTests {
@Test
void contextLoads() {
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment