diff --git a/api-webapp/pom.xml b/api-webapp/pom.xml
index 4ddf8bc..4e54e7a 100644
--- a/api-webapp/pom.xml
+++ b/api-webapp/pom.xml
@@ -16,7 +16,6 @@
PairGoth pairing system
TODO
- 5.7.1
package
@@ -166,9 +165,9 @@
- org.pac4j
- pac4j-oauth
- ${pac4j.version}
+ commons-codec
+ commons-codec
+ 1.16.1
diff --git a/api-webapp/src/main/kotlin/org/jeudego/pairgoth/api/TokenHandler.kt b/api-webapp/src/main/kotlin/org/jeudego/pairgoth/api/TokenHandler.kt
index 654edb9..a4d6e94 100644
--- a/api-webapp/src/main/kotlin/org/jeudego/pairgoth/api/TokenHandler.kt
+++ b/api-webapp/src/main/kotlin/org/jeudego/pairgoth/api/TokenHandler.kt
@@ -1,6 +1,8 @@
package org.jeudego.pairgoth.api
import com.republicate.kson.Json
+import org.jeudego.pairgoth.util.AESCryptograph
+import org.jeudego.pairgoth.util.Cryptograph
import javax.servlet.http.HttpServletRequest
import javax.servlet.http.HttpServletResponse
import javax.servlet.http.HttpSession
@@ -9,11 +11,14 @@ class TokenHandler: ApiHandler {
companion object {
const val AUTH_KEY = "pairgoth-auth"
const val CHALLENGE_KEY = "pairgoth-challenge"
+ private val cryptograph: Cryptograph = AESCryptograph().apply {
+ init("78659783ed8ccc0e")
+ }
}
override fun get(request: HttpServletRequest, response: HttpServletResponse): Json? {
val auth = request.session.getAttribute(AUTH_KEY) as String?
if (auth == null) {
- failed(request.session, response)
+ failed(request, response)
return null
} else {
return Json.Object(
@@ -25,19 +30,26 @@ class TokenHandler: ApiHandler {
override fun post(request: HttpServletRequest, response: HttpServletResponse): Json {
val auth = getObjectPayload(request)
val answer = auth.getString("answer")
- if (answer == null) {
- failed(request.session, response)
+ val challenge = request.session.getAttribute(CHALLENGE_KEY) as AuthChallenge?
+ if (answer == null || challenge == null) {
+ failed(request, response)
} else {
-
-
+ val parts = cryptograph.webDecrypt(answer).split(":")
+ if (parts.size != 2)
}
}
- private fun failed(session: HttpSession, response: HttpServletResponse) {
+ override fun delete(request: HttpServletRequest, response: HttpServletResponse): Json {
+ request.session.removeAttribute(AUTH_KEY)
+ return Json.Object("success" to true)
+ }
+
+ private fun failed(request: HttpServletRequest, response: HttpServletResponse) {
+ val session = request.session
val challenge = AuthChallenge()
session.setAttribute(CHALLENGE_KEY, challenge)
response.addHeader("WWW-Authenticate", challenge.value)
response.status = HttpServletResponse.SC_UNAUTHORIZED
- response.writer.println(Json.Object("status" to "failed"))
+ response.writer.println(Json.Object("status" to "failed", "message" to "unauthorized"))
}
}
diff --git a/api-webapp/src/main/kotlin/org/jeudego/pairgoth/server/AESCryptograph.kt b/api-webapp/src/main/kotlin/org/jeudego/pairgoth/server/AESCryptograph.kt
deleted file mode 100644
index d202a2e..0000000
--- a/api-webapp/src/main/kotlin/org/jeudego/pairgoth/server/AESCryptograph.kt
+++ /dev/null
@@ -1,57 +0,0 @@
-package org.jeudego.pairgoth.server
-
-import java.nio.charset.Charset
-import javax.crypto.Cipher
-import javax.crypto.Cipher.DECRYPT_MODE
-import javax.crypto.Cipher.ENCRYPT_MODE
-import javax.crypto.SecretKey
-import javax.crypto.spec.SecretKeySpec
-
-
-/**
- * Basic AES encryption. Please note that it uses the ECB block mode, which has the advantage
- * to not require random bytes, thus providing some *persistence* for the encrypted data, but
- * at the expense of some security weaknesses. The purpose here is just to encrypt temporary
- * session ids in URLs, not to protect state secrets.
- */
-class AESCryptograph : Cryptograph {
-
- override fun init(key: String) {
- val bytes = key.toByteArray(Charset.defaultCharset())
- if (bytes.size < 16) {
- throw Error("not enough secret bytes")
- }
- val secret: SecretKey = SecretKeySpec(bytes, 0, 16, ALGORITHM)
- try {
- encrypt.init(ENCRYPT_MODE, secret)
- decrypt.init(DECRYPT_MODE, secret)
- } catch (e: Exception) {
- throw RuntimeException("cyptograph initialization failed", e)
- }
- }
-
- override fun encrypt(str: String): ByteArray {
- return try {
- encrypt.doFinal(str.toByteArray(Charset.defaultCharset()))
- } catch (e: Exception) {
- throw RuntimeException("encryption failed failed", e)
- }
- }
-
- override fun decrypt(bytes: ByteArray): String {
- return try {
- String(decrypt.doFinal(bytes), Charset.defaultCharset())
- } catch (e: Exception) {
- throw RuntimeException("encryption failed failed", e)
- }
- }
-
- private var encrypt = Cipher.getInstance(CIPHER)
- private var decrypt = Cipher.getInstance(CIPHER)
-
- companion object {
- private val CIPHER = "AES/ECB/PKCS5Padding"
- private val ALGORITHM = "AES"
-
- }
-}
diff --git a/api-webapp/src/main/kotlin/org/jeudego/pairgoth/server/Cryptograph.kt b/api-webapp/src/main/kotlin/org/jeudego/pairgoth/server/Cryptograph.kt
deleted file mode 100644
index b3a34ed..0000000
--- a/api-webapp/src/main/kotlin/org/jeudego/pairgoth/server/Cryptograph.kt
+++ /dev/null
@@ -1,29 +0,0 @@
-package org.jeudego.pairgoth.server
-
-import java.io.Serializable
-
-/**
- * Cryptograph - used to encrypt and decrypt strings.
- *
- */
-interface Cryptograph : Serializable {
- /**
- * init.
- * @param random random string
- */
- fun init(random: String)
-
- /**
- * encrypt.
- * @param str string to encrypt
- * @return encrypted string
- */
- fun encrypt(str: String): ByteArray
-
- /**
- * decrypt.
- * @param bytes to decrypt
- * @return decrypted string
- */
- fun decrypt(bytes: ByteArray): String
-}
diff --git a/pairgoth-common/pom.xml b/pairgoth-common/pom.xml
index 4b545b7..7f668b3 100644
--- a/pairgoth-common/pom.xml
+++ b/pairgoth-common/pom.xml
@@ -76,6 +76,11 @@
kotlinx-datetime-jvm
0.4.0
+
+ commons-codec
+ commons-codec
+ 1.16.1
+
jakarta.servlet
diff --git a/pairgoth-common/src/main/kotlin/org/jeudego/pairgoth/util/Cryptograph.kt b/pairgoth-common/src/main/kotlin/org/jeudego/pairgoth/util/Cryptograph.kt
index 2a221d0..b9d0f34 100644
--- a/pairgoth-common/src/main/kotlin/org/jeudego/pairgoth/util/Cryptograph.kt
+++ b/pairgoth-common/src/main/kotlin/org/jeudego/pairgoth/util/Cryptograph.kt
@@ -1,5 +1,6 @@
package org.jeudego.pairgoth.util
+import org.apache.commons.codec.binary.Base64
import java.io.Serializable
/**
@@ -26,4 +27,10 @@ interface Cryptograph : Serializable {
* @return decrypted string
*/
fun decrypt(bytes: ByteArray): String
+
+
+ fun webEncrypt(str: String) = Base64.encodeBase64URLSafeString(encrypt(str))
+
+ fun webDecrypt(str: String) = decrypt(Base64.decodeBase64(str))
+
}
diff --git a/view-webapp/pom.xml b/view-webapp/pom.xml
index a96c5a8..af1f18d 100644
--- a/view-webapp/pom.xml
+++ b/view-webapp/pom.xml
@@ -16,7 +16,6 @@
PairGoth pairing system
TODO
- 5.7.1
9.9.0
@@ -184,9 +183,9 @@
- org.pac4j
- pac4j-oauth
- ${pac4j.version}
+ commons-codec
+ commons-codec
+ 1.16.1
diff --git a/view-webapp/src/main/kotlin/org/jeudego/pairgoth/oauth/OAuthHelper.kt b/view-webapp/src/main/kotlin/org/jeudego/pairgoth/oauth/OAuthHelper.kt
index 7cdf5fa..27665cc 100644
--- a/view-webapp/src/main/kotlin/org/jeudego/pairgoth/oauth/OAuthHelper.kt
+++ b/view-webapp/src/main/kotlin/org/jeudego/pairgoth/oauth/OAuthHelper.kt
@@ -6,7 +6,6 @@ import com.republicate.kson.Json
import org.jeudego.pairgoth.web.WebappManager
//import com.republicate.modality.util.AESCryptograph
//import com.republicate.modality.util.Cryptograph
-import org.apache.commons.codec.binary.Base64
import org.apache.http.NameValuePair
import org.jeudego.pairgoth.util.AESCryptograph
import org.jeudego.pairgoth.util.ApiClient.JsonApiClient
@@ -14,8 +13,6 @@ import org.jeudego.pairgoth.util.Cryptograph
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import java.io.IOException
-import java.io.UnsupportedEncodingException
-import java.net.URLEncoder
abstract class OAuthHelper {
abstract val name: String
@@ -28,20 +25,22 @@ abstract class OAuthHelper {
get() = WebappManager.getMandatoryProperty("webapp.external.url").removeSuffix("/") + "/oauth/${name}"
protected fun getState(sessionId: String): String {
- return name + ":" + encrypt(sessionId)
+ return name + ":" + cryptograph.webEncrypt(sessionId)
}
fun checkState(state: String, expectedSessionId: String): Boolean {
- val foundSessionId = decrypt(state)
+ val foundSessionId = cryptograph.webDecrypt(state)
return expectedSessionId == foundSessionId
}
protected abstract fun getAccessTokenURL(code: String): Pair>
@Throws(IOException::class)
- fun getAccessToken(code: String): String {
+ fun getAccessToken(sessionID: String, code: String): String {
val (url, params) = getAccessTokenURL(code)
val json = JsonApiClient.post(url, null, *params.toTypedArray()).asObject()
+ val state = json.getString("state") ?: throw IOException("could not get state")
+ if (!checkState(state, sessionID)) throw IOException("invalid state")
return json.getString("access_token") ?: throw IOException("could not get access token")
}
@@ -55,17 +54,8 @@ abstract class OAuthHelper {
companion object {
protected var logger: Logger = LoggerFactory.getLogger("oauth")
- private const val salt = "0efd28fb53cbac42"
- private val sessionIdCrypto: Cryptograph = AESCryptograph().apply {
- init(salt)
- }
-
- private fun encrypt(input: String): String {
- return Base64.encodeBase64URLSafeString(sessionIdCrypto.encrypt(input))
- }
-
- private fun decrypt(input: String): String {
- return sessionIdCrypto.decrypt(Base64.decodeBase64(input))
+ private val cryptograph: Cryptograph = AESCryptograph().apply {
+ init("0efd28fb53cbac42")
}
}
}
\ No newline at end of file
diff --git a/view-webapp/src/main/kotlin/org/jeudego/pairgoth/web/AuthFilter.kt b/view-webapp/src/main/kotlin/org/jeudego/pairgoth/web/AuthFilter.kt
index f08922b..3fbbfb6 100644
--- a/view-webapp/src/main/kotlin/org/jeudego/pairgoth/web/AuthFilter.kt
+++ b/view-webapp/src/main/kotlin/org/jeudego/pairgoth/web/AuthFilter.kt
@@ -34,7 +34,7 @@ class AuthFilter: Filter {
if (auth == "oauth" && uri.startsWith("/oauth/")) {
val provider = uri.substring("/oauth/".length)
val helper = OauthHelperFactory.getHelper(provider)
- val accessToken = helper.getAccessToken(request.getParameter("code") ?: "")
+ val accessToken = helper.getAccessToken(request.session.id, request.getParameter("code") ?: "")
val user = helper.getUserInfos(accessToken)
request.session.setAttribute("logged", user)
response.sendRedirect("/index")