Skip to content

Commit

Permalink
Merge pull request #783 from walt-id/dev-auth-service
Browse files Browse the repository at this point in the history
Add mocked auth service
  • Loading branch information
waltkb authored Oct 2, 2024
2 parents ccf870a + c1de507 commit c6e9efd
Show file tree
Hide file tree
Showing 5 changed files with 148 additions and 30 deletions.
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())
}
}

0 comments on commit c6e9efd

Please sign in to comment.