mirror of
https://github.com/TanelOrumaa/Estonian-ID-card-mobile-authenticator-POC.git
synced 2024-12-22 12:30:16 +02:00
MOB-42 Fixed token authentication issues (wrong library version, cache getting recreated every request, origin in wrong form)
This commit is contained in:
parent
9b0cb1a22d
commit
636beeb7f3
@ -90,7 +90,7 @@ class AuthFragment : Fragment() {
|
||||
if (args.auth) {
|
||||
val jws = Authenticator(comms).authenticate(
|
||||
intentParameters.challenge,
|
||||
intentParameters.authUrl,
|
||||
intentParameters.origin,
|
||||
viewModel.userPin
|
||||
)
|
||||
intentParameters.setToken(jws)
|
||||
|
@ -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,16 @@ 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.
|
||||
|
@ -67,7 +67,7 @@ class ResultFragment : Fragment() {
|
||||
Ion.getDefault(activity).getConscryptMiddleware().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 ->
|
||||
|
@ -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("=", "")
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
}
|
@ -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>
|
||||
|
@ -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://2c2c-85-253-195-252.ngrok.io"
|
||||
}
|
||||
|
||||
}
|
@ -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,
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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
|
||||
|
@ -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])
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -1,3 +1,7 @@
|
||||
html {
|
||||
font-size: 4vw;
|
||||
}
|
||||
|
||||
.cont {
|
||||
display: grid;
|
||||
width: 80%;
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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>
|
||||
|
Loading…
Reference in New Issue
Block a user