add gigawear auth repository
This commit is contained in:
@@ -10,7 +10,7 @@ sqldelight {
|
||||
databases {
|
||||
create("AppDatabase") {
|
||||
packageName.set("ru.shadowsparky.gigawear.backend")
|
||||
dialect("app.cash.sqldelight:postgresql-dialect:2.0.2")
|
||||
dialect("app.cash.sqldelight:postgresql-dialect:2.3.2")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+2
-1
@@ -6,8 +6,9 @@ import org.koin.core.annotation.Single
|
||||
import ru.shadowsparky.backend.data.EnvFetcher
|
||||
import ru.shadowsparky.backend.domain.JwtInfo
|
||||
import ru.shadowsparky.domain.AppConfig
|
||||
import ru.shadowsparky.gigawear.backend.di.DbModule
|
||||
|
||||
@Module
|
||||
@Module(includes = [DbModule::class])
|
||||
@ComponentScan
|
||||
@Configuration
|
||||
class BackendModule {
|
||||
|
||||
@@ -4,13 +4,17 @@ import io.ktor.http.HttpStatusCode
|
||||
import io.ktor.serialization.kotlinx.json.json
|
||||
import io.ktor.server.application.Application
|
||||
import io.ktor.server.application.install
|
||||
import io.ktor.server.auth.authenticate
|
||||
import io.ktor.server.plugins.contentnegotiation.ContentNegotiation
|
||||
import io.ktor.server.response.respond
|
||||
import io.ktor.server.routing.get
|
||||
import io.ktor.server.routing.routing
|
||||
import org.koin.core.annotation.KoinApplication
|
||||
import org.koin.plugin.module.dsl.startKoin
|
||||
import ru.shadowsparky.gigawear.backend.presentation.MOCK_USER_ID
|
||||
import ru.shadowsparky.backend.presentation.AUTH_JWT_NAME
|
||||
import ru.shadowsparky.backend.presentation.configureJwt
|
||||
import ru.shadowsparky.backend.presentation.installStatusPages
|
||||
import ru.shadowsparky.gigawear.backend.presentation.setupAuthRouting
|
||||
import ru.shadowsparky.gigawear.backend.presentation.setupChat
|
||||
import ru.shadowsparky.koin
|
||||
import ru.shadowsparky.updater.backend.presentation.setupUpdates
|
||||
@@ -25,9 +29,14 @@ fun main(args: Array<String>) {
|
||||
|
||||
fun Application.module() {
|
||||
install(ContentNegotiation) { json(koin.get()) }
|
||||
configureJwt(koin.get(), koin.get(), koin.get())
|
||||
installStatusPages()
|
||||
routing {
|
||||
get("health") { this.call.respond(HttpStatusCode.OK) }
|
||||
setupChat(koin.get())
|
||||
setupUpdates(koin.get()) { MOCK_USER_ID }
|
||||
setupAuthRouting(koin.get())
|
||||
authenticate(AUTH_JWT_NAME) {
|
||||
get("healthCheck") { this.call.respond(HttpStatusCode.OK) }
|
||||
setupChat(koin.get())
|
||||
setupUpdates(koin.get())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+72
@@ -0,0 +1,72 @@
|
||||
package ru.shadowsparky.gigawear.backend.data
|
||||
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.koin.core.annotation.Factory
|
||||
import ru.shadowsparky.backend.data.JwtPreparer
|
||||
import ru.shadowsparky.backend.data.StringFetcher
|
||||
import ru.shadowsparky.backend.data.StringResource
|
||||
import ru.shadowsparky.backend.data.generateRefreshToken
|
||||
import ru.shadowsparky.backend.data.toTokenHash
|
||||
import ru.shadowsparky.backend.domain.LoginVerifier
|
||||
import ru.shadowsparky.domain.DispatcherProvider
|
||||
import ru.shadowsparky.gigawear.backend.AppDatabase
|
||||
import ru.shadowsparky.gigawear.common.AuthRepository
|
||||
import ru.shadowsparky.gigawear.common.LoginRequest
|
||||
import ru.shadowsparky.http.domain.BadRequestException
|
||||
import ru.shadowsparky.http.domain.TokenInfo
|
||||
import java.time.ZoneOffset
|
||||
import kotlin.time.Clock
|
||||
import kotlin.time.Duration.Companion.days
|
||||
import kotlin.time.toJavaInstant
|
||||
|
||||
@Factory
|
||||
class BackendAuthRepositoryImpl(
|
||||
appDatabase: AppDatabase,
|
||||
private val stringFetcher: StringFetcher,
|
||||
private val loginVerifier: LoginVerifier,
|
||||
private val jwtPreparer: JwtPreparer,
|
||||
private val dispatcherProvider: DispatcherProvider
|
||||
) : AuthRepository {
|
||||
private val usersQueries = appDatabase.usersQueries
|
||||
private val tokenQueries = appDatabase.refresh_tokensQueries
|
||||
|
||||
override suspend fun login(request: LoginRequest) = withContext(dispatcherProvider.io) {
|
||||
val userCode = request.code
|
||||
val user = usersQueries.selectByCode(userCode).executeAsOneOrNull()
|
||||
?: throw BadRequestException(stringFetcher.get(StringResource.UNKNOWN_USER))
|
||||
loginVerifier.verify(user.user_id)
|
||||
create(user.user_id)
|
||||
}
|
||||
|
||||
override suspend fun update(oldTokenInfo: TokenInfo) = withContext(dispatcherProvider.io) {
|
||||
checkNotNull(oldTokenInfo.refresh)
|
||||
val tokenHash = oldTokenInfo.refresh!!.toTokenHash(SALT)
|
||||
val savedToken = tokenQueries.selectByHash(tokenHash)
|
||||
.executeAsOneOrNull()
|
||||
?: throw BadRequestException("Token not found")
|
||||
val now = Clock.System.now().toJavaInstant().atOffset(ZoneOffset.UTC)
|
||||
if (savedToken.revoked || savedToken.expires_at < now) {
|
||||
throw BadRequestException("Expired token")
|
||||
}
|
||||
tokenQueries.revokeById(savedToken.token_id)
|
||||
val user = usersQueries.selectById(savedToken.user_id).executeAsOneOrNull()
|
||||
?: throw BadRequestException(stringFetcher.get(StringResource.UNKNOWN_USER))
|
||||
create(user.user_id)
|
||||
}
|
||||
|
||||
private suspend fun create(userId: Long): TokenInfo {
|
||||
val newAccessToken = jwtPreparer.prepare(userId)
|
||||
val expiresAt = Clock.System.now() + 30.days
|
||||
val refreshToken = generateRefreshToken()
|
||||
tokenQueries.insertToken(
|
||||
user_id = userId,
|
||||
token_hash = refreshToken.toTokenHash(SALT),
|
||||
expires_at = expiresAt.toJavaInstant().atOffset(ZoneOffset.UTC)
|
||||
).await()
|
||||
return TokenInfo(newAccessToken, refreshToken)
|
||||
}
|
||||
|
||||
private companion object {
|
||||
const val SALT = "[=TkATMHb!`o}II{=/|!n?06?~z%h=5POwK|\$UUcj,mHI=\$R4<p;!D-=!14|EV%M"
|
||||
}
|
||||
}
|
||||
+17
-2
@@ -1,10 +1,25 @@
|
||||
package ru.shadowsparky.gigawear.backend.data
|
||||
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.koin.core.annotation.Factory
|
||||
import ru.shadowsparky.backend.data.StringFetcher
|
||||
import ru.shadowsparky.backend.data.StringResource
|
||||
import ru.shadowsparky.backend.domain.LoginVerifier
|
||||
import ru.shadowsparky.domain.DispatcherProvider
|
||||
import ru.shadowsparky.gigawear.backend.AppDatabase
|
||||
import ru.shadowsparky.http.domain.BadRequestException
|
||||
|
||||
@Factory
|
||||
class LoginVerifierImpl : LoginVerifier {
|
||||
override suspend fun verify(login: String) {
|
||||
class LoginVerifierImpl(
|
||||
private val appDatabase: AppDatabase,
|
||||
private val dispatcherProvider: DispatcherProvider,
|
||||
private val stringFetcher: StringFetcher
|
||||
) : LoginVerifier {
|
||||
|
||||
override suspend fun verify(userId: Long) {
|
||||
withContext(dispatcherProvider.io) {
|
||||
appDatabase.usersQueries.selectById(userId).executeAsOneOrNull()
|
||||
?: throw BadRequestException(stringFetcher.get(StringResource.UNKNOWN_USER))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
package ru.shadowsparky.gigawear.backend.di
|
||||
|
||||
import app.cash.sqldelight.db.SqlDriver
|
||||
import app.cash.sqldelight.driver.jdbc.asJdbcDriver
|
||||
import org.koin.core.annotation.Factory
|
||||
import org.koin.core.annotation.Module
|
||||
import org.koin.core.annotation.Single
|
||||
import org.postgresql.ds.PGSimpleDataSource
|
||||
import ru.shadowsparky.backend.data.EnvFetcher
|
||||
import ru.shadowsparky.gigawear.backend.AppDatabase
|
||||
|
||||
@Module
|
||||
class DbModule {
|
||||
|
||||
@Factory
|
||||
fun provideSqlDriver(envFetcher: EnvFetcher): SqlDriver {
|
||||
return PGSimpleDataSource().apply {
|
||||
val db = envFetcher.get("POSTGRES_DB", "gigawear")
|
||||
val uri = envFetcher.get("VIDEOBOX_SERVER_URI", "localhost")
|
||||
val login = envFetcher.get("POSTGRES_USER", "login")
|
||||
val pass = envFetcher.get("POSTGRES_PASSWORD", "password")
|
||||
setUrl("jdbc:postgresql://$uri:5432/$db")
|
||||
user = login
|
||||
password = pass
|
||||
}.asJdbcDriver()
|
||||
}
|
||||
|
||||
@Single
|
||||
fun provideDb(driver: SqlDriver): AppDatabase {
|
||||
return AppDatabase.invoke(driver)
|
||||
}
|
||||
}
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
package ru.shadowsparky.gigawear.backend.presentation
|
||||
|
||||
import io.ktor.server.request.receive
|
||||
import io.ktor.server.response.respond
|
||||
import io.ktor.server.routing.Route
|
||||
import io.ktor.server.routing.post
|
||||
import ru.shadowsparky.gigawear.common.AuthRepository
|
||||
|
||||
fun Route.setupAuthRouting(authRepository: AuthRepository) {
|
||||
post(AuthRepository.LOGIN_PATH) {
|
||||
call.respond(authRepository.login(call.receive()))
|
||||
}
|
||||
post(AuthRepository.UPDATE_TOKEN_PATH) {
|
||||
call.respond(authRepository.update(call.receive()))
|
||||
}
|
||||
}
|
||||
+2
-3
@@ -4,14 +4,13 @@ import io.ktor.server.request.receive
|
||||
import io.ktor.server.response.respond
|
||||
import io.ktor.server.routing.Route
|
||||
import io.ktor.server.routing.post
|
||||
import ru.shadowsparky.backend.presentation.obtainUserId
|
||||
import ru.shadowsparky.chat.backend.domain.ProcessUserMessageUseCase
|
||||
import ru.shadowsparky.chat.domain.Message
|
||||
|
||||
const val MOCK_USER_ID = -1L
|
||||
|
||||
fun Route.setupChat(useCase: ProcessUserMessageUseCase) {
|
||||
post("chat") {
|
||||
val message = call.receive<Message>().copy(timestamp = System.currentTimeMillis())
|
||||
call.respond(useCase.execute(message, MOCK_USER_ID))
|
||||
call.respond(useCase.execute(message, call.obtainUserId()))
|
||||
}
|
||||
}
|
||||
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
CREATE TABLE refresh_tokens (
|
||||
token_id int8 NOT NULL GENERATED BY DEFAULT AS IDENTITY,
|
||||
user_id int8 NOT NULL,
|
||||
token_hash varchar NOT NULL,
|
||||
expires_at TIMESTAMPTZ NOT NULL,
|
||||
revoked bool NOT NULL
|
||||
);
|
||||
|
||||
insertToken:
|
||||
INSERT INTO refresh_tokens (user_id, token_hash, expires_at, revoked)
|
||||
VALUES (?, ?, ?, FALSE);
|
||||
|
||||
selectByHash:
|
||||
SELECT token_id, user_id, token_hash, expires_at, revoked
|
||||
FROM refresh_tokens
|
||||
WHERE token_hash = ?;
|
||||
|
||||
revokeById:
|
||||
UPDATE refresh_tokens
|
||||
SET revoked = TRUE
|
||||
WHERE token_id = ?;
|
||||
@@ -0,0 +1,23 @@
|
||||
CREATE TABLE "users" (
|
||||
user_id int8 NOT NULL,
|
||||
login varchar NOT NULL,
|
||||
code int,
|
||||
CONSTRAINT user_id_pk PRIMARY KEY (user_id),
|
||||
CONSTRAINT login_unique UNIQUE (login),
|
||||
CONSTRAINT code_unique UNIQUE (code)
|
||||
);
|
||||
|
||||
selectByCode:
|
||||
SELECT user_id, login, code
|
||||
FROM "users"
|
||||
WHERE code = ?;
|
||||
|
||||
selectById:
|
||||
SELECT user_id, login, code
|
||||
FROM "users"
|
||||
WHERE user_id = ?;
|
||||
|
||||
selectByLogin:
|
||||
SELECT user_id, login, code
|
||||
FROM "users"
|
||||
WHERE login = ?;
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
package ru.shadowsparky.gigawear.common
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
import ru.shadowsparky.http.domain.TokenInfo
|
||||
|
||||
interface AuthRepository {
|
||||
suspend fun login(request: LoginRequest): TokenInfo
|
||||
suspend fun update(oldTokenInfo: TokenInfo): TokenInfo
|
||||
|
||||
companion object {
|
||||
const val LOGIN_PATH = "login"
|
||||
const val UPDATE_TOKEN_PATH = "updateToken"
|
||||
}
|
||||
}
|
||||
|
||||
@Serializable
|
||||
data class LoginRequest(
|
||||
val login: String,
|
||||
val code: Int
|
||||
)
|
||||
@@ -10,7 +10,7 @@ sqldelight {
|
||||
databases {
|
||||
create("AppDatabase") {
|
||||
packageName.set("ru.shadowsparky.vbox.backend")
|
||||
dialect("app.cash.sqldelight:postgresql-dialect:2.0.2")
|
||||
dialect("app.cash.sqldelight:postgresql-dialect:2.3.2")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+4
-8
@@ -6,6 +6,7 @@ import ru.shadowsparky.backend.data.EnvFetcher
|
||||
import ru.shadowsparky.backend.data.JwtPreparer
|
||||
import ru.shadowsparky.backend.data.StringFetcher
|
||||
import ru.shadowsparky.backend.data.StringResource
|
||||
import ru.shadowsparky.backend.data.toTokenHash
|
||||
import ru.shadowsparky.backend.domain.LoginVerifier
|
||||
import ru.shadowsparky.domain.Log
|
||||
import ru.shadowsparky.http.domain.BadRequestException
|
||||
@@ -17,10 +18,8 @@ import ru.shadowsparky.vbox.shared.domain.AuthTokenRepository
|
||||
import ru.shadowsparky.vbox.shared.domain.ChangePasswordRequest
|
||||
import ru.shadowsparky.vbox.shared.domain.TokenRequest
|
||||
import ru.shadowsparky.vbox.shared.domain.model.LoginInfo
|
||||
import java.security.MessageDigest
|
||||
import java.security.SecureRandom
|
||||
import java.util.concurrent.TimeUnit
|
||||
import kotlin.io.encoding.Base64
|
||||
|
||||
@Factory
|
||||
class AuthTokenRepositoryFactory(
|
||||
@@ -131,10 +130,10 @@ class BackendAuthTokenRepository(
|
||||
}
|
||||
|
||||
private suspend fun create(login: String): TokenInfo = withContext(dispatcherProvider.io) {
|
||||
loginVerifier.verify(login)
|
||||
val userInfo = userInfoCache.query(login)
|
||||
?: throw BadRequestException(stringFetcher.get(StringResource.UNKNOWN_USER))
|
||||
val rsp = jwtPreparer.prepare(login, userInfo.userId)
|
||||
loginVerifier.verify(userInfo.userId)
|
||||
val rsp = jwtPreparer.prepare(userInfo.userId)
|
||||
val refresh = ByteArray(32)
|
||||
random.nextBytes(refresh)
|
||||
val refreshStr = refresh.toHexString()
|
||||
@@ -164,10 +163,7 @@ class BackendAuthTokenRepository(
|
||||
}
|
||||
|
||||
private fun String.toHash(): String {
|
||||
val md = MessageDigest.getInstance("SHA-256")
|
||||
md.update(SALT.toByteArray())
|
||||
val result = md.digest(this.toByteArray())
|
||||
return Base64.encode(result)
|
||||
return toTokenHash(SALT)
|
||||
}
|
||||
|
||||
private companion object {
|
||||
|
||||
+2
-2
@@ -12,8 +12,8 @@ class BackendLoginVerifier(
|
||||
private val stringFetcher: StringFetcher,
|
||||
private val userInfoCache: UserInfoCache
|
||||
) : LoginVerifier {
|
||||
override suspend fun verify(login: String) {
|
||||
val user = userInfoCache.query(login)
|
||||
override suspend fun verify(userId: Long) {
|
||||
val user = userInfoCache.query(userId)
|
||||
?: throw VerifyTokenException(stringFetcher.get(StringResource.UNKNOWN_USER))
|
||||
user.flags?.let {
|
||||
if ((it and FLAG_USER_BLOCKED) != 0) {
|
||||
|
||||
@@ -30,4 +30,4 @@ class DbModule {
|
||||
fun provideDb(driver: SqlDriver): AppDatabase {
|
||||
return AppDatabase.Companion.invoke(driver)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ import ru.shadowsparky.backend.domain.JwtInfo
|
||||
import ru.shadowsparky.backend.domain.LoginVerifier
|
||||
import ru.shadowsparky.backend.domain.RemoteEventHandler
|
||||
import ru.shadowsparky.chat.backend.domain.ProcessUserMessageUseCase
|
||||
import ru.shadowsparky.vbox.backend.data.BackendUpdateFetcherFactory
|
||||
import ru.shadowsparky.updater.backend.data.BackendUpdateFetcherFactory
|
||||
import ru.shadowsparky.vbox.backend.data.auth.AuthTokenRepositoryFactory
|
||||
import ru.shadowsparky.vbox.backend.data.chat.BackendChatRepositoryFactory
|
||||
import ru.shadowsparky.vbox.backend.data.tags.MovieTagRepositoryFactory
|
||||
|
||||
@@ -6,8 +6,6 @@ import ru.shadowsparky.backend.presentation.configureJwt
|
||||
import ru.shadowsparky.backend.presentation.obtainUserId
|
||||
import ru.shadowsparky.vbox.backend.di.AuthEntryPoint
|
||||
|
||||
const val AUTH_JWT_NAME = "auth-jwt"
|
||||
|
||||
fun Application.configureJwt(authEntryPoint: AuthEntryPoint) = with(authEntryPoint) {
|
||||
configureJwt(jwtInfo, tokenVerifier, loginVerifier)
|
||||
}
|
||||
|
||||
+1
-1
@@ -7,9 +7,9 @@ import io.ktor.server.response.respond
|
||||
import io.ktor.server.routing.Routing
|
||||
import io.ktor.server.routing.get
|
||||
import io.ktor.server.routing.post
|
||||
import ru.shadowsparky.backend.presentation.AUTH_JWT_NAME
|
||||
import ru.shadowsparky.vbox.backend.di.AuthEntryPoint
|
||||
import ru.shadowsparky.vbox.backend.di.RoutingEntryPoint
|
||||
import ru.shadowsparky.vbox.backend.presentation.AUTH_JWT_NAME
|
||||
import ru.shadowsparky.vbox.backend.presentation.obtainUserId
|
||||
import ru.shadowsparky.vbox.shared.domain.AuthTokenRepository
|
||||
import ru.shadowsparky.vbox.shared.domain.HealthCheck
|
||||
|
||||
@@ -4,7 +4,6 @@ import com.auth0.jwt.JWT
|
||||
import com.auth0.jwt.algorithms.Algorithm
|
||||
import org.koin.core.annotation.Single
|
||||
import ru.shadowsparky.backend.domain.JwtInfo
|
||||
import ru.shadowsparky.backend.domain.LOGIN_NAME
|
||||
import ru.shadowsparky.backend.domain.USER_ID_ARG
|
||||
import java.util.Date
|
||||
import java.util.concurrent.TimeUnit
|
||||
@@ -15,15 +14,11 @@ class JwtPreparer(
|
||||
private val envFetcher: EnvFetcher
|
||||
) {
|
||||
private val tokenTimeInMinutes = envFetcher.get("JWT_DURATION", "30").toLong()
|
||||
fun prepare(
|
||||
login: String,
|
||||
userId: Long
|
||||
): String {
|
||||
fun prepare(userId: Long): String {
|
||||
val exp = Date(System.currentTimeMillis() + TimeUnit.MINUTES.toMillis(tokenTimeInMinutes))
|
||||
val token = JWT.create()
|
||||
.withAudience(jwtInfo.audience)
|
||||
.withIssuer(jwtInfo.issuer)
|
||||
.withClaim(LOGIN_NAME, login)
|
||||
.withClaim(USER_ID_ARG, userId)
|
||||
.withSubject(userId.toString())
|
||||
.withExpiresAt(exp)
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
package ru.shadowsparky.backend.data
|
||||
|
||||
import java.security.MessageDigest
|
||||
import java.security.SecureRandom
|
||||
import kotlin.io.encoding.Base64
|
||||
|
||||
fun generateRefreshToken(): String {
|
||||
val refresh = ByteArray(32)
|
||||
SecureRandom().nextBytes(refresh)
|
||||
return refresh.toHexString()
|
||||
}
|
||||
|
||||
fun String.toTokenHash(salt: String): String {
|
||||
val md = MessageDigest.getInstance("SHA-256")
|
||||
md.update(salt.toByteArray())
|
||||
val result = md.digest(this.toByteArray())
|
||||
return Base64.encode(result)
|
||||
}
|
||||
@@ -6,8 +6,8 @@ import com.auth0.jwt.algorithms.Algorithm
|
||||
import com.auth0.jwt.interfaces.DecodedJWT
|
||||
import org.koin.core.annotation.Factory
|
||||
import ru.shadowsparky.backend.domain.JwtInfo
|
||||
import ru.shadowsparky.backend.domain.LOGIN_NAME
|
||||
import ru.shadowsparky.backend.domain.LoginVerifier
|
||||
import ru.shadowsparky.backend.domain.USER_ID_ARG
|
||||
|
||||
@Factory
|
||||
class TokenVerifier(
|
||||
@@ -22,7 +22,7 @@ class TokenVerifier(
|
||||
|
||||
suspend fun verify(token: String): DecodedJWT {
|
||||
val jwt = verifier.verify(token)
|
||||
loginVerifier.verify(jwt.getClaim(LOGIN_NAME).asString())
|
||||
loginVerifier.verify(jwt.getClaim(USER_ID_ARG).asLong())
|
||||
return jwt
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
package ru.shadowsparky.backend.domain
|
||||
|
||||
interface LoginVerifier {
|
||||
suspend fun verify(login: String)
|
||||
suspend fun verify(userId: Long)
|
||||
}
|
||||
|
||||
const val LOGIN_NAME = "login"
|
||||
const val USER_ID_ARG = "user_id"
|
||||
|
||||
const val INVALID_TOKEN_MSG = "Для выполнения этого действия требуется авторизация."
|
||||
|
||||
+3
-4
@@ -16,7 +16,6 @@ import ru.shadowsparky.backend.data.TokenVerifier
|
||||
import ru.shadowsparky.backend.domain.ExceptionInfo
|
||||
import ru.shadowsparky.backend.domain.INVALID_TOKEN_MSG
|
||||
import ru.shadowsparky.backend.domain.JwtInfo
|
||||
import ru.shadowsparky.backend.domain.LOGIN_NAME
|
||||
import ru.shadowsparky.backend.domain.LoginVerifier
|
||||
import ru.shadowsparky.backend.domain.USER_ID_ARG
|
||||
import ru.shadowsparky.backend.domain.VerifyTokenException
|
||||
@@ -44,10 +43,10 @@ fun Application.configureJwt(
|
||||
realm = jwtInfo.realm
|
||||
verifier(tokenVerifier.verifier)
|
||||
validate { credential ->
|
||||
val login = credential.payload.getClaim(LOGIN_NAME).asString()
|
||||
val userId = credential.payload.getClaim(USER_ID_ARG).asLong()
|
||||
try {
|
||||
if (login != null) {
|
||||
loginVerifier.verify(login)
|
||||
if (userId != null) {
|
||||
loginVerifier.verify(userId)
|
||||
if (credential.payload.expiresAt == null) {
|
||||
throw VerifyTokenException("Static tokens not supported!")
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user