mirror of
https://github.com/TanelOrumaa/Estonian-ID-card-mobile-authenticator-POC.git
synced 2024-11-16 19:00:59 +02:00
Fixed the error handling a bit, added some text to login page
This commit is contained in:
parent
e5931692b6
commit
c232a1f734
@ -68,18 +68,18 @@ class AuthFragment : Fragment() {
|
||||
|
||||
override fun onFinish() {
|
||||
Thread.sleep(750)
|
||||
cancelAuth()
|
||||
cancelAuth(408)
|
||||
}
|
||||
}.start()
|
||||
// The button exists in code for testing reasons, but not visible to the user anymore unless visibility is changed in the code.
|
||||
binding.nextButton.visibility = View.GONE
|
||||
binding.nextButton.setOnClickListener { goToNextFragment() }
|
||||
binding.cancelButton.setOnClickListener { cancelAuth() }
|
||||
binding.cancelButton.setOnClickListener { cancelAuth(444) }
|
||||
val adapter = NfcAdapter.getDefaultAdapter(activity)
|
||||
if (adapter != null)
|
||||
getInfoFromIdCard(adapter)
|
||||
else { // If NFC adapter can not be detected then end the auth process as it is not possible to read an ID card
|
||||
cancelAuth() // It would be a good idea to show user some notification as it might be confusing if the app suddenly closes
|
||||
cancelAuth(447) // It would be a good idea to show user some notification as it might be confusing if the app suddenly closes
|
||||
}
|
||||
}
|
||||
|
||||
@ -89,7 +89,7 @@ class AuthFragment : Fragment() {
|
||||
findNavController().navigate(action)
|
||||
}
|
||||
|
||||
private fun cancelAuth() {
|
||||
private fun cancelAuth(code: Int) {
|
||||
viewModel.clearUserInfo()
|
||||
timer.cancel()
|
||||
if (args.mobile) {
|
||||
@ -97,6 +97,7 @@ class AuthFragment : Fragment() {
|
||||
requireActivity().setResult(AppCompatActivity.RESULT_CANCELED, resultIntent)
|
||||
requireActivity().finish()
|
||||
} else {
|
||||
(activity as MainActivity).returnError(code)
|
||||
requireActivity().finishAndRemoveTask()
|
||||
}
|
||||
}
|
||||
@ -123,24 +124,28 @@ class AuthFragment : Fragment() {
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
when(e) {
|
||||
is TagLostException -> requireActivity().runOnUiThread { binding!!.timeCounter.text = getString(R.string.id_card_removed_early) }
|
||||
is TagLostException -> requireActivity().runOnUiThread {
|
||||
binding!!.timeCounter.text = getString(R.string.id_card_removed_early)
|
||||
cancelAuth(444)
|
||||
}
|
||||
else -> {
|
||||
when ("invalid pin") {
|
||||
in e.message.toString().lowercase() -> requireActivity().runOnUiThread {
|
||||
val messagePieces = e.message.toString().split(" ")
|
||||
binding.timeCounter.text = getString(R.string.wrong_pin, messagePieces[messagePieces.size - 1])
|
||||
viewModel.deletePin(requireContext())
|
||||
cancelAuth(449)
|
||||
}
|
||||
else -> requireActivity().runOnUiThread {
|
||||
binding.timeCounter.text = getString(R.string.wrong_can_text)
|
||||
viewModel.deleteCan(requireContext())
|
||||
cancelAuth(449)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Give user some time to read the error message
|
||||
Thread.sleep(2000)
|
||||
cancelAuth()
|
||||
} finally {
|
||||
adapter.disableReaderMode(activity)
|
||||
}
|
||||
|
@ -90,6 +90,7 @@ class CanFragment : Fragment() {
|
||||
requireActivity().setResult(AppCompatActivity.RESULT_CANCELED, resultIntent)
|
||||
requireActivity().finish()
|
||||
} else {
|
||||
(activity as MainActivity).returnError(444)
|
||||
requireActivity().finishAndRemoveTask()
|
||||
}
|
||||
} else {
|
||||
|
@ -5,9 +5,17 @@ import android.os.Bundle
|
||||
import android.view.Menu
|
||||
import android.view.MenuItem
|
||||
import android.widget.Toast
|
||||
import androidx.activity.viewModels
|
||||
import androidx.fragment.app.activityViewModels
|
||||
import androidx.navigation.NavController
|
||||
import androidx.navigation.fragment.NavHostFragment
|
||||
import androidx.navigation.fragment.navArgs
|
||||
import androidx.navigation.navArgs
|
||||
import com.google.gson.JsonObject
|
||||
import com.koushikdutta.ion.Ion
|
||||
import com.tarkvaraprojekt.mobileauthapp.databinding.ActivityMainBinding
|
||||
import com.tarkvaraprojekt.mobileauthapp.databinding.FragmentResultBinding
|
||||
import com.tarkvaraprojekt.mobileauthapp.model.ParametersViewModel
|
||||
|
||||
|
||||
/**
|
||||
@ -16,6 +24,8 @@ import com.tarkvaraprojekt.mobileauthapp.databinding.ActivityMainBinding
|
||||
class MainActivity : AppCompatActivity() {
|
||||
|
||||
private lateinit var navigationController: NavController
|
||||
private val paramsModel: ParametersViewModel by viewModels()
|
||||
|
||||
|
||||
// If true the settings menu can be accessed from the toolbar in the upper part of the screen.
|
||||
var menuAvailable: Boolean = true
|
||||
@ -54,4 +64,24 @@ class MainActivity : AppCompatActivity() {
|
||||
}
|
||||
else -> super.onOptionsItemSelected(item)
|
||||
}
|
||||
|
||||
fun returnError(errorCode: Int) {
|
||||
val json = JsonObject()
|
||||
json.addProperty("auth-token", "")
|
||||
json.addProperty("error", errorCode)
|
||||
|
||||
Ion.getDefault(this).conscryptMiddleware.enable(false)
|
||||
val ion = Ion.with(this)
|
||||
.load(paramsModel.authUrl)
|
||||
for ((header, value) in paramsModel.headers) {
|
||||
ion.setHeader(header, value)
|
||||
}
|
||||
|
||||
ion
|
||||
.setJsonObjectBody(json)
|
||||
.asJsonObject()
|
||||
.setCallback { _, _ ->
|
||||
|
||||
}
|
||||
}
|
||||
}
|
@ -96,6 +96,7 @@ class PinFragment : Fragment() {
|
||||
requireActivity().setResult(AppCompatActivity.RESULT_CANCELED, resultIntent)
|
||||
requireActivity().finish()
|
||||
} else {
|
||||
(activity as MainActivity).returnError(444)
|
||||
requireActivity().finishAndRemoveTask()
|
||||
}
|
||||
} else {
|
||||
|
@ -68,8 +68,7 @@ class ResultFragment : Fragment() {
|
||||
fun postToken() {
|
||||
val json = JsonObject()
|
||||
json.addProperty("auth-token", paramsModel.token)
|
||||
|
||||
|
||||
json.addProperty("error", 200)
|
||||
|
||||
Ion.getDefault(activity).conscryptMiddleware.enable(false)
|
||||
val ion = Ion.with(activity)
|
||||
|
2
demoBackend/.gitignore
vendored
2
demoBackend/.gitignore
vendored
@ -33,4 +33,4 @@ build/
|
||||
.vscode/
|
||||
|
||||
### web-eid.js ###
|
||||
!**src/demo-website/src/web-eid.js
|
||||
src/demo-website/src/web-eid.js
|
@ -94,6 +94,7 @@
|
||||
<build>
|
||||
<sourceDirectory>${project.basedir}/src/main/kotlin</sourceDirectory>
|
||||
<testSourceDirectory>${project.basedir}/src/test/kotlin</testSourceDirectory>
|
||||
<finalName>demo</finalName>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
|
30
demoBackend/src/demo-website/package-lock.json
generated
30
demoBackend/src/demo-website/package-lock.json
generated
@ -8,7 +8,7 @@
|
||||
"name": "demo-website",
|
||||
"version": "0.1.0",
|
||||
"dependencies": {
|
||||
"@web-eid/web-eid-library": "github:TanelOrumaa/web-eid.js#main",
|
||||
"@web-eid/web-eid-library": "../../../../web-eid.js/",
|
||||
"core-js": "^3.6.5",
|
||||
"vue": "^3.0.0",
|
||||
"vue-cookie-next": "^1.3.0",
|
||||
@ -32,7 +32,6 @@
|
||||
"../../../../web-eid.js": {
|
||||
"name": "@web-eid/web-eid-library",
|
||||
"version": "1.0.1",
|
||||
"extraneous": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/node": "^16.11.11"
|
||||
@ -1925,7 +1924,8 @@
|
||||
"node_modules/@types/node": {
|
||||
"version": "16.11.12",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.12.tgz",
|
||||
"integrity": "sha512-+2Iggwg7PxoO5Kyhvsq9VarmPbIelXP070HMImEpbtGCoyWNINQj4wzjbQCXzdHTRXnqufutJb5KAURZANNBAw=="
|
||||
"integrity": "sha512-+2Iggwg7PxoO5Kyhvsq9VarmPbIelXP070HMImEpbtGCoyWNINQj4wzjbQCXzdHTRXnqufutJb5KAURZANNBAw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/normalize-package-data": {
|
||||
"version": "2.4.1",
|
||||
@ -2643,12 +2643,8 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@web-eid/web-eid-library": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "git+ssh://git@github.com/TanelOrumaa/web-eid.js.git#b9c6f50c78a39b444d5df308826cd3f3476233d1",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/node": "^16.11.11"
|
||||
}
|
||||
"resolved": "../../../../web-eid.js",
|
||||
"link": true
|
||||
},
|
||||
"node_modules/@webassemblyjs/ast": {
|
||||
"version": "1.9.0",
|
||||
@ -16604,7 +16600,8 @@
|
||||
"@types/node": {
|
||||
"version": "16.11.12",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.12.tgz",
|
||||
"integrity": "sha512-+2Iggwg7PxoO5Kyhvsq9VarmPbIelXP070HMImEpbtGCoyWNINQj4wzjbQCXzdHTRXnqufutJb5KAURZANNBAw=="
|
||||
"integrity": "sha512-+2Iggwg7PxoO5Kyhvsq9VarmPbIelXP070HMImEpbtGCoyWNINQj4wzjbQCXzdHTRXnqufutJb5KAURZANNBAw==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/normalize-package-data": {
|
||||
"version": "2.4.1",
|
||||
@ -17212,10 +17209,17 @@
|
||||
"dev": true
|
||||
},
|
||||
"@web-eid/web-eid-library": {
|
||||
"version": "git+ssh://git@github.com/TanelOrumaa/web-eid.js.git#b9c6f50c78a39b444d5df308826cd3f3476233d1",
|
||||
"from": "@web-eid/web-eid-library@github:TanelOrumaa/web-eid.js#main",
|
||||
"version": "file:../../../../web-eid.js",
|
||||
"requires": {
|
||||
"@types/node": "^16.11.11"
|
||||
"@types/node": "^16.11.11",
|
||||
"@typescript-eslint/eslint-plugin": "^4.31.2",
|
||||
"@typescript-eslint/parser": "^4.31.2",
|
||||
"eslint": "^7.25.0",
|
||||
"rimraf": "^3.0.2",
|
||||
"rollup": "^2.26.11",
|
||||
"rollup-plugin-polyfill-node": "^0.8.0",
|
||||
"rollup-plugin-terser": "^5.3.1",
|
||||
"typescript": "^3.8.3"
|
||||
}
|
||||
},
|
||||
"@webassemblyjs/ast": {
|
||||
|
@ -8,7 +8,8 @@
|
||||
"lint": "vue-cli-service lint"
|
||||
},
|
||||
"dependencies": {
|
||||
"@web-eid/web-eid-library": "github:TanelOrumaa/web-eid.js#main",
|
||||
|
||||
"@web-eid/web-eid-library": "../../../../web-eid.js/",
|
||||
"core-js": "^3.6.5",
|
||||
"vue": "^3.0.0",
|
||||
"vue-cookie-next": "^1.3.0",
|
||||
|
@ -1,9 +1,15 @@
|
||||
<template>
|
||||
<div class="container container-md d-flex flex-column">
|
||||
<div>
|
||||
<h3 class="text-center">Welcome to Estonian ID card mobile authentication demo website. When using an Android mobile phone, you can
|
||||
log in to the
|
||||
website using your ID card by using the button below.</h3>
|
||||
<h3 class="text-center">Welcome to Estonian ID card mobile authentication demo website.</h3>
|
||||
<p>This website to demonstrates the viability of using your NFC-enabled ID-card and your smartphone to authenticate yourself.
|
||||
This is a proof of concept solution, so currently only authentication is supported. This solution was created for <a href="https://courses.cs.ut.ee/2021/tvp/">Software Project (Tarkvaraprojekt)</a> course in the University of Tartu
|
||||
in cooperation with <a href="https://github.com/martinpaljak/">Martin Paljak</a>.</p>
|
||||
<p>This solution is meant to be web-eid.js compatible, so this example website uses a <a href="https://github.com/TanelOrumaa/web-eid.js">fork of web-eid.js</a> which supports the Android authentication app.</p>
|
||||
<h2>Usage</h2>
|
||||
<p>To get started, download and install the authentication Android app from <a href="https://github.com/TanelOrumaa/Estonian-ID-card-mobile-authenticator-POC/releases">GitHub</a> (Android 8.0+ required).
|
||||
You can then click "Login" to authenticate yourself on this demo website with the app or if you are using a non-Android device, you can use both the app or the default web-eid.js option to login using the smartcard reader.
|
||||
</p>
|
||||
|
||||
<p class="text-center">Read more from <a href="https://github.com/TanelOrumaa/Estonian-ID-card-mobile-authenticator-POC">here.</a></p>
|
||||
</div>
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -4,7 +4,7 @@ class ApplicationConfiguration {
|
||||
|
||||
companion object {
|
||||
// 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://5a0b-85-253-195-195.ngrok.io"
|
||||
val WEBSITE_ORIGIN_URL = "https://6fa5-145-14-34-146.ngrok.io"
|
||||
|
||||
// Authentication request timeout in seconds.
|
||||
val AUTH_REQUEST_TIMEOUT_MS = 120000
|
||||
|
@ -19,11 +19,10 @@ class SessionManager {
|
||||
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())
|
||||
sessionRegistry[sessionId] = AuthDto(arrayListOf(), hashMapOf(), 200)
|
||||
}
|
||||
}
|
||||
|
||||
@ -37,6 +36,22 @@ class SessionManager {
|
||||
}
|
||||
}
|
||||
|
||||
fun addErrorToSession(sessionId: String?, authDto: AuthDto) {
|
||||
// Errors are only sent by authentication app, so we can ignore sessionId being null.
|
||||
if (sessionRegistry.containsKey(sessionId)) {
|
||||
sessionRegistry[sessionId]!!.errorCode = authDto.errorCode
|
||||
}
|
||||
}
|
||||
|
||||
fun getError(sessionId: String) : Int {
|
||||
if (sessionRegistry.containsKey(sessionId)) {
|
||||
if (sessionRegistry[sessionId]!!.errorCode != 200) {
|
||||
return sessionRegistry[sessionId]!!.errorCode
|
||||
}
|
||||
}
|
||||
return 200
|
||||
}
|
||||
|
||||
/**
|
||||
* Function adds role and userdata specified in authDto to the current session.
|
||||
*/
|
||||
|
@ -2,4 +2,4 @@ package com.tarkvaratehnika.demobackend.dto
|
||||
|
||||
import org.springframework.security.core.GrantedAuthority
|
||||
|
||||
data class AuthDto(var roles: ArrayList<GrantedAuthority>, var userData: HashMap<String, String>)
|
||||
data class AuthDto(var roles: ArrayList<GrantedAuthority>, var userData: HashMap<String, String>, var errorCode: Int)
|
@ -2,4 +2,4 @@ package com.tarkvaratehnika.demobackend.dto
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty
|
||||
|
||||
class AuthTokenDTO (@JsonProperty("auth-token") val token : String)
|
||||
class AuthTokenDTO (@JsonProperty("auth-token") val token : String, val error : Int?)
|
@ -22,12 +22,14 @@
|
||||
|
||||
package com.tarkvaratehnika.demobackend.security
|
||||
|
||||
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
|
||||
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.springframework.http.ResponseEntity
|
||||
import org.webeid.security.certificate.CertificateData
|
||||
|
||||
import org.springframework.security.core.Authentication
|
||||
@ -78,59 +80,46 @@ class WebEidAuthentication(
|
||||
}
|
||||
|
||||
/**
|
||||
* Function for getting a Spring authentication object by supplying a challenge.
|
||||
* TODO: Figure out a more secure solution in the future.
|
||||
* Function for getting a Spring authentication object for this session.
|
||||
*/
|
||||
fun fromSession(headers: HashMap<String, String>): AuthDto {
|
||||
fun fromSession(headers: HashMap<String, String>): ResponseEntity<String> {
|
||||
val mapper = jacksonObjectMapper()
|
||||
|
||||
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.")
|
||||
return ResponseEntity.status(400).body(mapper.writeValueAsString(400))
|
||||
}
|
||||
LOG.warn("SESSION IS NOW: " + sessionId)
|
||||
}
|
||||
|
||||
while (currentTime.time + ApplicationConfiguration.AUTH_REQUEST_TIMEOUT_MS > Date().time) {
|
||||
Thread.sleep(1000)
|
||||
|
||||
// Check if an error has been submitted for this session.
|
||||
val error = SessionManager.getError(sessionId)
|
||||
if (error != 200) {
|
||||
return ResponseEntity.status(error).body(mapper.writeValueAsString(error))
|
||||
}
|
||||
|
||||
// Check if this session has received a role.
|
||||
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
|
||||
return ResponseEntity.status(200).body(mapper.writeValueAsString(auth))
|
||||
}
|
||||
|
||||
}
|
||||
// if (ThreadLocalRandom.current().nextFloat() < 0.5f) { // TODO: For testing.
|
||||
// return null
|
||||
// }
|
||||
throw ResponseStatusException(HttpStatus.REQUEST_TIMEOUT, "Token not received in time.")
|
||||
}
|
||||
|
||||
// // TODO: DELETE
|
||||
//
|
||||
// const val ROLE_USER: String = "ROLE_USER"
|
||||
// private val USER_ROLE: GrantedAuthority = SimpleGrantedAuthority(ROLE_USER)
|
||||
//
|
||||
// fun addAuth(challenge: String) {
|
||||
// val authorities = arrayListOf<GrantedAuthority>()
|
||||
// authorities.add(USER_ROLE)
|
||||
// val auth = WebEidAuthentication("Somename", "11111111111", authorities)
|
||||
// loggedInUsers[challenge] = auth
|
||||
// }
|
||||
//
|
||||
//
|
||||
// // TODO: DELETE UNTIL
|
||||
// In case of timeout return 408.
|
||||
return ResponseEntity.status(408).body(mapper.writeValueAsString(408))
|
||||
}
|
||||
|
||||
private fun getPrincipalNameFromCertificate(userCertificate: X509Certificate): String {
|
||||
return Objects.requireNonNull(CertificateData.getSubjectGivenName(userCertificate)) + " " +
|
||||
|
@ -1,5 +1,6 @@
|
||||
package com.tarkvaratehnika.demobackend.web.rest
|
||||
|
||||
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
|
||||
import com.tarkvaratehnika.demobackend.config.SessionManager
|
||||
import com.tarkvaratehnika.demobackend.dto.AuthDto
|
||||
import com.tarkvaratehnika.demobackend.dto.AuthTokenDTO
|
||||
@ -8,6 +9,7 @@ import com.tarkvaratehnika.demobackend.security.WebEidAuthentication
|
||||
import org.slf4j.LoggerFactory
|
||||
import org.springframework.http.HttpStatus
|
||||
import org.springframework.http.MediaType
|
||||
import org.springframework.http.ResponseEntity
|
||||
import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken
|
||||
import org.springframework.web.bind.annotation.*
|
||||
|
||||
@ -19,12 +21,18 @@ class AuthenticationController {
|
||||
|
||||
|
||||
@PostMapping("login", consumes = [MediaType.APPLICATION_JSON_VALUE], produces = [MediaType.APPLICATION_JSON_VALUE])
|
||||
fun authenticate(@RequestHeader headers: Map<String, String>, @RequestBody body : AuthTokenDTO): AuthDto {
|
||||
|
||||
fun authenticate(@RequestHeader headers: Map<String, String>, @RequestBody authTokenDTO: AuthTokenDTO): AuthDto {
|
||||
val sessionId = SessionManager.getSessionId(headers)
|
||||
|
||||
// Check if an error occurred in the auth app.
|
||||
if (authTokenDTO.error != null && authTokenDTO.error != 200) {
|
||||
val auth = AuthDto(arrayListOf(), hashMapOf(), authTokenDTO.error)
|
||||
SessionManager.addErrorToSession(sessionId, auth)
|
||||
return auth
|
||||
}
|
||||
|
||||
// Create Spring Security Authentication object with supplied token as credentials.
|
||||
val auth = PreAuthenticatedAuthenticationToken(null, body)
|
||||
val auth = PreAuthenticatedAuthenticationToken(null, authTokenDTO)
|
||||
|
||||
// Return authentication object if success.
|
||||
return AuthTokenDTOAuthenticationProvider.authenticate(auth, sessionId)
|
||||
@ -32,7 +40,7 @@ class AuthenticationController {
|
||||
|
||||
|
||||
@GetMapping("login", produces = [MediaType.APPLICATION_JSON_VALUE])
|
||||
fun getAuthenticated(@RequestHeader headers: HashMap<String, String>) : AuthDto {
|
||||
fun getAuthenticated(@RequestHeader headers: HashMap<String, String>) : ResponseEntity<String> {
|
||||
return WebEidAuthentication.fromSession(headers)
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user