From f41d7aec335678f8ba072be67548e286dd3a66fa Mon Sep 17 00:00:00 2001 From: Eugene Date: Mon, 14 Sep 2026 18:30:53 +0300 Subject: [PATCH] add gigawear auth repository --- apps/giga-wear/backend/build.gradle.kts | 2 +- .../gigawear/backend/BackendModule.kt | 3 +- .../ru/shadowsparky/gigawear/backend/Main.kt | 17 +++-- .../backend/data/BackendAuthRepositoryImpl.kt | 72 +++++++++++++++++++ .../backend/data/LoginVerifierImpl.kt | 19 ++++- .../gigawear/backend/di/DbModule.kt | 32 +++++++++ .../backend/presentation/AuthRouting.kt | 16 +++++ .../backend/presentation/ChatRouting.kt | 5 +- .../gigawear/backend/refresh_tokens.sq | 21 ++++++ .../ru/shadowsparky/gigawear/backend/users.sq | 23 ++++++ .../gigawear/common/AuthRepository.kt | 20 ++++++ apps/vbox/backend/build.gradle.kts | 2 +- .../data/auth/BackendAuthTokenRepository.kt | 12 ++-- .../backend/data/auth/BackendLoginVerifier.kt | 4 +- .../shadowsparky/vbox/backend/di/DbModule.kt | 2 +- .../vbox/backend/di/EntryPoint.kt | 2 +- .../vbox/backend/presentation/Jwt.kt | 2 - .../presentation/routing/AuthRouting.kt | 2 +- .../shadowsparky/backend/data/JwtPreparer.kt | 7 +- .../backend/data/RefreshTokenExt.kt | 18 +++++ .../backend/data/TokenVerifier.kt | 4 +- .../backend/domain/LoginVerifier.kt | 3 +- .../backend/presentation/KtorExtensions.kt | 7 +- 23 files changed, 254 insertions(+), 41 deletions(-) create mode 100644 apps/giga-wear/backend/src/main/kotlin/ru/shadowsparky/gigawear/backend/data/BackendAuthRepositoryImpl.kt create mode 100644 apps/giga-wear/backend/src/main/kotlin/ru/shadowsparky/gigawear/backend/di/DbModule.kt create mode 100644 apps/giga-wear/backend/src/main/kotlin/ru/shadowsparky/gigawear/backend/presentation/AuthRouting.kt create mode 100644 apps/giga-wear/backend/src/main/sqldelight/ru/shadowsparky/gigawear/backend/refresh_tokens.sq create mode 100644 apps/giga-wear/backend/src/main/sqldelight/ru/shadowsparky/gigawear/backend/users.sq create mode 100644 apps/giga-wear/common/src/commonMain/kotlin/ru/shadowsparky/gigawear/common/AuthRepository.kt create mode 100644 libs/backend-base/src/main/kotlin/ru/shadowsparky/backend/data/RefreshTokenExt.kt diff --git a/apps/giga-wear/backend/build.gradle.kts b/apps/giga-wear/backend/build.gradle.kts index d1e8824..14c121f 100644 --- a/apps/giga-wear/backend/build.gradle.kts +++ b/apps/giga-wear/backend/build.gradle.kts @@ -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") } } } diff --git a/apps/giga-wear/backend/src/main/kotlin/ru/shadowsparky/gigawear/backend/BackendModule.kt b/apps/giga-wear/backend/src/main/kotlin/ru/shadowsparky/gigawear/backend/BackendModule.kt index b08d85f..a2ab523 100644 --- a/apps/giga-wear/backend/src/main/kotlin/ru/shadowsparky/gigawear/backend/BackendModule.kt +++ b/apps/giga-wear/backend/src/main/kotlin/ru/shadowsparky/gigawear/backend/BackendModule.kt @@ -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 { diff --git a/apps/giga-wear/backend/src/main/kotlin/ru/shadowsparky/gigawear/backend/Main.kt b/apps/giga-wear/backend/src/main/kotlin/ru/shadowsparky/gigawear/backend/Main.kt index 033cc73..1f0366d 100644 --- a/apps/giga-wear/backend/src/main/kotlin/ru/shadowsparky/gigawear/backend/Main.kt +++ b/apps/giga-wear/backend/src/main/kotlin/ru/shadowsparky/gigawear/backend/Main.kt @@ -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) { 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()) + } } } diff --git a/apps/giga-wear/backend/src/main/kotlin/ru/shadowsparky/gigawear/backend/data/BackendAuthRepositoryImpl.kt b/apps/giga-wear/backend/src/main/kotlin/ru/shadowsparky/gigawear/backend/data/BackendAuthRepositoryImpl.kt new file mode 100644 index 0000000..d0b9983 --- /dev/null +++ b/apps/giga-wear/backend/src/main/kotlin/ru/shadowsparky/gigawear/backend/data/BackendAuthRepositoryImpl.kt @@ -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().copy(timestamp = System.currentTimeMillis()) - call.respond(useCase.execute(message, MOCK_USER_ID)) + call.respond(useCase.execute(message, call.obtainUserId())) } } diff --git a/apps/giga-wear/backend/src/main/sqldelight/ru/shadowsparky/gigawear/backend/refresh_tokens.sq b/apps/giga-wear/backend/src/main/sqldelight/ru/shadowsparky/gigawear/backend/refresh_tokens.sq new file mode 100644 index 0000000..d56acc1 --- /dev/null +++ b/apps/giga-wear/backend/src/main/sqldelight/ru/shadowsparky/gigawear/backend/refresh_tokens.sq @@ -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 = ?; diff --git a/apps/giga-wear/backend/src/main/sqldelight/ru/shadowsparky/gigawear/backend/users.sq b/apps/giga-wear/backend/src/main/sqldelight/ru/shadowsparky/gigawear/backend/users.sq new file mode 100644 index 0000000..0b22f9f --- /dev/null +++ b/apps/giga-wear/backend/src/main/sqldelight/ru/shadowsparky/gigawear/backend/users.sq @@ -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 = ?; diff --git a/apps/giga-wear/common/src/commonMain/kotlin/ru/shadowsparky/gigawear/common/AuthRepository.kt b/apps/giga-wear/common/src/commonMain/kotlin/ru/shadowsparky/gigawear/common/AuthRepository.kt new file mode 100644 index 0000000..8de783d --- /dev/null +++ b/apps/giga-wear/common/src/commonMain/kotlin/ru/shadowsparky/gigawear/common/AuthRepository.kt @@ -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 +) diff --git a/apps/vbox/backend/build.gradle.kts b/apps/vbox/backend/build.gradle.kts index ee2849c..ca4de32 100644 --- a/apps/vbox/backend/build.gradle.kts +++ b/apps/vbox/backend/build.gradle.kts @@ -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") } } } diff --git a/apps/vbox/backend/src/main/kotlin/ru/shadowsparky/vbox/backend/data/auth/BackendAuthTokenRepository.kt b/apps/vbox/backend/src/main/kotlin/ru/shadowsparky/vbox/backend/data/auth/BackendAuthTokenRepository.kt index 0c114ea..7e09ce0 100644 --- a/apps/vbox/backend/src/main/kotlin/ru/shadowsparky/vbox/backend/data/auth/BackendAuthTokenRepository.kt +++ b/apps/vbox/backend/src/main/kotlin/ru/shadowsparky/vbox/backend/data/auth/BackendAuthTokenRepository.kt @@ -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 { diff --git a/apps/vbox/backend/src/main/kotlin/ru/shadowsparky/vbox/backend/data/auth/BackendLoginVerifier.kt b/apps/vbox/backend/src/main/kotlin/ru/shadowsparky/vbox/backend/data/auth/BackendLoginVerifier.kt index fbeb297..0a7ba0f 100644 --- a/apps/vbox/backend/src/main/kotlin/ru/shadowsparky/vbox/backend/data/auth/BackendLoginVerifier.kt +++ b/apps/vbox/backend/src/main/kotlin/ru/shadowsparky/vbox/backend/data/auth/BackendLoginVerifier.kt @@ -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) { diff --git a/apps/vbox/backend/src/main/kotlin/ru/shadowsparky/vbox/backend/di/DbModule.kt b/apps/vbox/backend/src/main/kotlin/ru/shadowsparky/vbox/backend/di/DbModule.kt index a37a3fd..8e277a0 100644 --- a/apps/vbox/backend/src/main/kotlin/ru/shadowsparky/vbox/backend/di/DbModule.kt +++ b/apps/vbox/backend/src/main/kotlin/ru/shadowsparky/vbox/backend/di/DbModule.kt @@ -30,4 +30,4 @@ class DbModule { fun provideDb(driver: SqlDriver): AppDatabase { return AppDatabase.Companion.invoke(driver) } -} \ No newline at end of file +} diff --git a/apps/vbox/backend/src/main/kotlin/ru/shadowsparky/vbox/backend/di/EntryPoint.kt b/apps/vbox/backend/src/main/kotlin/ru/shadowsparky/vbox/backend/di/EntryPoint.kt index eb3654e..c699446 100644 --- a/apps/vbox/backend/src/main/kotlin/ru/shadowsparky/vbox/backend/di/EntryPoint.kt +++ b/apps/vbox/backend/src/main/kotlin/ru/shadowsparky/vbox/backend/di/EntryPoint.kt @@ -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 diff --git a/apps/vbox/backend/src/main/kotlin/ru/shadowsparky/vbox/backend/presentation/Jwt.kt b/apps/vbox/backend/src/main/kotlin/ru/shadowsparky/vbox/backend/presentation/Jwt.kt index 76f8712..4628e99 100644 --- a/apps/vbox/backend/src/main/kotlin/ru/shadowsparky/vbox/backend/presentation/Jwt.kt +++ b/apps/vbox/backend/src/main/kotlin/ru/shadowsparky/vbox/backend/presentation/Jwt.kt @@ -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) } diff --git a/apps/vbox/backend/src/main/kotlin/ru/shadowsparky/vbox/backend/presentation/routing/AuthRouting.kt b/apps/vbox/backend/src/main/kotlin/ru/shadowsparky/vbox/backend/presentation/routing/AuthRouting.kt index 6874398..79d1944 100644 --- a/apps/vbox/backend/src/main/kotlin/ru/shadowsparky/vbox/backend/presentation/routing/AuthRouting.kt +++ b/apps/vbox/backend/src/main/kotlin/ru/shadowsparky/vbox/backend/presentation/routing/AuthRouting.kt @@ -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 diff --git a/libs/backend-base/src/main/kotlin/ru/shadowsparky/backend/data/JwtPreparer.kt b/libs/backend-base/src/main/kotlin/ru/shadowsparky/backend/data/JwtPreparer.kt index 4dfe122..4eb61ba 100644 --- a/libs/backend-base/src/main/kotlin/ru/shadowsparky/backend/data/JwtPreparer.kt +++ b/libs/backend-base/src/main/kotlin/ru/shadowsparky/backend/data/JwtPreparer.kt @@ -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) diff --git a/libs/backend-base/src/main/kotlin/ru/shadowsparky/backend/data/RefreshTokenExt.kt b/libs/backend-base/src/main/kotlin/ru/shadowsparky/backend/data/RefreshTokenExt.kt new file mode 100644 index 0000000..b1586d9 --- /dev/null +++ b/libs/backend-base/src/main/kotlin/ru/shadowsparky/backend/data/RefreshTokenExt.kt @@ -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) +} diff --git a/libs/backend-base/src/main/kotlin/ru/shadowsparky/backend/data/TokenVerifier.kt b/libs/backend-base/src/main/kotlin/ru/shadowsparky/backend/data/TokenVerifier.kt index 0f447ae..813434e 100644 --- a/libs/backend-base/src/main/kotlin/ru/shadowsparky/backend/data/TokenVerifier.kt +++ b/libs/backend-base/src/main/kotlin/ru/shadowsparky/backend/data/TokenVerifier.kt @@ -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 } } diff --git a/libs/backend-base/src/main/kotlin/ru/shadowsparky/backend/domain/LoginVerifier.kt b/libs/backend-base/src/main/kotlin/ru/shadowsparky/backend/domain/LoginVerifier.kt index 6247de0..1141878 100644 --- a/libs/backend-base/src/main/kotlin/ru/shadowsparky/backend/domain/LoginVerifier.kt +++ b/libs/backend-base/src/main/kotlin/ru/shadowsparky/backend/domain/LoginVerifier.kt @@ -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 = "Для выполнения этого действия требуется авторизация." diff --git a/libs/backend-base/src/main/kotlin/ru/shadowsparky/backend/presentation/KtorExtensions.kt b/libs/backend-base/src/main/kotlin/ru/shadowsparky/backend/presentation/KtorExtensions.kt index bd609cd..d7918f1 100644 --- a/libs/backend-base/src/main/kotlin/ru/shadowsparky/backend/presentation/KtorExtensions.kt +++ b/libs/backend-base/src/main/kotlin/ru/shadowsparky/backend/presentation/KtorExtensions.kt @@ -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!") }