Skip to content

Commit

Permalink
feat: authnz registration initial auth
Browse files Browse the repository at this point in the history
  • Loading branch information
waltkb committed Oct 13, 2024
1 parent 7b0dc29 commit 668f8d2
Show file tree
Hide file tree
Showing 33 changed files with 305 additions and 38 deletions.
1 change: 1 addition & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ plugins {
val kotlinVersion = "2.0.20"
kotlin("multiplatform") version kotlinVersion apply false
kotlin("jvm") version kotlinVersion
kotlin("plugin.power-assert") version kotlinVersion apply false

kotlin("plugin.compose") version kotlinVersion apply false

Expand Down
15 changes: 15 additions & 0 deletions waltid-libraries/auth/waltid-ktor-authnz/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi

plugins {
kotlin("jvm")
kotlin("plugin.power-assert")
kotlin("plugin.serialization")
id("io.ktor.plugin") version "2.3.12"
id("maven-publish")
Expand Down Expand Up @@ -130,3 +133,15 @@ publishing {
}
}
}

@OptIn(ExperimentalKotlinGradlePluginApi::class)
powerAssert {
includedSourceSets = listOf("test")
functions = listOf(
// kotlin.test
"kotlin.assert", "kotlin.test.assertTrue", "kotlin.test.assertEquals", "kotlin.test.assertNull",

// checks
"kotlin.require", "kotlin.check"
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package id.walt.ktorauthnz.accounts.identifiers

import id.walt.ktorauthnz.accounts.identifiers.methods.*

object AccountIdentifierRegistry {
object AccountIdentifierManager {

private val defaultIdentifiers =
listOf(EmailIdentifier, JWTIdentifier, LDAPIdentifier, OIDCIdentifier, RADIUSIdentifier, UsernameIdentifier)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,19 @@ import kotlinx.serialization.Serializable

@Suppress("EqualsOrHashCode")
@Serializable
abstract class AccountIdentifier(val identifierName: String) {
sealed class AccountIdentifier {

override fun toString(): String = "[$identifierName: ${toDataString()}]"
internal abstract fun identifierName(): String

val accountIdentifierName
get() = identifierName()

override fun toString(): String = "[$accountIdentifierName: ${toDataString()}]"
abstract fun toDataString(): String

override fun equals(other: Any?): Boolean {
if (other !is AccountIdentifier) return false
if (other.identifierName != identifierName) return false
if (other.accountIdentifierName != accountIdentifierName) return false
if (other.hashCode() != hashCode()) return false

return true
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,18 @@
package id.walt.ktorauthnz.accounts.identifiers.methods

import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

@Serializable
data class EmailIdentifier(val email: String) : AccountIdentifier("email") {
@SerialName("email")
data class EmailIdentifier(val email: String) : AccountIdentifier() {
override fun identifierName() = "email"

override fun toDataString() = email

companion object : AccountIdentifierFactory<EmailIdentifier>("email") {
override fun fromAccountIdentifierDataString(dataString: String): EmailIdentifier = EmailIdentifier(dataString)
}


}
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
package id.walt.ktorauthnz.accounts.identifiers.methods

import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

@Serializable
data class JWTIdentifier(val subject: String) : AccountIdentifier("jwt") {
@SerialName("jwt")
data class JWTIdentifier(val subject: String) : AccountIdentifier() {
override fun identifierName() = "jwt"
override fun toDataString() = subject

companion object : AccountIdentifierFactory<JWTIdentifier>("jwt") {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
package id.walt.ktorauthnz.accounts.identifiers.methods

import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json

@Serializable
data class LDAPIdentifier(val host: String, val name: String) : AccountIdentifier("ldap") {
@SerialName("ldap")
data class LDAPIdentifier(val host: String, val name: String) : AccountIdentifier() {
override fun identifierName() = "ldap"
override fun toDataString() = Json.encodeToString(this)

companion object : AccountIdentifierFactory<LDAPIdentifier>("ldap") {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
package id.walt.ktorauthnz.accounts.identifiers.methods

import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json

data class OIDCIdentifier(val host: String, val name: String) : AccountIdentifier("oidc") {
@Serializable
@SerialName("oidc")
data class OIDCIdentifier(val host: String, val name: String) : AccountIdentifier() {
override fun identifierName() = "oidc"
override fun toDataString() = Json.encodeToString(this)

companion object : AccountIdentifierFactory<OIDCIdentifier>("oidc") {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
package id.walt.ktorauthnz.accounts.identifiers.methods

import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json

@Serializable
data class RADIUSIdentifier(val host: String, val name: String) : AccountIdentifier("radius") {
@SerialName("radius")
data class RADIUSIdentifier(val host: String, val name: String) : AccountIdentifier() {
override fun identifierName() = "radius"
override fun toDataString() = Json.encodeToString(this)

companion object : AccountIdentifierFactory<RADIUSIdentifier>("radius") {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
package id.walt.ktorauthnz.accounts.identifiers.methods

import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

@Serializable
data class UsernameIdentifier(val name: String) : AccountIdentifier("username") {
@SerialName("userpass")
data class UsernameIdentifier(val name: String) : AccountIdentifier() {
override fun identifierName() = "userpass"
override fun toDataString() = name

companion object : AccountIdentifierFactory<UsernameIdentifier>("username") {
companion object : AccountIdentifierFactory<UsernameIdentifier>("userpass") {
override fun fromAccountIdentifierDataString(dataString: String) = UsernameIdentifier(dataString)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package id.walt.ktorauthnz.methods
import id.walt.ktorauthnz.AuthContext
import id.walt.ktorauthnz.KtorAuthnzManager
import id.walt.ktorauthnz.accounts.identifiers.methods.AccountIdentifier
import id.walt.ktorauthnz.methods.config.AuthMethodConfiguration
import id.walt.ktorauthnz.methods.data.AuthMethodStoredData
import id.walt.ktorauthnz.sessions.AuthSession
import id.walt.ktorauthnz.sessions.AuthSessionStatus
Expand All @@ -12,6 +13,22 @@ import io.ktor.server.application.*
import io.ktor.server.response.*
import io.ktor.server.routing.*
import io.ktor.util.pipeline.*
import kotlinx.serialization.EncodeDefault
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.Serializable
import kotlin.reflect.KClass

@OptIn(ExperimentalSerializationApi::class)
@Serializable
sealed interface MethodInstance {
@EncodeDefault(EncodeDefault.Mode.NEVER)
val data: AuthMethodStoredData?

@EncodeDefault(EncodeDefault.Mode.NEVER)
val config: AuthMethodConfiguration?

fun authMethod(): AuthenticationMethod
}

abstract class AuthenticationMethod(open val id: String) {
abstract fun Route.register(authContext: PipelineContext<Unit, ApplicationCall>.() -> AuthContext)
Expand Down Expand Up @@ -54,6 +71,9 @@ abstract class AuthenticationMethod(open val id: String) {

return session
}

open val relatedAuthMethodStoredData: KClass<out AuthMethodStoredData>? = null
open val relatedAuthMethodConfiguration: KClass<out AuthMethodConfiguration>? = null
}


Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
package id.walt.ktorauthnz.methods

import id.walt.ktorauthnz.AuthContext
import id.walt.ktorauthnz.accounts.identifiers.AccountIdentifierManager
import id.walt.ktorauthnz.accounts.identifiers.methods.AccountIdentifier
import id.walt.ktorauthnz.accounts.identifiers.methods.EmailIdentifier
import id.walt.ktorauthnz.exceptions.authCheck
import id.walt.ktorauthnz.methods.config.AuthMethodConfiguration
import id.walt.ktorauthnz.methods.data.AuthMethodStoredData
import id.walt.ktorauthnz.methods.data.EmailPassStoredData
import id.walt.ktorauthnz.sessions.AuthSession
import id.walt.ktorauthnz.sessions.AuthSessionInformation
Expand All @@ -13,10 +16,28 @@ import io.ktor.server.application.*
import io.ktor.server.auth.*
import io.ktor.server.routing.*
import io.ktor.util.pipeline.*
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.json.JsonPrimitive
import org.intellij.lang.annotations.Language

@Serializable
@SerialName("email")
data class EmailPassMethodInstance(
override val data: EmailPassStoredData,
) : MethodInstance {
override val config = null

override fun authMethod() = EmailPass
}

object EmailPass : UserPassBasedAuthMethod("email", usernameName = "email") {

override val relatedAuthMethodStoredData = EmailPassStoredData::class

override suspend fun auth(session: AuthSession, credential: UserPasswordCredential, context: ApplicationCall): AccountIdentifier {
val identifier = EmailIdentifier(credential.name)

Expand Down Expand Up @@ -44,5 +65,4 @@ object EmailPass : UserPassBasedAuthMethod("email", usernameName = "email") {
context.handleAuthSuccess(session, identifier.resolveToAccountId())
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import kotlinx.serialization.Serializable

object TOTP : AuthenticationMethod("totp") {

override val relatedAuthMethodStoredData = TOTPStoredData::class

fun auth(session: AuthSession, code: String) {
val storedData = lookupStoredMultiData<TOTPStoredData>(session /* context() */)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ data class UserPassCredentials(val username: String, val password: String)

object UserPass : UserPassBasedAuthMethod("userpass") {

override val relatedAuthMethodStoredData = UserPassStoredData::class

override suspend fun auth(session: AuthSession, credential: UserPasswordCredential, context: ApplicationCall): AccountIdentifier {
val identifier = UsernameIdentifier(credential.name)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package id.walt.ktorauthnz.methods.config

import id.walt.ktorauthnz.methods.AuthenticationMethod
import kotlinx.serialization.Serializable

@Serializable
sealed interface AuthMethodConfiguration
sealed interface AuthMethodConfiguration {
fun authMethod(): AuthenticationMethod
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package id.walt.ktorauthnz.methods.config

import id.walt.ktorauthnz.methods.JWT
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

Expand All @@ -8,4 +9,6 @@ import kotlinx.serialization.Serializable
data class JwtAuthConfiguration(
val verifyKey: String,
val identifyClaim: String = "sub",
) : AuthMethodConfiguration
) : AuthMethodConfiguration {
override fun authMethod() = JWT
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package id.walt.ktorauthnz.methods.config

import id.walt.ktorauthnz.methods.LDAP
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

Expand All @@ -8,4 +9,6 @@ import kotlinx.serialization.Serializable
data class LDAPConfiguration(
val ldapServerUrl: String,
val userDNFormat: String,
) : AuthMethodConfiguration
) : AuthMethodConfiguration {
override fun authMethod() = LDAP
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,4 +33,6 @@ data class OidcAuthConfiguration(
init {
runBlocking { init() }
}

override fun authMethod() = OIDC
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package id.walt.ktorauthnz.methods.config

import id.walt.ktorauthnz.methods.RADIUS
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

Expand All @@ -10,4 +11,6 @@ data class RADIUSConfiguration(
val radiusServerPort: Int,
val radiusServerSecret: String,
val radiusNasIdentifier: String,
) : AuthMethodConfiguration
) : AuthMethodConfiguration {
override fun authMethod() = RADIUS
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package id.walt.ktorauthnz.methods.config

import id.walt.ktorauthnz.methods.VerifiableCredential
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.JsonElement
Expand All @@ -10,4 +11,6 @@ data class VerifiableCredentialAuthConfiguration(
val verification: Map<String, JsonElement>,
//val claimMappings: Map<String, String>? = null,
//val redirectUrl: String? = null,
) : AuthMethodConfiguration
) : AuthMethodConfiguration {
override fun authMethod() = VerifiableCredential
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package id.walt.ktorauthnz.methods.data

import id.walt.ktorauthnz.methods.AuthenticationMethod
import kotlinx.serialization.Serializable

@Serializable
sealed interface AuthMethodStoredData
sealed interface AuthMethodStoredData {
fun authMethod(): AuthenticationMethod
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
package id.walt.ktorauthnz.methods.data

import id.walt.ktorauthnz.methods.EmailPass
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

@Serializable
@SerialName("emailpass-data")
@SerialName("email")
data class EmailPassStoredData(
val password: String,
) : AuthMethodStoredData
) : AuthMethodStoredData {
override fun authMethod() = EmailPass
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
package id.walt.ktorauthnz.methods.data

import id.walt.ktorauthnz.flows.AuthFlow
import id.walt.ktorauthnz.methods.virtual.GlobalIdentify
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

@Serializable
@SerialName("flowamendment-data")
@SerialName("flowamendment")
data class FlowAmendmentData(
val appendFlow: Set<AuthFlow>? = null,
var replaceFlow: Set<AuthFlow>? = null,
) : AuthMethodStoredData
) : AuthMethodStoredData {
override fun authMethod() = GlobalIdentify
}
Loading

0 comments on commit 668f8d2

Please sign in to comment.