6 Commits

Author SHA1 Message Date
Henrik Lepson
2c5430977d Updated main readme 2021-11-17 09:31:30 +02:00
Henrik Lepson
68a7db2e77 Created a readme for TestMobileApp 2021-11-17 09:26:21 +02:00
Henrik Lepson
a4caf24a35 MOB-41 fixed some remaining issues 2021-11-17 09:15:29 +02:00
TanelOrumaa
5b70a8f997 MOB-42 Added log out button to backend, fixed issue with challenge for test app 2021-11-16 21:30:58 +02:00
Henrik Lepson
168c9be010 fixed app not closing bug, when started from website 2021-11-14 10:13:40 +02:00
TanelOrumaa
636beeb7f3 MOB-42 Fixed token authentication issues (wrong library version, cache getting recreated every request, origin in wrong form) 2021-11-11 21:47:27 +02:00
28 changed files with 234 additions and 108 deletions

View File

@@ -69,6 +69,7 @@ class AuthFragment : Fragment() {
goToTheStart()
}
}.start()
//binding!!.nextButton.visibility = View.INVISIBLE
binding!!.nextButton.setOnClickListener { goToNextFragment() }
binding!!.cancelButton.setOnClickListener { goToTheStart() }
val adapter = NfcAdapter.getDefaultAdapter(activity)
@@ -90,7 +91,7 @@ class AuthFragment : Fragment() {
if (args.auth) {
val jws = Authenticator(comms).authenticate(
intentParameters.challenge,
intentParameters.authUrl,
intentParameters.origin,
viewModel.userPin
)
intentParameters.setToken(jws)
@@ -140,8 +141,7 @@ class AuthFragment : Fragment() {
} else {
if (!args.mobile) {
//Currently for some reason the activity is not killed entirely. Must be looked into further.
requireActivity().finish()
exitProcess(0)
requireActivity().finishAndRemoveTask()
} else {
val resultIntent = Intent()
requireActivity().setResult(AppCompatActivity.RESULT_CANCELED, resultIntent)

View File

@@ -127,10 +127,14 @@ class CanFragment : Fragment() {
// TODO: Needs special handling when the app is launched with intent. Temporary solution at the moment.
if (args.saving) {
findNavController().navigate(R.id.action_canFragment_to_settingsFragment)
} else if (args.auth) {
} else if (args.auth || args.mobile) {
if (args.mobile) {
val resultIntent = Intent()
requireActivity().setResult(AppCompatActivity.RESULT_CANCELED, resultIntent)
requireActivity().finish()
} else {
requireActivity().finishAndRemoveTask()
}
} else {
findNavController().navigate(R.id.action_canFragment_to_homeFragment)
}

View File

@@ -14,6 +14,7 @@ import com.tarkvaraprojekt.mobileauthapp.databinding.FragmentHomeBinding
import com.tarkvaraprojekt.mobileauthapp.model.ParametersViewModel
import com.tarkvaraprojekt.mobileauthapp.model.SmartCardViewModel
import java.lang.Exception
import java.net.URLDecoder
/**
* HomeFragment is only shown to the user when then the user launches the application. When the application
@@ -30,6 +31,7 @@ class HomeFragment : Fragment() {
private var binding: FragmentHomeBinding? = null
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
@@ -56,11 +58,14 @@ class HomeFragment : Fragment() {
// We use !! because we want an exception when something is not right.
intentParams.setChallenge(requireActivity().intent.getStringExtra("challenge")!!)
intentParams.setAuthUrl(requireActivity().intent.getStringExtra("authUrl")!!)
intentParams.setOrigin(requireActivity().intent.getStringExtra("originUrl")!!)
} else { //Website
// Currently the test website won't send the authUrl parameter
//Log.i("intentDebugging", requireActivity().intent.data.toString())
intentParams.setChallenge(requireActivity().intent.data!!.getQueryParameter("challenge")!!)
var challenge = requireActivity().intent.data!!.getQueryParameter("challenge")!!
// TODO: Since due to encoding plus gets converted to space, temporary solution is to replace it back.
challenge = challenge.replace(" ", "+")
intentParams.setChallenge(challenge)
intentParams.setAuthUrl(requireActivity().intent.data!!.getQueryParameter("authUrl")!!)
intentParams.setOrigin(requireActivity().intent.data!!.getQueryParameter("originUrl")!!)
}
} catch (e: Exception) {
// There was a problem with parameters, which means that authentication is not possible.

View File

@@ -128,10 +128,14 @@ class PinFragment : Fragment() {
private fun goToTheStart() {
if (args.saving) {
findNavController().navigate(R.id.action_canFragment_to_settingsFragment)
} else if (args.auth) {
} else if (args.auth || args.mobile) {
if (args.mobile) {
val resultIntent = Intent()
requireActivity().setResult(AppCompatActivity.RESULT_CANCELED, resultIntent)
requireActivity().finish()
} else {
requireActivity().finishAndRemoveTask()
}
} else {
findNavController().navigate(R.id.action_canFragment_to_homeFragment)
}

View File

@@ -14,15 +14,6 @@ import com.google.gson.JsonObject
import com.koushikdutta.ion.Ion
import com.tarkvaraprojekt.mobileauthapp.databinding.FragmentResultBinding
import com.tarkvaraprojekt.mobileauthapp.model.ParametersViewModel
import com.tarkvaraprojekt.mobileauthapp.model.SmartCardViewModel
import com.tarkvaraprojekt.mobileauthapp.network.BASE_URL
import com.tarkvaraprojekt.mobileauthapp.network.TokenApi
import com.tarkvaraprojekt.mobileauthapp.network.TokenApiService
import com.tarkvaraprojekt.mobileauthapp.network.TokenItem
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlin.system.exitProcess
/**
* ResultFragment is used to create a JWT and to send response to the website/application
@@ -48,13 +39,9 @@ class ResultFragment : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding!!.resultBackButton.setOnClickListener {
// if (args.mobile) {
// createResponse()
// }
binding!!.resultBackButton.visibility = View.GONE
postToken()
}
}
/**
* Makes a POST request to the backend server with a tokenItem
@@ -64,40 +51,40 @@ class ResultFragment : Fragment() {
json.addProperty("token", paramsModel.token)
json.addProperty("challenge", paramsModel.challenge)
Ion.getDefault(activity).getConscryptMiddleware().enable(false)
Ion.getDefault(activity).conscryptMiddleware.enable(false)
Ion.with(activity)
.load("https://6bb0-85-253-195-252.ngrok.io/auth/authentication")
.load(paramsModel.origin + paramsModel.authUrl)
.setJsonObjectBody(json)
.asJsonObject()
.setCallback { e, result ->
// do stuff with the result or error
Log.i("Log thingy", result.toString())
if (result == null) {
// TODO: Set auth message failed and close the app
Log.i("Log thingy fail", "result was null")
if (args.mobile) {
createResponse(false)
} else {
requireActivity().finishAndRemoveTask()
}
} else {
Log.i("POST request response", result.toString())
if (args.mobile) {
createResponse(true, result.toString(), paramsModel.token)
} else {
requireActivity().finishAndRemoveTask()
}
}
}
// CoroutineScope(Dispatchers.Default).launch {
// val response = TokenApi.retrofitService.postToken(jsonBody)
// Log.v("Response", response.message())
// if (response.isSuccessful) {
// //Success scenario here
// } else {
// //Failure scenario here
// if (args.mobile) {
// createResponse(false)
// } else {
// //Currently for some reason the activity is not killed entirely. Must be looked into further.
// requireActivity().finish()
// exitProcess(0)
// }
// }
// }
}
/**
* Only used when the MobileAuthApp was launched by an app. Not for website use.
*/
private fun createResponse(success: Boolean = true) {
private fun createResponse(success: Boolean = true, result: String = "noResult", token: String = "noToken") {
val responseCode = if (success) AppCompatActivity.RESULT_OK else AppCompatActivity.RESULT_CANCELED
val resultIntent = Intent()
resultIntent.putExtra("result", result)
resultIntent.putExtra("token", token)
requireActivity().setResult(responseCode, resultIntent)
requireActivity().finish()
}

View File

@@ -8,7 +8,7 @@ import java.security.MessageDigest
import java.time.LocalDateTime
import java.time.ZoneOffset
class Authenticator(val comms : Comms) {
class Authenticator(val comms: Comms) {
val type = "JWT"
val algorithm = "ES384"
@@ -36,7 +36,7 @@ class Authenticator(val comms : Comms) {
// 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":""}}"""
"""{"iat":"$epoch","exp":"$exp","aud":["$originUrl"],"iss":"$iss","sub":"$sub","nonce":"$challenge","cnf":{"tbh":""}}"""
val jwt = base64Encode(header.toByteArray(Charsets.UTF_8)) + "." + base64Encode(
claims.toByteArray(Charsets.UTF_8)
@@ -51,7 +51,7 @@ class Authenticator(val comms : Comms) {
return jwt + "." + base64Encode(signed)
}
fun base64Encode(bytes: ByteArray) : String? {
fun base64Encode(bytes: ByteArray): String? {
val encoded = java.util.Base64.getUrlEncoder().encodeToString(bytes)
return encoded.replace("=", "")
}

View File

@@ -13,6 +13,9 @@ class ParametersViewModel: ViewModel() {
private var _token: String = ""
val token get() = _token
private var _origin: String = ""
val origin get() = _origin
fun setChallenge(newChallenge: String) {
_challenge = newChallenge
}
@@ -24,4 +27,8 @@ class ParametersViewModel: ViewModel() {
fun setToken(newToken: String) {
_token = newToken
}
fun setOrigin(newOrigin: String) {
_origin = newOrigin
}
}

View File

@@ -56,8 +56,8 @@
<string name="clear_button">FORGET</string>
<!-- string resources for ResultFragment layout-->
<string name="result_text">See Fragment vastutab vastuse tagastamise eest.</string>
<string name="result_info">Hiljem sulgeb rakendus automaatselt.</string>
<string name="result_text">Controlling the created token</string>
<string name="result_info">Wait for the app to close</string>
<!-- menu -->
<string name="menu_settings_title">Settings</string>

View File

@@ -55,8 +55,8 @@
<string name="gender_label">SUGU</string>
<!-- string resources for ResultFragment layout-->
<string name="result_text">See Fragment vastutab vastuse tagastamise eest.</string>
<string name="result_info">Hiljem sulgeb rakendus automaatselt.</string>
<string name="result_text">Tulemust kontrollitakse</string>
<string name="result_info">Rakendus sulgeb ennast ise</string>
<!-- menu -->
<string name="menu_settings_title">Seaded</string>

View File

@@ -55,8 +55,8 @@
<string name="clear_button">FORGET</string>
<!-- string resources for ResultFragment layout-->
<string name="result_text">See Fragment vastutab vastuse tagastamise eest.</string>
<string name="result_info">Hiljem sulgeb rakendus automaatselt.</string>
<string name="result_text">Controlling the created token</string>
<string name="result_info">Wait for the app to close</string>
<!-- menu -->
<string name="menu_settings_title">Settings</string>

View File

@@ -20,6 +20,9 @@ More info about installing third party applications on the Android phones: https
**NB! Before using the application make sure that the NFC is enabled on the phone, otherwise information can not be read from the ID card.**
### Testing the application
The project comes with a test mobile application and a test web application that can be used to try the MobileAuthApp authentication feature even if you don't have any web applications or mobile applications that require user authentication. Both projects come with a README file that help with a setup.
### Wiki pages relevant for the "Software project" subject
* [Project Vision](https://github.com/TanelOrumaa/Estonian-ID-card-mobile-authenticator-POC/wiki/Project-Vision) *last updated on 10.10*
* [Release Notes](https://github.com/TanelOrumaa/Estonian-ID-card-mobile-authenticator-POC/wiki/Release-notes) *last updated for iteration3 on 08.11*

10
TestMobileApp/README.md Normal file
View File

@@ -0,0 +1,10 @@
# TestMobileApp overview
### The purpose
The TestMobileApp was created in order to demonstrate how a different application on the Android smartphone could use the MobileAuthApp for user authentication purposes.
### Installing the application
The application installation process is the same as with the MobileAuthApp. Check the guide in the project's [main readme file](https://github.com/TanelOrumaa/Estonian-ID-card-mobile-authenticator-POC#installing-the-application-on-the-phone).
### Using the application
In order to use this application a backend server must be running that can issue challenges and verify the token created by the MobileAuthApp.
Use demoBackend application that is included in the project. Follow the demoBackend setup guide and once you have a backend running take the https address of the backend
and add it in the TestMobileApp's MainActivty.kt file as the new value for the constant variable BASE_URL (this is easly noticeable in the class as it is pointed out with a comment).
Now the app can be used.

View File

@@ -5,10 +5,18 @@ import android.content.Intent
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import android.view.View
import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.contract.ActivityResultContracts
import com.example.testmobileapp.databinding.ActivityMainBinding
import com.google.gson.JsonObject
import com.koushikdutta.ion.Ion
import org.json.JSONObject
/**
* Base url where the requests should be made. Add yours here. It must use https.
*/
private const val BASE_URL = "https-base-url-here"
/**
* Test mobile app to demonstrate how other applications can use MobileAuthApp.
@@ -18,9 +26,11 @@ class MainActivity : AppCompatActivity() {
private lateinit var authLauncher: ActivityResultLauncher<Intent>
private lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val binding = ActivityMainBinding.inflate(layoutInflater)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
authLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { response ->
@@ -28,25 +38,45 @@ class MainActivity : AppCompatActivity() {
// Currently we are not actually checking whether we get a valid token.
// For testing purposes only, to make sure that we are able to get a response at all.
binding.loginTextView.text = getString(R.string.auth_success)
Log.i("getResult", response.data?.getStringExtra("token").toString())
Log.i("getResult", response.data?.getStringExtra("result").toString())
var user = ""
try {
val resultObject = JSONObject(response.data?.getStringExtra("result").toString())
user = resultObject.getString("principal")
} catch (e: Exception) {
Log.i("getResult", "unable to retrieve name from principal")
}
showResult(user)
/*
binding.loginOptionNfcButton.text = "Log Out"
binding.loginOptionNfcButton.setOnClickListener {
binding.loginOptionNfcButton.text = "NFC auth"
binding.loginOptionNfcButton.setOnClickListener { getData() }
}
*/
}
if (response.resultCode == Activity.RESULT_CANCELED) {
binding.loginTextView.text = getString(R.string.auth_failure)
}
}
binding.loginOptionNfcButton.setOnClickListener { launchAuth() }
//binding.loginOptionNfcButton.setOnClickListener { getData() }
showLogin()
binding.loginOptionNfcButton.setOnClickListener { getData() }
}
/**
* Method that creates an intent to launch the MobileAuthApp
*/
private fun launchAuth(challenge: String = "challenge", authUrl: String = "authUrl") {
private fun launchAuth(challenge: String = "challenge", originUrl: String = "baseUrl", authUrl: String = "authUrl") {
val launchIntent = Intent()
launchIntent.setClassName("com.tarkvaraprojekt.mobileauthapp", "com.tarkvaraprojekt.mobileauthapp.MainActivity")
launchIntent.putExtra("action", "auth")
launchIntent.putExtra("challenge", challenge)
launchIntent.putExtra("originUrl", originUrl)
launchIntent.putExtra("authUrl", authUrl)
launchIntent.putExtra("mobile", true)
authLauncher.launch(launchIntent)
@@ -58,19 +88,35 @@ class MainActivity : AppCompatActivity() {
*/
private fun getData() {
// Enter the server endpoint address to here
val baseUrl = "enter-base-url-here"
val url = "$baseUrl/auth/challenge"
val url = "$BASE_URL/auth/challenge"
Ion.getDefault(this).conscryptMiddleware.enable(false)
Ion.with(applicationContext)
.load(url)
.asJsonObject()
.setCallback { _, result ->
try {
// Get data from the result and call launchAuth method
val challenge = result.asJsonObject["nonce"].toString()
launchAuth(challenge, baseUrl)
val challenge = result.asJsonObject["nonce"].toString().replace("\"", "")
Log.v("Challenge", challenge)
launchAuth(challenge, BASE_URL, "/auth/authentication")
} catch (e: Exception) {
Log.i("GETrequest", "was unsuccessful")
}
}
}
private fun showLogin() {
binding.loginOptions.visibility = View.VISIBLE
}
private fun showResult(user: String) {
binding.loginOptions.visibility = View.GONE
binding.resultLayout.visibility = View.VISIBLE
binding.resultObject.text = getString(R.string.hello, user)
binding.buttonForget.setOnClickListener {
binding.resultObject.text = ""
binding.resultLayout.visibility = View.GONE
binding.loginOptions.visibility = View.VISIBLE
}
}
}

View File

@@ -26,7 +26,8 @@
android:layout_margin="12dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/login_text_view"
app:layout_constraintEnd_toEndOf="parent">
app:layout_constraintEnd_toEndOf="parent"
android:visibility="gone">
<TextView
android:id="@+id/choose_method_text_view"
@@ -46,4 +47,30 @@
</LinearLayout>
<LinearLayout
android:id="@+id/result_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintTop_toBottomOf="@id/login_text_view"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:visibility="gone">
<TextView
android:id="@+id/result_object"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="6dp"
android:textSize="18sp"/>
<Button
android:id="@+id/button_forget"
android:text="@string/forget_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="14sp"/>
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -4,6 +4,8 @@
<string name="login_text">Login</string>
<string name="choose_login_method">Choose login method</string>
<string name="method_nfc">NFC auth</string>
<string name="auth_success">Successful response</string>
<string name="auth_success">Logged in</string>
<string name="auth_failure">Response failed</string>
<string name="forget_button">Forget</string>
<string name="hello">Hello, %s!</string>
</resources>

View File

@@ -4,6 +4,8 @@
<string name="login_text">Logi sisse</string>
<string name="choose_login_method">Vali sobiv meetod</string>
<string name="method_nfc">NFC auth</string>
<string name="auth_success">Vastus kätte saadud</string>
<string name="auth_success">Sisse logimine õnnestus</string>
<string name="auth_failure">Vastust ei õnnestunud kätte saada</string>
<string name="forget_button">Unusta</string>
<string name="hello">Tere, %s!</string>
</resources>

View File

@@ -3,6 +3,8 @@
<string name="login_text">Login</string>
<string name="choose_login_method">Choose login method</string>
<string name="method_nfc">NFC auth</string>
<string name="auth_success">Successful response</string>
<string name="auth_success">Logged in</string>
<string name="auth_failure">Response failed</string>
<string name="forget_button">Forget</string>
<string name="hello">Hello, %s!</string>
</resources>

View File

@@ -42,6 +42,11 @@
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>4.9.0</version>
</dependency>
<dependency>
<groupId>org.webeid.security</groupId>
<artifactId>authtoken-validation</artifactId>

View File

@@ -10,7 +10,7 @@ class ApplicationConfiguration {
// 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"
val WEBSITE_ORIGIN_URL = "https://5d0c-85-253-195-195.ngrok.io"
}
}

View File

@@ -1,6 +1,8 @@
package com.tarkvaratehnika.demobackend.config
import com.github.benmanes.caffeine.jcache.spi.CaffeineCachingProvider
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.core.io.support.PathMatchingResourcePatternResolver
@@ -28,14 +30,25 @@ import javax.cache.configuration.MutableConfiguration
import javax.cache.expiry.CreatedExpiryPolicy
import javax.cache.expiry.Duration
import javax.cache.configuration.FactoryBuilder.factoryOf
@Configuration
class ValidationConfiguration {
private val LOG: Logger = LoggerFactory.getLogger(ValidationConfiguration::class.java)
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"
companion object {
const val ROLE_USER : String = "ROLE_USER"
}
init {
LOG.warn("Creating new ValidationConfiguration.")
}
@Bean
fun cacheManager(): CacheManager {
@@ -47,7 +60,9 @@ class ValidationConfiguration {
val cacheManager: CacheManager = cacheManager()
var cache =
cacheManager.getCache<String?, ZonedDateTime?>(CACHE_NAME)
if (cache == null) {
LOG.warn("Creating new cache.")
cache = createNonceCache(cacheManager)
}
return cache
@@ -65,7 +80,7 @@ class ValidationConfiguration {
val cacheConfig: CompleteConfiguration<String, ZonedDateTime> = MutableConfiguration<String, ZonedDateTime>()
.setTypes(String::class.java, ZonedDateTime::class.java)
.setExpiryPolicyFactory(
FactoryBuilder.factoryOf(
factoryOf(
CreatedExpiryPolicy(
Duration(
TimeUnit.MINUTES,

View File

@@ -23,6 +23,9 @@
package com.tarkvaratehnika.demobackend.security
import com.tarkvaratehnika.demobackend.config.ValidationConfiguration
import com.tarkvaratehnika.demobackend.config.ValidationConfiguration.Companion.ROLE_USER
import com.tarkvaratehnika.demobackend.web.rest.AuthenticationController
import org.slf4j.LoggerFactory
import org.springframework.security.authentication.AuthenticationServiceException
import org.springframework.security.core.Authentication
import org.springframework.security.core.AuthenticationException
@@ -37,11 +40,11 @@ import java.security.cert.X509Certificate
@Component
class AuthTokenDTOAuthenticationProvider {
object AuthTokenDTOAuthenticationProvider {
private val LOG = LoggerFactory.getLogger(AuthTokenDTOAuthenticationProvider::class.java)
companion object {
const val ROLE_USER : String = "ROLE_USER"
}
private val USER_ROLE: GrantedAuthority = SimpleGrantedAuthority(ROLE_USER)
@@ -52,7 +55,6 @@ class AuthTokenDTOAuthenticationProvider {
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)

View File

@@ -1,6 +1,6 @@
package com.tarkvaratehnika.demobackend.web
import com.tarkvaratehnika.demobackend.security.AuthTokenDTOAuthenticationProvider.Companion.ROLE_USER
import com.tarkvaratehnika.demobackend.config.ValidationConfiguration.Companion.ROLE_USER
import org.springframework.security.access.prepost.PreAuthorize
import org.springframework.stereotype.Controller
import org.springframework.ui.Model

View File

@@ -26,7 +26,7 @@ class AuthenticationController {
val auth = PreAuthenticatedAuthenticationToken(null, authToken)
// Return authentication object if success.
return AuthTokenDTOAuthenticationProvider().authenticate(auth)
return AuthTokenDTOAuthenticationProvider.authenticate(auth)
}
@GetMapping("authentication", produces = [MediaType.APPLICATION_JSON_VALUE])

View File

@@ -24,6 +24,7 @@ package com.tarkvaratehnika.demobackend.web.rest
import com.tarkvaratehnika.demobackend.dto.ChallengeDto
import com.tarkvaratehnika.demobackend.security.WebEidAuthentication
import org.slf4j.LoggerFactory
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController
@@ -33,10 +34,12 @@ import org.webeid.security.nonce.NonceGenerator
@RequestMapping("auth")
class ChallengeController (val nonceGenerator: NonceGenerator) {
private val LOG = LoggerFactory.getLogger(ChallengeController::class.java)
@GetMapping("challenge")
fun challenge(): ChallengeDto {
val challengeDto = ChallengeDto(nonceGenerator.generateAndStoreNonce())
LOG.warn(challengeDto.nonce)
// WebEidAuthentication.addAuth(challengeDto.nonce) // For testing.
return challengeDto
}

View File

@@ -1,3 +1,12 @@
html {
font-size: 2vh;
}
.navbar {
padding-left: 1rem;
padding-right: 1rem;
}
.cont {
display: grid;
width: 80%;

View File

@@ -11,7 +11,7 @@ function launchAuthApp(action) {
httpGetAsync(originUrl + challengeUrl, (body) => {
let data = JSON.parse(body);
let challenge = data.nonce;
let intent = createParametrizedIntentUrl(challenge, action); // TODO: Error handling.
let intent = createParametrizedIntentUrl(challenge, action, originUrl); // TODO: Error handling.
console.log(intent);
window.location.href = intent;
pollForAuth(POLLING_INTERVAL, challenge);
@@ -20,8 +20,8 @@ function launchAuthApp(action) {
function pollForAuth(timeout, challenge) {
console.log("Polling for auth");
let requestUrl = originUrl + authenticationRequestUrl + "?challenge=" + challenge;
let encodedChallenge = encodeURIComponent(challenge);
let requestUrl = originUrl + authenticationRequestUrl + "?challenge=" + encodedChallenge;
let counter = 0;
let timer = setInterval(() => {
// Fetch authentication object.
@@ -48,7 +48,7 @@ function createParametrizedIntentUrl(challenge, action) {
else if (challenge == null) {
console.error("Challenge missing, can't authenticate without it.")
} else {
return intentUrl + "?" + "action=" + action + "&challenge=" + challenge + "&authUrl=" + originUrl + authenticationRequestUrl;
return intentUrl + "?" + "action=" + action + "&challenge=" + encodeURIComponent(challenge) + "&authUrl=" + authenticationRequestUrl + "&originUrl=" + originUrl;
}
}

View File

@@ -21,7 +21,7 @@
<body>
<nav class="navbar navbar-dark bg-dark">
<div class="container-fluid">
<a class="navbar-brand" href="#">Auth demo web application</a>
<a class="navbar-brand" href="#">Auth demo webapp</a>
</div>
</nav>
<div class="cont">
@@ -29,7 +29,7 @@
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>
<button type="button" class="btn btn-lg btn-secondary" id="loginButton" data-action="auth">Log in</button>
<div class="alert alert-danger d-none" role="alert" id="loginErrorAlert">
Login failed. Refresh the page to try again.
</div>

View File

@@ -3,38 +3,31 @@
<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 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 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 class="nav-item">
<a href="/" class="btn btn-danger">Log out</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>
<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>
<input type="file" class="custom-file-input" id="customFile">
</div>
<button type="button" class="btn btn-secondary" id="signFile" data-action="auth">Sign</button>
</div>