Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add mocked auth service #783

Merged
merged 1 commit into from
Oct 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,22 +1,19 @@
package id.walt.ktorauthnz.auth

import id.walt.ktorauthnz.KtorAuthnzManager
import id.walt.ktorauthnz.sessions.AuthSession
import io.ktor.http.*
import io.ktor.server.application.*
import io.ktor.server.auth.*
import io.ktor.server.response.*
import io.ktor.util.pipeline.*

/**
* A `basic` [Authentication] provider.
*
* @see [basic]
* @property name is the name of the provider, or `null` for a default provider.
*/
public class KtorAuthnzAuthenticationProvider internal constructor(
class DefaultKtorAuthnzAuthentication internal constructor(
config: Config,
) : AuthenticationProvider(config) {
) : KtorAuthnzAuthenticationProvider(config) {

// private val challengeFunction: FormAuthChallengeFunction = config.challengeFunction

Expand Down Expand Up @@ -61,7 +58,7 @@ public class KtorAuthnzAuthenticationProvider internal constructor(
/**
* A configuration for the ktor-authnz authentication provider.
*/
public class Config internal constructor(name: String?) : AuthenticationProvider.Config(name) {
class Config internal constructor(name: String?) : AuthenticationProvider.Config(name) {
/*internal var challengeFunction: FormAuthChallengeFunction = {
call.respond(UnauthorizedResponse())
}*/
Expand All @@ -71,31 +68,10 @@ public class KtorAuthnzAuthenticationProvider internal constructor(
/**
* Installs the ktor-authnz [Authentication] provider.
*/
public fun AuthenticationConfig.ktorAuthnz(
fun AuthenticationConfig.ktorAuthnz(
name: String? = null,
configure: KtorAuthnzAuthenticationProvider.Config.() -> Unit,
configure: DefaultKtorAuthnzAuthentication.Config.() -> Unit,
) {
val provider = KtorAuthnzAuthenticationProvider(KtorAuthnzAuthenticationProvider.Config(name).apply(configure))
val provider = DefaultKtorAuthnzAuthentication(DefaultKtorAuthnzAuthentication.Config(name).apply(configure))
register(provider)
}

fun PipelineContext<Unit, ApplicationCall>.getAuthToken(): String {
val token = call.principal<UserIdPrincipal>()?.name
check(token != null) { "No token for request principal" }

return token
}

// TODO: switch to @OptIn instead of @Deprecated
@Deprecated("Externally provided JWT token cannot resolve to authenticated session")
suspend fun PipelineContext<Unit, ApplicationCall>.getAuthenticatedSession(): AuthSession {
val token = getAuthToken()

return KtorAuthnzManager.tokenHandler.resolveTokenToSession(token)
}

suspend fun PipelineContext<Unit, ApplicationCall>.getAuthenticatedAccount(): String {
val token = getAuthToken()

return KtorAuthnzManager.tokenHandler.getTokenAccountId(token)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package id.walt.ktorauthnz.auth

import id.walt.ktorauthnz.auth.ExampleKtorAuthnzAuthenticationProvider.ExampleKtorAuthnzConfig
import io.ktor.server.auth.*

/**
* Mock authentication for development purposes, all requests will appear
* as logged in with a pre-defined token.
*/
class ExampleKtorAuthnzAuthenticationProvider(val config: ExampleKtorAuthnzConfig) :
KtorAuthnzAuthenticationProvider(config) {

override suspend fun onAuthenticate(context: AuthenticationContext) {
context.principal(name, UserIdPrincipal(config.token))
}

/**
* Config for (development purpose) mocked authentication provider
* @param token token to always use
*/
class ExampleKtorAuthnzConfig(name: String? = null, val token: String) : Config(name)
}

/**
* Installs a mocked ktor-authnz [Authentication] provider.
*/
fun AuthenticationConfig.devKtorAuthnzMocked(
name: String?,
token: String,
configure: ExampleKtorAuthnzConfig.() -> Unit,
) {
val provider = ExampleKtorAuthnzAuthenticationProvider(ExampleKtorAuthnzConfig(name, token).apply(configure))
register(provider)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package id.walt.ktorauthnz.auth

import id.walt.ktorauthnz.KtorAuthnzManager
import id.walt.ktorauthnz.sessions.AuthSession
import io.ktor.server.application.*
import io.ktor.server.auth.*
import io.ktor.util.pipeline.*

fun PipelineContext<Unit, ApplicationCall>.getAuthToken(): String {
val token = call.principal<UserIdPrincipal>()?.name
check(token != null) { "No token for request principal" }

return token
}

// TODO: switch to @OptIn instead of @Deprecated
@Deprecated("Externally provided JWT token cannot resolve to authenticated session")
suspend fun PipelineContext<Unit, ApplicationCall>.getAuthenticatedSession(): AuthSession {
val token = getAuthToken()

return KtorAuthnzManager.tokenHandler.resolveTokenToSession(token)
}

suspend fun PipelineContext<Unit, ApplicationCall>.getAuthenticatedAccount(): String {
val token = getAuthToken()

return KtorAuthnzManager.tokenHandler.getTokenAccountId(token)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package id.walt.ktorauthnz.auth

import io.ktor.server.auth.*

abstract class KtorAuthnzAuthenticationProvider(config: Config) : AuthenticationProvider(config) {

abstract override suspend fun onAuthenticate(context: AuthenticationContext)

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package id.walt

import id.walt.ktorauthnz.KtorAuthnzManager
import id.walt.ktorauthnz.auth.devKtorAuthnzMocked
import id.walt.ktorauthnz.auth.getAuthenticatedAccount
import id.walt.ktorauthnz.auth.ktorAuthnz
import id.walt.ktorauthnz.sessions.AuthSession
import id.walt.ktorauthnz.sessions.AuthSessionStatus
import id.walt.ktorauthnz.tokens.ktorauthnztoken.KtorAuthNzTokenHandler
import io.ktor.client.request.*
import io.ktor.client.statement.*
import io.ktor.http.*
import io.ktor.server.auth.*
import io.ktor.server.response.*
import io.ktor.server.routing.*
import io.ktor.server.testing.*
import kotlin.test.Test

class KtorAuthnzDevMockedTest {

@Test
fun testUnMockedAuth() = testApplication {
install(Authentication) {
ktorAuthnz { }
}

routing {
authenticate {
get("/protected") {
context.respond("protected")
}
}
}

val resp = client.get("/protected")
println(resp)

check(!resp.status.isSuccess())
}

@Test
fun testMockedAuth() = testApplication {
install(Authentication) {
devKtorAuthnzMocked("dev-auth", "dev-token") {
}
}

KtorAuthnzManager.sessionStore.wip_sessions["dev-session"] = AuthSession(
id = "dev-session",
status = AuthSessionStatus.OK,
token = "dev-token",
accountId = "11111111-1111-1111-1111-000000000000"
)
(KtorAuthnzManager.tokenHandler as KtorAuthNzTokenHandler).tokenStore.tokens["dev-token"] = "dev-session"

routing {
authenticate("dev-auth") {
get("/protected") {
val acc = getAuthenticatedAccount()
context.respond("protected! you are: $acc")
}
}
}

val resp = client.get("/protected")
println(resp)

check(resp.status.isSuccess())
check("11111111-1111-1111-1111-000000000000" in resp.bodyAsText())
}
}
Loading