MOB-55 Fixed some issues with session management.

This commit is contained in:
TanelOrumaa 2021-12-12 20:06:08 +02:00
parent 13a0a9430f
commit 8b78ddf51a
17 changed files with 456 additions and 126 deletions

View File

@ -7,9 +7,10 @@
<p class="text-center">Read more from <a href="https://github.com/TanelOrumaa/Estonian-ID-card-mobile-authenticator-POC">here.</a></p>
</div>
<div id="canvas"></div>
<div class="justify-content-center d-flex">
<div id="canvas"></div>
</div>
<div class="justify-content-center d-flex">
<button type="button" class="btn loginButton btn-dark" v-on:click="authenticate">
<div v-if="loading" class="d-flex justify-content-center">
<div class="spinner-border text-light spinner-border-sm" role="status">
@ -20,7 +21,7 @@
</button>
</div>
<div class="btn-group-sm d-flex justify-content-center" role="group" aria-label="Basic radio toggle button group">
<div class="btn-group-sm d-flex justify-content-center" v-if="!isAndroidDevice" role="group" aria-label="Basic radio toggle button group">
<input type="radio" class="btn-check" name="btnradio" id="btnCardReader" autocomplete="off" v-on:click="useCardReader">
<label class="btn btn-outline-secondary" for="btnCardReader">using ID-card reader</label>
@ -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);
}
}
</script>

View File

@ -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);
}
}
</script>
<style scoped>
nav {
height: 5vh;
height: 7vh;
}
</style>

View File

@ -1,8 +1,11 @@
<template>
<div class="container container-md d-flex flex-column">
<div>
<h3 class="text-center">Congratulations, you logged into the site. Log out to try again.</h3>
<p class="text-center">Read more from <a href="https://github.com/TanelOrumaa/Estonian-ID-card-mobile-authenticator-POC">here.</a></p>
<h3 class="text-center">Welcome {{ userName }}!</h3>
<h4 class="text-center">{{ userIdCode }}</h4>
<p class="text-center">You've successfully logged into this site using your ID card.</p>
<p class="text-center">Read more from <a
href="https://github.com/TanelOrumaa/Estonian-ID-card-mobile-authenticator-POC">here.</a></p>
</div>
</div>
@ -12,14 +15,46 @@
<script>
export default {
name: 'WelcomeComponent',
props: {
"csrftoken": String,
props: {},
methods: {
getUserData: async function () {
const requestOptions = {
method: "GET",
headers: {
"sessionid": this.$store.getters.getSessionId
}
};
fetch("/auth/userData", requestOptions)
.then((response) => {
let data = response.body;
data.getReader().read().then((body) => {
let authObject = JSON.parse(new TextDecoder().decode(body.value));
this.$store.dispatch("setUserName", authObject.userData.name);
let idCode = authObject.userData.idCode.substring(6)
console.log(idCode)
this.$store.dispatch("setUserIdCode", idCode);
});
console.log(data);
}
);
},
},
computed: {
isLoggedIn() {
return this.$store.getters.getAuthenticated;
},
userName() {
return this.$store.getters.getUserName;
},
userIdCode() {
return this.$store.getters.getUserIdCode;
}
}
,
mounted() {
// Get user data.
this.getUserData();
}
}
</script>

View File

@ -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()],
})

View File

@ -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 };

View File

@ -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"
}
}

View File

@ -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()
}
}

View File

@ -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<String, AuthDto>()
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<String, String>) {
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, String>): 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
}
}
}

View File

@ -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<String, ZonedDateTime>? {
val cacheManager: CacheManager = cacheManager()

View File

@ -0,0 +1,5 @@
package com.tarkvaratehnika.demobackend.dto
import org.springframework.security.core.GrantedAuthority
data class AuthDto(var roles: ArrayList<GrantedAuthority>, var userData: HashMap<String, String>)

View File

@ -0,0 +1,5 @@
package com.tarkvaratehnika.demobackend.dto
import com.fasterxml.jackson.annotation.JsonProperty
class AuthTokenDTO (@JsonProperty("auth-token") val token : String)

View File

@ -1,4 +0,0 @@
package com.tarkvaratehnika.demobackend.security
class AuthTokenDTO (val token : String, val xsrfToken : String) {
}

View File

@ -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<GrantedAuthority>()
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)

View File

@ -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<GrantedAuthority>
) : PreAuthenticatedAuthenticationToken(principalName, idCode, authorities), Authentication {
// Companion object is for static functions.
companion object {
private val loggedInUsers = HashMap<String, Authentication>()
private val LOG = LoggerFactory.getLogger(WebEidAuthentication::class.java)
fun fromCertificate(
userCertificate: X509Certificate,
authorities: ArrayList<GrantedAuthority>,
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<String, String>): 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

View File

@ -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<String, String>, @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<String, String>) : AuthDto {
return WebEidAuthentication.fromSession(headers)
}
@GetMapping("userData", produces = [MediaType.APPLICATION_JSON_VALUE])
fun getUserData(@RequestHeader headers: Map<String, String>) : 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<String, String>, @RequestBody body: String) : HttpStatus? {
SessionManager.removeRoleFromCurrentSession(headers)
return HttpStatus.ACCEPTED
}

View File

@ -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<String, String>): 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<GrantedAuthority>()
// 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
}

View File

@ -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, String>): String {
return "<h1>JOUUUUUUUU</h1>"
}
@GetMapping("test2")
fun test2(@RequestHeader headers: Map<String, String>): String {
return "<h1>JOUUUUUUUU22222222222222222</h1>"
}
}