@@ -39,14 +40,11 @@ import router from "@/router";
export default {
name: 'LoginComponent',
- props: {
- "csrftoken": String,
- "csrfHeaderName": String,
- },
data() {
return {
useAndroidApp: true,
loading: false,
+ challenge: "",
}
},
methods: {
@@ -58,6 +56,8 @@ export default {
this.useAndroidApp = false;
},
+
+
authenticate: async function () {
this.loading = true;
@@ -69,7 +69,6 @@ export default {
headers: {
"sessionId": this.$store.getters.getSessionId
},
-
};
console.log(options);
@@ -78,7 +77,7 @@ export default {
const response = await webeid.authenticate(options);
console.log("Authentication successful! Response:", response);
this.loading = false;
- this.$store.commit("setLoggedIn", true);
+ this.$store.dispatch("setLoggedIn", true);
await router.push("welcome");
} catch (error) {
@@ -95,7 +94,14 @@ export default {
},
loading() {
return this.loading;
+ },
+ isAndroidDevice() {
+ return this.$store.getters.getIsAndroid
}
+ },
+ mounted() {
+ const isAndroid = webeid.isAndroidDevice();
+ this.$store.dispatch("setIsAndroid", isAndroid);
}
}
diff --git a/demoBackend/src/demo-website/src/components/Navbar.vue b/demoBackend/src/demo-website/src/components/Navbar.vue
index dbc1942..604f4c4 100644
--- a/demoBackend/src/demo-website/src/components/Navbar.vue
+++ b/demoBackend/src/demo-website/src/components/Navbar.vue
@@ -31,23 +31,21 @@ export default {
fetch("/auth/logout", requestOptions)
.then((response) => {
console.log(response);
- this.$store.commit("setLoggedIn", false);
+ this.$store.dispatch("setLoggedIn", false);
router.push("/");
}
)
}
},
mounted() {
- if (this.$store.getters.getSessionId == null) {
- const sessionId = this.$cookie.getCookie("JSESSIONID");
- this.$store.dispatch("fetchSessionId", sessionId);
- }
+ const sessionId = this.$cookie.getCookie("JSESSIONID");
+ this.$store.dispatch("fetchSessionId", sessionId);
}
}
\ No newline at end of file
diff --git a/demoBackend/src/demo-website/src/components/Welcome.vue b/demoBackend/src/demo-website/src/components/Welcome.vue
index fd86c45..0e90422 100644
--- a/demoBackend/src/demo-website/src/components/Welcome.vue
+++ b/demoBackend/src/demo-website/src/components/Welcome.vue
@@ -1,8 +1,11 @@
-
Congratulations, you logged into the site. Log out to try again.
-
Read more from here.
+
Welcome {{ userName }}!
+
{{ userIdCode }}
+
You've successfully logged into this site using your ID card.
+
Read more from here.
@@ -12,14 +15,46 @@
diff --git a/demoBackend/src/demo-website/src/main.js b/demoBackend/src/demo-website/src/main.js
index 83898c2..f3737ba 100644
--- a/demoBackend/src/demo-website/src/main.js
+++ b/demoBackend/src/demo-website/src/main.js
@@ -16,6 +16,9 @@ const store = createStore({
return {
authenticated: false,
jSessionId: null,
+ isAndroid: false,
+ userName: null,
+ userIdCode: null,
}
},
mutations: {
@@ -24,11 +27,32 @@ const store = createStore({
},
setSessionId(state, sessionId) {
state.jSessionId = sessionId;
+ },
+ setIsAndroid(state, isAndroid) {
+ state.isAndroid = isAndroid;
+ },
+ setUserName(state, userName) {
+ state.userName = userName;
+ },
+ setIdCode(state, idCode) {
+ state.userIdCode = idCode;
}
},
actions: {
fetchSessionId(context, sessionId) {
context.commit("setSessionId", sessionId);
+ },
+ setLoggedIn(context, isLoggedIn) {
+ context.commit("setLoggedIn", isLoggedIn);
+ },
+ setIsAndroid(context, isAndroid) {
+ context.commit("setIsAndroid", isAndroid);
+ },
+ setUserName(context, userName) {
+ context.commit("setUserName", userName);
+ },
+ setUserIdCode(context, userIdCode) {
+ context.commit("setIdCode", userIdCode);
}
},
getters: {
@@ -37,7 +61,16 @@ const store = createStore({
},
getSessionId: state => {
return state.jSessionId;
- }
+ },
+ getIsAndroid: state => {
+ return state.isAndroid;
+ },
+ getUserName: state => {
+ return state.userName;
+ },
+ getUserIdCode: state => {
+ return state.userIdCode;
+ },
},
plugins: [createPersistedState()],
})
diff --git a/demoBackend/src/demo-website/src/web-eid.js b/demoBackend/src/demo-website/src/web-eid.js
index 0e85756..344bcfa 100644
--- a/demoBackend/src/demo-website/src/web-eid.js
+++ b/demoBackend/src/demo-website/src/web-eid.js
@@ -8658,16 +8658,33 @@ class WebExtensionService {
}
publishMessage(message, timeout) {
if (message.useAuthApp && message.useAuthApp == true) {
- if (this.isAndroidDevice()) {
+ if (isAndroidDevice()) {
// Launch auth app.
+ console.log("Launching auth app");
this.launchAuthApp(message);
}
else {
// Display QR code.
this.displayQRCode(message);
}
- this.pollForLoginSuccess(message, timeout).then((res) => {
- console.log(res);
+ console.log("Polling for success.");
+ this.pollForLoginSuccess(message, timeout).then((req) => {
+ req.on("response", (res) => {
+ if (res.statusCode == 200) {
+ console.log(res.statusCode);
+ window.postMessage({ action: this.getRelevantSuccessAction(message) }, location.origin);
+ res.on("data", (data) => {
+ console.log("HERE WE GOOO:" + data);
+ });
+ }
+ else {
+ this.removeFromQueue(message.action);
+ return Promise.reject(new ServerRejectedError("Server rejected the authentication."));
+ }
+ }).on("error", () => {
+ this.removeFromQueue(message.action);
+ return Promise.reject(new ServerRejectedError("Server unreachable."));
+ });
});
}
else {
@@ -8703,35 +8720,29 @@ class WebExtensionService {
if (!message.getAuthSuccessUrl.startsWith("https://")) {
throw new ProtocolInsecureError(`HTTPS required for getAuthSuccessUrl ${message.getAuthSuccessUrl}`);
}
+ console.log("Polling for success.");
const headers = message.headers;
+ const url = new URL(message.getAuthSuccessUrl);
+ const host = url.hostname;
+ const port = url.port;
+ const path = url.pathname;
const options = {
+ host: host,
+ port: port,
+ path: path,
method: "GET",
headers: headers,
timeout: timeout,
};
- return get(message.getAuthSuccessUrl, options).on("error", (e) => {
- console.error(e);
+ return get(options, (res) => {
+ console.log("Polling request answered.");
+ }).on("data", (data) => {
+ console.log("DATA: " + data);
+ }).on("error", () => {
+ throw new ServerRejectedError("Authentication failed.");
}).on("timeout", () => {
- console.error("Timeout");
+ throw new ServerTimeoutError("Server didn't respond in time");
});
- // return await Promise.race([
- // https.get(message.getAuthSuccessUrl, options, (res) => {
- // if (res.statusCode < 200 || res.statusCode > 299) {
- // return reject(new Error(`HTTP status code ${res.statusCode}`))
- // }
- //
- // const body = []
- // res.on('data', (chunk) => body.push(chunk))
- // res.on('end', () => {
- // const resString = Buffer.concat(body).toString()
- // resolve(resString)
- // })
- //
- // this.throwAfterTimeout(
- // timeout,
- // new ServerTimeoutError(`server failed to respond in time - GET ${message.getAuthSuccessUrl}`),
- // ),
- // ]) as HttpResponse;
}
else {
throw new MissingParameterError("getAuthSuccessUrl missing for Android auth app authentication option.");
@@ -8746,9 +8757,6 @@ class WebExtensionService {
setTimeout(() => resolve(), milliseconds);
});
}
- isAndroidDevice() {
- return navigator.userAgent.toLowerCase().indexOf("android") > -1;
- }
getRelevantAckAction(message) {
let ackAction;
switch (message.action) {
@@ -8767,6 +8775,24 @@ class WebExtensionService {
}
return ackAction;
}
+ getRelevantSuccessAction(message) {
+ let ackAction;
+ switch (message.action) {
+ case Action$1.AUTHENTICATE:
+ ackAction = Action$1.AUTHENTICATE_SUCCESS;
+ break;
+ case Action$1.SIGN:
+ ackAction = Action$1.SIGN_SUCCESS;
+ break;
+ case Action$1.STATUS:
+ ackAction = Action$1.STATUS_SUCCESS;
+ break;
+ default:
+ ackAction = Action$1.STATUS_SUCCESS;
+ break;
+ }
+ return ackAction;
+ }
onReplyTimeout(pending) {
var _a;
console.log("onReplyTimeout", pending.message.action);
@@ -8776,14 +8802,13 @@ class WebExtensionService {
onAckTimeout(pending) {
var _a, _b;
console.log("onAckTimeout", pending.message.action);
- console.log("Pending message");
- console.log(pending.message.authApp);
if (pending.message.useAuthApp && pending.message.useAuthApp == true) {
(_a = pending.reject) === null || _a === void 0 ? void 0 : _a.call(pending, new AuthAppNotInstalledError());
}
else {
(_b = pending.reject) === null || _b === void 0 ? void 0 : _b.call(pending, new ExtensionUnavailableError());
}
+ this.removeFromQueue(pending.message.action);
clearTimeout(pending.replyTimer);
}
getPendingMessage(action) {
@@ -9021,5 +9046,8 @@ async function sign(options) {
const result = await webExtensionService.send(message, timeout);
return result.response;
}
+function isAndroidDevice() {
+ return navigator.userAgent.toLowerCase().indexOf("android") > -1;
+}
-export { Action$1 as Action, ErrorCode$1 as ErrorCode, authenticate, config$1 as config, hasVersionProperties, sign, status };
+export { Action$1 as Action, ErrorCode$1 as ErrorCode, authenticate, config$1 as config, hasVersionProperties, isAndroidDevice, sign, status };
diff --git a/demoBackend/src/main/kotlin/com/tarkvaratehnika/demobackend/config/ApplicationConfiguration.kt b/demoBackend/src/main/kotlin/com/tarkvaratehnika/demobackend/config/ApplicationConfiguration.kt
index 123df21..76a2ae2 100644
--- a/demoBackend/src/main/kotlin/com/tarkvaratehnika/demobackend/config/ApplicationConfiguration.kt
+++ b/demoBackend/src/main/kotlin/com/tarkvaratehnika/demobackend/config/ApplicationConfiguration.kt
@@ -3,14 +3,13 @@ package com.tarkvaratehnika.demobackend.config
class ApplicationConfiguration {
companion object {
- // URL for intent, do not edit.
- val AUTH_APP_LAUNCH_INTENT = "authapp://start/"
- // Endpoint for challenge.
- val CHALLENGE_ENDPOINT_URL = "/auth/challenge"
- // Endpoint for authentication
- val AUTHENTICATION_ENDPOINT_URL = "/auth/authentication"
// URL for application. Use ngrok for HTTPS (or a tool of your own choice) and put the HTTPS link here.
- val WEBSITE_ORIGIN_URL = "https://6b9f-85-253-195-195.ngrok.io"
+ val WEBSITE_ORIGIN_URL = "https://5a0b-85-253-195-195.ngrok.io"
+
+ // Authentication request timeout in seconds.
+ val AUTH_REQUEST_TIMEOUT_MS = 120000
+
+ val USER_ROLE = "USER"
}
}
\ No newline at end of file
diff --git a/demoBackend/src/main/kotlin/com/tarkvaratehnika/demobackend/security/SecurityConfiguration.kt b/demoBackend/src/main/kotlin/com/tarkvaratehnika/demobackend/config/SecurityConfiguration.kt
similarity index 74%
rename from demoBackend/src/main/kotlin/com/tarkvaratehnika/demobackend/security/SecurityConfiguration.kt
rename to demoBackend/src/main/kotlin/com/tarkvaratehnika/demobackend/config/SecurityConfiguration.kt
index b37c308..d0aa807 100644
--- a/demoBackend/src/main/kotlin/com/tarkvaratehnika/demobackend/security/SecurityConfiguration.kt
+++ b/demoBackend/src/main/kotlin/com/tarkvaratehnika/demobackend/config/SecurityConfiguration.kt
@@ -1,4 +1,4 @@
-package com.tarkvaratehnika.demobackend.security
+package com.tarkvaratehnika.demobackend.config
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder
import org.springframework.security.config.annotation.web.builders.HttpSecurity
@@ -15,9 +15,10 @@ class SecurityConfiguration : WebSecurityConfigurerAdapter() {
}
override fun configure(http: HttpSecurity) {
- http.authorizeRequests()?.antMatchers("/**")?.permitAll()
- ?.antMatchers("/auth/**")?.permitAll()
- http.sessionManagement()?.sessionCreationPolicy(SessionCreationPolicy.ALWAYS)
- http.csrf().disable()
+ http.authorizeRequests()
+ ?.antMatchers("/welcome")?.hasRole("USER")
+ ?.and()
+ ?.sessionManagement()?.sessionCreationPolicy(SessionCreationPolicy.ALWAYS)
+ ?.and()?.csrf()?.disable()
}
}
\ No newline at end of file
diff --git a/demoBackend/src/main/kotlin/com/tarkvaratehnika/demobackend/config/SessionManager.kt b/demoBackend/src/main/kotlin/com/tarkvaratehnika/demobackend/config/SessionManager.kt
new file mode 100644
index 0000000..75c0bcc
--- /dev/null
+++ b/demoBackend/src/main/kotlin/com/tarkvaratehnika/demobackend/config/SessionManager.kt
@@ -0,0 +1,107 @@
+package com.tarkvaratehnika.demobackend.config
+
+import com.tarkvaratehnika.demobackend.dto.AuthDto
+import org.slf4j.LoggerFactory
+import org.springframework.context.annotation.Configuration
+import org.springframework.security.authentication.UsernamePasswordAuthenticationToken
+import org.springframework.security.core.GrantedAuthority
+import org.springframework.security.core.authority.SimpleGrantedAuthority
+import org.springframework.security.core.context.SecurityContextHolder
+import org.springframework.security.web.authentication.WebAuthenticationDetails
+
+@Configuration
+class SessionManager {
+
+ companion object {
+
+ private val LOG = LoggerFactory.getLogger(SessionManager::class.java)
+
+ private val sessionRegistry = HashMap()
+
+ fun registerSession(sessionId: String) {
+ LOG.warn("REGISTERING SESSION $sessionId")
+ if (sessionRegistry.containsKey(sessionId)) {
+ LOG.debug("Session already exists.")
+ } else {
+ sessionRegistry[sessionId] = AuthDto(arrayListOf(), hashMapOf())
+ }
+ }
+
+ fun addRoleToSession(sessionId: String, role: GrantedAuthority): AuthDto {
+ if (sessionRegistry.containsKey(sessionId)) {
+ val session = sessionRegistry[sessionId]
+ session!!.roles.add(role)
+ return session
+ } else {
+ throw Exception("Session with sessionId: $sessionId does not exist.")
+ }
+ }
+
+ /**
+ * Function adds role and userdata specified in authDto to the current session.
+ */
+ fun addRoleToCurrentSession(authDto: AuthDto) {
+ val securityContext = SecurityContextHolder.getContext()
+ var sessionId = getSessionId()
+ if (sessionId == null) {
+ // No sessionId attached to the session, get one from credentials.
+ sessionId = securityContext.authentication.credentials.toString()
+ }
+ val authentication = UsernamePasswordAuthenticationToken(authDto.userData, sessionId, authDto.roles)
+ securityContext.authentication = authentication
+ }
+
+ fun removeRoleFromCurrentSession(headers: Map) {
+ val securityContext = SecurityContextHolder.getContext()
+ var sessionId = securityContext.authentication.credentials
+ if (sessionId == null) {
+ // Fallback to when for some reason session object doesn't have sessionId attached.
+ sessionId = getSessionId(headers)
+ }
+ sessionRegistry[sessionId]!!.roles = arrayListOf()
+ val authentication = UsernamePasswordAuthenticationToken(null, sessionId, listOf())
+ securityContext.authentication = authentication
+ }
+
+ fun addUserDataToSession(sessionId: String, name: String, idCode: String): AuthDto {
+ if (sessionRegistry.containsKey(sessionId)) {
+ val session = sessionRegistry[sessionId]
+ session!!.userData["name"] = name
+ session.userData["idCode"] = idCode
+ return session
+ } else {
+ throw Exception("Session with sessionId: $sessionId does not exist.")
+ }
+ }
+
+ fun getSessionHasRole(sessionId: String, role: String): Boolean {
+ if (sessionRegistry.containsKey(sessionId)) {
+ if (sessionRegistry[sessionId]!!.roles.contains(SimpleGrantedAuthority(role))) {
+ return true
+ }
+ }
+ return false
+ }
+
+ fun getSessionAuth(sessionId: String?): AuthDto? {
+ if (sessionId == null) {
+ return null
+ }
+ return sessionRegistry[sessionId]
+ }
+
+ fun getSessionId(headers: Map): String? {
+ return headers["sessionid"]
+ }
+
+ fun getSessionId(): String? {
+ val context = SecurityContextHolder.getContext()
+ if (context.authentication != null && context.authentication.details != null) {
+ return (context.authentication.details as WebAuthenticationDetails).sessionId
+ }
+ return null
+ }
+ }
+
+
+}
\ No newline at end of file
diff --git a/demoBackend/src/main/kotlin/com/tarkvaratehnika/demobackend/config/ValidationConfiguration.kt b/demoBackend/src/main/kotlin/com/tarkvaratehnika/demobackend/config/ValidationConfiguration.kt
index e59b67f..fdd6fa6 100644
--- a/demoBackend/src/main/kotlin/com/tarkvaratehnika/demobackend/config/ValidationConfiguration.kt
+++ b/demoBackend/src/main/kotlin/com/tarkvaratehnika/demobackend/config/ValidationConfiguration.kt
@@ -3,9 +3,12 @@ package com.tarkvaratehnika.demobackend.config
import com.github.benmanes.caffeine.jcache.spi.CaffeineCachingProvider
import org.slf4j.Logger
import org.slf4j.LoggerFactory
+import org.springframework.beans.factory.annotation.Autowired
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.core.io.support.PathMatchingResourcePatternResolver
+import org.springframework.security.core.session.SessionRegistry
+import org.springframework.security.core.session.SessionRegistryImpl
import org.webeid.security.exceptions.JceException
import org.webeid.security.nonce.NonceGenerator
import org.webeid.security.nonce.NonceGeneratorBuilder
@@ -13,7 +16,6 @@ import org.webeid.security.validator.AuthTokenValidator
import org.webeid.security.validator.AuthTokenValidatorBuilder
import java.io.IOException
import java.net.URI
-import java.net.URL
import java.security.KeyStore
import java.security.KeyStoreException
import java.security.NoSuchAlgorithmException
@@ -26,12 +28,11 @@ import javax.cache.Cache
import javax.cache.CacheManager
import javax.cache.Caching
import javax.cache.configuration.CompleteConfiguration
-import javax.cache.configuration.FactoryBuilder
+import javax.cache.configuration.FactoryBuilder.factoryOf
import javax.cache.configuration.MutableConfiguration
import javax.cache.expiry.CreatedExpiryPolicy
import javax.cache.expiry.Duration
-import javax.cache.configuration.FactoryBuilder.factoryOf
@Configuration
class ValidationConfiguration {
@@ -43,9 +44,8 @@ class ValidationConfiguration {
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.")
@@ -56,6 +56,8 @@ class ValidationConfiguration {
return Caching.getCachingProvider(CaffeineCachingProvider::class.java.name).cacheManager
}
+
+
@Bean
fun nonceCache(): Cache? {
val cacheManager: CacheManager = cacheManager()
diff --git a/demoBackend/src/main/kotlin/com/tarkvaratehnika/demobackend/dto/AuthDto.kt b/demoBackend/src/main/kotlin/com/tarkvaratehnika/demobackend/dto/AuthDto.kt
new file mode 100644
index 0000000..4d638be
--- /dev/null
+++ b/demoBackend/src/main/kotlin/com/tarkvaratehnika/demobackend/dto/AuthDto.kt
@@ -0,0 +1,5 @@
+package com.tarkvaratehnika.demobackend.dto
+
+import org.springframework.security.core.GrantedAuthority
+
+data class AuthDto(var roles: ArrayList, var userData: HashMap)
\ No newline at end of file
diff --git a/demoBackend/src/main/kotlin/com/tarkvaratehnika/demobackend/dto/AuthTokenDTO.kt b/demoBackend/src/main/kotlin/com/tarkvaratehnika/demobackend/dto/AuthTokenDTO.kt
new file mode 100644
index 0000000..e3ce412
--- /dev/null
+++ b/demoBackend/src/main/kotlin/com/tarkvaratehnika/demobackend/dto/AuthTokenDTO.kt
@@ -0,0 +1,5 @@
+package com.tarkvaratehnika.demobackend.dto
+
+import com.fasterxml.jackson.annotation.JsonProperty
+
+class AuthTokenDTO (@JsonProperty("auth-token") val token : String)
\ No newline at end of file
diff --git a/demoBackend/src/main/kotlin/com/tarkvaratehnika/demobackend/security/AuthTokenDTO.kt b/demoBackend/src/main/kotlin/com/tarkvaratehnika/demobackend/security/AuthTokenDTO.kt
deleted file mode 100644
index 255402e..0000000
--- a/demoBackend/src/main/kotlin/com/tarkvaratehnika/demobackend/security/AuthTokenDTO.kt
+++ /dev/null
@@ -1,4 +0,0 @@
-package com.tarkvaratehnika.demobackend.security
-
-class AuthTokenDTO (val token : String, val xsrfToken : String) {
-}
\ No newline at end of file
diff --git a/demoBackend/src/main/kotlin/com/tarkvaratehnika/demobackend/security/AuthTokenDTOAuthenticationProvider.kt b/demoBackend/src/main/kotlin/com/tarkvaratehnika/demobackend/security/AuthTokenDTOAuthenticationProvider.kt
index 89f7ca4..d08aae6 100644
--- a/demoBackend/src/main/kotlin/com/tarkvaratehnika/demobackend/security/AuthTokenDTOAuthenticationProvider.kt
+++ b/demoBackend/src/main/kotlin/com/tarkvaratehnika/demobackend/security/AuthTokenDTOAuthenticationProvider.kt
@@ -23,14 +23,12 @@
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 com.tarkvaratehnika.demobackend.dto.AuthDto
+import com.tarkvaratehnika.demobackend.dto.AuthTokenDTO
import org.slf4j.LoggerFactory
import org.springframework.security.authentication.AuthenticationServiceException
import org.springframework.security.core.Authentication
import org.springframework.security.core.AuthenticationException
-import org.springframework.security.core.GrantedAuthority
-import org.springframework.security.core.authority.SimpleGrantedAuthority
import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken
import org.springframework.stereotype.Component
import org.webeid.security.exceptions.TokenValidationException
@@ -44,23 +42,15 @@ object AuthTokenDTOAuthenticationProvider {
private val LOG = LoggerFactory.getLogger(AuthTokenDTOAuthenticationProvider::class.java)
-
- private val USER_ROLE: GrantedAuthority = SimpleGrantedAuthority(ROLE_USER)
-
-
val tokenValidator: AuthTokenValidator = ValidationConfiguration().validator()
@Throws(AuthenticationException::class)
- fun authenticate(auth : Authentication) : Authentication {
+ fun authenticate(auth : Authentication, sessionId: String?) : AuthDto {
val authentication = auth as PreAuthenticatedAuthenticationToken
val token = (authentication.credentials as AuthTokenDTO).token
- val challenge = (authentication.credentials as AuthTokenDTO)
- val authorities = arrayListOf()
- authorities.add(USER_ROLE)
-
try {
val userCertificate: X509Certificate = tokenValidator.validate(token)
- return WebEidAuthentication.fromCertificate(userCertificate, authorities, "as")
+ return WebEidAuthentication.fromCertificate(userCertificate, sessionId)
} catch (e : TokenValidationException) {
// Validation failed.
throw AuthenticationServiceException("Token validation failed. " + e.message)
diff --git a/demoBackend/src/main/kotlin/com/tarkvaratehnika/demobackend/security/WebEidAuthentication.kt b/demoBackend/src/main/kotlin/com/tarkvaratehnika/demobackend/security/WebEidAuthentication.kt
index c87ed42..98f5a56 100644
--- a/demoBackend/src/main/kotlin/com/tarkvaratehnika/demobackend/security/WebEidAuthentication.kt
+++ b/demoBackend/src/main/kotlin/com/tarkvaratehnika/demobackend/security/WebEidAuthentication.kt
@@ -22,17 +22,27 @@
package com.tarkvaratehnika.demobackend.security
+import com.tarkvaratehnika.demobackend.config.ApplicationConfiguration
+import com.tarkvaratehnika.demobackend.config.ApplicationConfiguration.Companion.USER_ROLE
+import com.tarkvaratehnika.demobackend.config.SessionManager
+import com.tarkvaratehnika.demobackend.dto.AuthDto
+import org.slf4j.LoggerFactory
+import org.springframework.http.HttpStatus
import org.webeid.security.certificate.CertificateData
import org.springframework.security.core.Authentication
import org.springframework.security.core.GrantedAuthority
import org.springframework.security.core.authority.SimpleGrantedAuthority
+import org.springframework.security.core.context.SecurityContext
+import org.springframework.security.core.context.SecurityContextHolder
+import org.springframework.security.web.authentication.WebAuthenticationDetails
import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken
+import org.springframework.web.server.ResponseStatusException
+import java.io.Serializable
import java.security.cert.X509Certificate
import java.util.*
-import java.util.concurrent.ThreadLocalRandom
import kotlin.collections.ArrayList
-import kotlin.math.log
+import kotlin.collections.HashMap
class WebEidAuthentication(
private val principalName: String,
@@ -40,39 +50,71 @@ class WebEidAuthentication(
private val authorities: ArrayList
) : PreAuthenticatedAuthenticationToken(principalName, idCode, authorities), Authentication {
+
// Companion object is for static functions.
companion object {
-
- private val loggedInUsers = HashMap()
+ private val LOG = LoggerFactory.getLogger(WebEidAuthentication::class.java)
fun fromCertificate(
userCertificate: X509Certificate,
- authorities: ArrayList,
- challenge: String
- ): Authentication {
- val principalName = getPrincipalNameFromCertificate(userCertificate)
+ sessionId: String?,
+ ): AuthDto {
+ // Get user data.
+ val name = getPrincipalNameFromCertificate(userCertificate)
val idCode = Objects.requireNonNull(CertificateData.getSubjectIdCode(userCertificate))
- val authentication = WebEidAuthentication(principalName, idCode, authorities)
- loggedInUsers[challenge] = authentication
- return authentication
+
+ // Fetch valid sessionId.
+ var methodIndependentSessionId = sessionId
+ if (methodIndependentSessionId == null) {
+ methodIndependentSessionId = SessionManager.getSessionId()
+ if (methodIndependentSessionId == null) {
+ throw Exception("No session")
+ }
+ }
+
+ // Add role and user data to the AuthDto and return it.
+ SessionManager.addRoleToSession(methodIndependentSessionId, SimpleGrantedAuthority(USER_ROLE))
+ return SessionManager.addUserDataToSession(methodIndependentSessionId, name, idCode)
}
/**
* Function for getting a Spring authentication object by supplying a challenge.
* TODO: Figure out a more secure solution in the future.
*/
- fun fromChallenge(challenge: String): Authentication? {
+ fun fromSession(headers: HashMap): AuthDto {
+ val currentTime = Date()
+
+ // Get sessionId for current session.
+ var sessionId = SessionManager.getSessionId()
+
+ if (sessionId == null) {
+ LOG.warn("SESSION IS NULL")
+ sessionId = SessionManager.getSessionId(headers)
+ if (sessionId == null) {
+ LOG.warn("SESSION IS STILL NULL")
+ throw ResponseStatusException(HttpStatus.FORBIDDEN, "Session ID not found.")
+ }
+ LOG.warn("SESSION IS NOW: " + sessionId)
+ }
+
+ while (currentTime.time + ApplicationConfiguration.AUTH_REQUEST_TIMEOUT_MS > Date().time) {
+ Thread.sleep(1000)
+
+ if (SessionManager.getSessionHasRole(sessionId, USER_ROLE)) {
+ // Get AuthDto
+ val auth = SessionManager.getSessionAuth(sessionId)
+
+ // Set role and user data to current session.
+ SessionManager.addRoleToCurrentSession(auth!!)
+ LOG.warn("ROLE ADDED AND LOGGING IN.")
+ return auth
+ }
+
+ }
// if (ThreadLocalRandom.current().nextFloat() < 0.5f) { // TODO: For testing.
// return null
// }
- val auth = loggedInUsers[challenge]
- if (auth != null) {
- // If challenge is valid, delete the authentication object from the map (so this can only be fetched once).
- loggedInUsers.remove(challenge)
- } else {
- return null
- }
- return auth
+ throw ResponseStatusException(HttpStatus.REQUEST_TIMEOUT, "Token not received in time.")
}
// // TODO: DELETE
diff --git a/demoBackend/src/main/kotlin/com/tarkvaratehnika/demobackend/web/rest/AuthenticationController.kt b/demoBackend/src/main/kotlin/com/tarkvaratehnika/demobackend/web/rest/AuthenticationController.kt
index f86517e..45e1e70 100644
--- a/demoBackend/src/main/kotlin/com/tarkvaratehnika/demobackend/web/rest/AuthenticationController.kt
+++ b/demoBackend/src/main/kotlin/com/tarkvaratehnika/demobackend/web/rest/AuthenticationController.kt
@@ -1,15 +1,15 @@
package com.tarkvaratehnika.demobackend.web.rest
-import com.tarkvaratehnika.demobackend.security.AuthTokenDTO
+import com.tarkvaratehnika.demobackend.config.SessionManager
+import com.tarkvaratehnika.demobackend.dto.AuthDto
+import com.tarkvaratehnika.demobackend.dto.AuthTokenDTO
import com.tarkvaratehnika.demobackend.security.AuthTokenDTOAuthenticationProvider
import com.tarkvaratehnika.demobackend.security.WebEidAuthentication
import org.slf4j.LoggerFactory
import org.springframework.http.HttpStatus
import org.springframework.http.MediaType
-import org.springframework.security.core.Authentication
import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken
import org.springframework.web.bind.annotation.*
-import org.springframework.web.server.ResponseStatusException
@RestController
@RequestMapping("auth")
@@ -19,30 +19,31 @@ class AuthenticationController {
@PostMapping("login", consumes = [MediaType.APPLICATION_JSON_VALUE], produces = [MediaType.APPLICATION_JSON_VALUE])
- fun authenticate(@RequestBody body : String): Authentication {
- val parts = body.split("\"")
- val authToken = AuthTokenDTO(parts[3], parts[7])
+ fun authenticate(@RequestHeader headers: Map, @RequestBody body : AuthTokenDTO): AuthDto {
+
+ val sessionId = SessionManager.getSessionId(headers)
+
// Create Spring Security Authentication object with supplied token as credentials.
- val auth = PreAuthenticatedAuthenticationToken(null, authToken)
+ val auth = PreAuthenticatedAuthenticationToken(null, body)
// Return authentication object if success.
- return AuthTokenDTOAuthenticationProvider.authenticate(auth)
+ return AuthTokenDTOAuthenticationProvider.authenticate(auth, sessionId)
}
@GetMapping("login", produces = [MediaType.APPLICATION_JSON_VALUE])
- fun getAuthenticated(headers: String) : Authentication? {
- val auth = WebEidAuthentication.fromChallenge("as")
- if (auth == null) {
- throw ResponseStatusException(HttpStatus.FORBIDDEN, "Not allowed.")
- }
- return auth
+ fun getAuthenticated(@RequestHeader headers: HashMap) : AuthDto {
+ return WebEidAuthentication.fromSession(headers)
+ }
+
+ @GetMapping("userData", produces = [MediaType.APPLICATION_JSON_VALUE])
+ fun getUserData(@RequestHeader headers: Map) : AuthDto? {
+ return SessionManager.getSessionAuth(SessionManager.getSessionId(headers))
}
@PostMapping("logout", consumes = [MediaType.APPLICATION_JSON_VALUE])
- fun logOut(@RequestBody body: String) : HttpStatus? {
- LOG.warn("I WAS HERE")
- LOG.warn(body)
+ fun logOut(@RequestHeader headers: Map, @RequestBody body: String) : HttpStatus? {
+ SessionManager.removeRoleFromCurrentSession(headers)
return HttpStatus.ACCEPTED
}
diff --git a/demoBackend/src/main/kotlin/com/tarkvaratehnika/demobackend/web/rest/ChallengeController.kt b/demoBackend/src/main/kotlin/com/tarkvaratehnika/demobackend/web/rest/ChallengeController.kt
index 7333c9d..ec09d28 100644
--- a/demoBackend/src/main/kotlin/com/tarkvaratehnika/demobackend/web/rest/ChallengeController.kt
+++ b/demoBackend/src/main/kotlin/com/tarkvaratehnika/demobackend/web/rest/ChallengeController.kt
@@ -22,14 +22,24 @@
package com.tarkvaratehnika.demobackend.web.rest
+import com.tarkvaratehnika.demobackend.config.SessionManager
import com.tarkvaratehnika.demobackend.dto.ChallengeDto
-import com.tarkvaratehnika.demobackend.security.WebEidAuthentication
import org.slf4j.LoggerFactory
+import org.springframework.http.HttpStatus
+import org.springframework.security.authentication.UsernamePasswordAuthenticationToken
+import org.springframework.security.core.Authentication
+import org.springframework.security.core.GrantedAuthority
+import org.springframework.security.core.authority.SimpleGrantedAuthority
+import org.springframework.security.core.context.SecurityContextHolder
import org.springframework.web.bind.annotation.GetMapping
+import org.springframework.web.bind.annotation.RequestHeader
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController
+import org.springframework.web.client.HttpClientErrorException
+import org.springframework.web.server.ResponseStatusException
import org.webeid.security.nonce.NonceGenerator
+
@RestController
@RequestMapping("auth")
class ChallengeController (val nonceGenerator: NonceGenerator) {
@@ -37,10 +47,30 @@ class ChallengeController (val nonceGenerator: NonceGenerator) {
private val LOG = LoggerFactory.getLogger(ChallengeController::class.java)
@GetMapping("challenge")
- fun challenge(): ChallengeDto {
+ fun challenge(@RequestHeader headers: Map): ChallengeDto {
+
+ val sessionId = SessionManager.getSessionId(headers)
+
+ if (sessionId == null) {
+ LOG.warn("SESSION ID MISSING FOR CHALLENGE")
+ throw ResponseStatusException(HttpStatus.FORBIDDEN, "SessionId missing.")
+ }
+
+ SessionManager.registerSession(sessionId)
+
+// val context = SecurityContextHolder.getContext()
+// val authorities = arrayListOf()
+// authorities.add(SimpleGrantedAuthority("USER"))
+// authorities.add(SimpleGrantedAuthority("ROLE_USER"))
+// val auth = context.authentication
+//
+// val newAuth: Authentication =
+// UsernamePasswordAuthenticationToken(auth.principal, auth.credentials, authorities)
+// SecurityContextHolder.getContext().authentication = newAuth;
+
+// SessionManager.createSession(SessionManager.getSessionId(headers))
val challengeDto = ChallengeDto(nonceGenerator.generateAndStoreNonce())
LOG.warn(challengeDto.nonce)
-// WebEidAuthentication.addAuth(challengeDto.nonce) // For testing.
return challengeDto
}
diff --git a/demoBackend/src/main/kotlin/com/tarkvaratehnika/demobackend/web/rest/Test.kt b/demoBackend/src/main/kotlin/com/tarkvaratehnika/demobackend/web/rest/Test.kt
new file mode 100644
index 0000000..4ab3418
--- /dev/null
+++ b/demoBackend/src/main/kotlin/com/tarkvaratehnika/demobackend/web/rest/Test.kt
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2020, 2021 The Web eID Project
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+package com.tarkvaratehnika.demobackend.web.rest
+
+import com.tarkvaratehnika.demobackend.config.SessionManager
+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.RequestHeader
+import org.springframework.web.bind.annotation.RequestMapping
+import org.springframework.web.bind.annotation.RestController
+import org.webeid.security.nonce.NonceGenerator
+
+@RestController
+@RequestMapping("auth")
+class Test (val nonceGenerator: NonceGenerator) {
+
+ private val LOG = LoggerFactory.getLogger(ChallengeController::class.java)
+
+ @GetMapping("test")
+ fun test(@RequestHeader headers: Map): String {
+ return "JOUUUUUUUU
"
+ }
+
+ @GetMapping("test2")
+ fun test2(@RequestHeader headers: Map): String {
+ return "JOUUUUUUUU22222222222222222
"
+ }
+
+}
+