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

WAL-703 : web3 based auth #837

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
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
Expand Up @@ -5,7 +5,10 @@ import id.walt.ktorauthnz.accounts.identifiers.methods.*
object AccountIdentifierManager {

private val defaultIdentifiers =
listOf(EmailIdentifier, JWTIdentifier, LDAPIdentifier, OIDCIdentifier, RADIUSIdentifier, UsernameIdentifier)
listOf(
EmailIdentifier, JWTIdentifier, LDAPIdentifier, OIDCIdentifier, RADIUSIdentifier, UsernameIdentifier,
Web3Identifier
)

private val factories: MutableMap<String, AccountIdentifier.AccountIdentifierFactory<out AccountIdentifier>> =
defaultIdentifiers.associateBy { it.identifierName }.toMutableMap()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package id.walt.ktorauthnz.accounts.identifiers.methods

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

@Serializable
@SerialName("web3")
data class Web3Identifier(
val publicKey: String
) : AccountIdentifier() {
override fun identifierName() = "multistep-example" // SerialName and identifierName should match

override fun toDataString() = publicKey // what is part of this identifier? In this case just the string "publicKey"

companion object :
AccountIdentifierFactory<Web3Identifier>("multistep-example") { // this creator id also has to match with identifierName
override fun fromAccountIdentifierDataString(dataString: String) = Web3Identifier(dataString)

val EXAMPLE = Web3Identifier("0xABCDEF0123456789") // Define a nice example for the docs
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package id.walt.ktorauthnz.methods

import id.walt.ktorauthnz.AuthContext
import id.walt.ktorauthnz.accounts.identifiers.methods.Web3Identifier
import id.walt.ktorauthnz.exceptions.authCheck
import io.github.smiley4.ktorswaggerui.dsl.routing.post
import io.ktor.server.application.*
import io.ktor.server.response.*
import io.ktor.server.routing.*
import io.ktor.util.pipeline.*
import kotlinx.serialization.Serializable
import kotlin.random.Random

object Web3 : AuthenticationMethod("web3") {

fun makeNonce() = "n" + Random.nextInt() // should be a JWT

fun verifiySignature(web3ExampleSigned: MultiStepExampleSigned) {
web3ExampleSigned.challenge // check that the challenge comes from us (is a JWT made by us)

// check that signed challenge verifies correctly
authCheck(web3ExampleSigned.signed == "${web3ExampleSigned.challenge}-signed") { "Invalid signature" }

// check that public key belongs to signature
web3ExampleSigned.publicKey
}

@Serializable
data class MultiStepExampleSigned(
val challenge: String,
val signed: String,
val publicKey: String
)

override fun Route.register(authContext: PipelineContext<Unit, ApplicationCall>.() -> AuthContext) {
route("web3") {
get("nonce") { // Step 1
context.respond(makeNonce())
}

post<MultiStepExampleSigned>("signed", { // Step 2
request { body<MultiStepExampleSigned>() }
}) { req ->
val session = getSession(authContext)

verifiySignature(req) // Verification

// Verification was successful:

val identifier =
Web3Identifier(req.publicKey) // select identifier (= who logged in with this method now?)

context.handleAuthSuccess(
session,
identifier.resolveToAccountId()
) // handleAuthSuccess() -> session is now logged in
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,31 @@ import io.ktor.util.pipeline.*
import kotlinx.coroutines.runBlocking
import org.intellij.lang.annotations.Language


fun Route.globalMultistepExample() {
route("web3") {
@Language("JSON")
val flowConfig = """
{
"method": "web3",
"ok": true
}
""".trimIndent()
val authFlow = AuthFlow.fromConfig(flowConfig)

val contextFunction: PipelineContext<Unit, ApplicationCall>.() -> AuthContext = {
AuthContext(
tenant = call.request.host(),
sessionId = call.parameters["sessionId"],
implicitSessionGeneration = true,
initialFlow = authFlow
)
}

registerAuthenticationMethod(Web3, contextFunction)
}
}

fun Route.globalImplicitSingleStep() {
route("global-implicit1") {
@Language("JSON")
Expand Down
Loading