Skip to content

Support banner during authentication#151

Merged
kruton merged 2 commits into
mainfrom
tailscale-support
May 19, 2026
Merged

Support banner during authentication#151
kruton merged 2 commits into
mainfrom
tailscale-support

Conversation

@kruton
Copy link
Copy Markdown
Member

@kruton kruton commented May 19, 2026

Some authentication methods use out-of-band authentication where the user
is prompted to take action using the SSH_MSG_USERAUTH_BANNER message from
the server. Support this by adding a new onBanner callback to the
AuthHandler.

kruton added 2 commits May 19, 2026 13:51
Some authentication methods use out-of-band authentication where the user
is prompted to take action using the SSH_MSG_USERAUTH_BANNER message from
the server. Support this by adding a new onBanner callback to the
AuthHandler.
Copilot AI review requested due to automatic review settings May 19, 2026 20:57
@kruton kruton merged commit d0ebd20 into main May 19, 2026
13 checks passed
@kruton kruton deleted the tailscale-support branch May 19, 2026 21:04
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds support for SSH SSH_MSG_USERAUTH_BANNER during the strategy-based AuthHandler authentication flow, including a new callback so consumers can surface out-of-band instructions (e.g., MFA URLs) to users.

Changes:

  • Add AuthHandler.onBanner(message: String) callback and plumb banner delivery through SshConnection’s auth result channel.
  • Extend FakeSshServer and add a new unit test to validate banner delivery during auth.
  • Move Spotless configuration to the root project and introduce a shared license header template.

Reviewed changes

Copilot reviewed 7 out of 8 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
sshlib/src/test/kotlin/org/connectbot/sshlib/client/SshAuthBannerTest.kt New test validating that auth banners are delivered to AuthHandler.onBanner.
sshlib/src/test/kotlin/org/connectbot/sshlib/client/FakeSshServer.kt Adds helper methods to emit USERAUTH_BANNER and USERAUTH_FAILURE in tests.
sshlib/src/main/kotlin/org/connectbot/sshlib/client/SshConnection.kt Routes SSH_MSG_USERAUTH_BANNER into the auth flow and forwards it to the handler.
sshlib/src/main/kotlin/org/connectbot/sshlib/client/AuthResult.kt Adds an internal Banner auth result type.
sshlib/src/main/kotlin/org/connectbot/sshlib/AuthHandler.kt Adds the onBanner callback with a default implementation.
sshlib/build.gradle.kts Removes per-module Spotless configuration (now centralized).
spotless/license-header.txt Adds a shared license header template for Spotless.
build.gradle.kts Centralizes Spotless configuration at the root and applies the license header step.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +552 to +573
fun sendUserauthBanner(message: String) {
scope.launch(coroutineContext) {
val banner = SshMsgUserauthBanner()
val utf8 = createUtf8String(message)
banner.setMessage(utf8)
banner.setLanguageTag(createByteString(ByteArray(0)))
banner._check()
writeMutex.withLock {
serverIo.writePacket(SshEnums.MessageType.SSH_MSG_USERAUTH_BANNER.id().toInt(), banner.toByteArray())
}
}
}

fun sendUserauthFailure(allowedMethods: Set<String>, partialSuccess: Boolean) {
scope.launch(coroutineContext) {
val failure = SshMsgUserauthFailure()
failure.setValidAuthentications(createNameList(allowedMethods.joinToString(",")))
failure.setPartialSuccess(if (partialSuccess) 1 else 0)
failure._check()
writeMutex.withLock {
serverIo.writePacket(SshEnums.MessageType.SSH_MSG_USERAUTH_FAILURE.id().toInt(), failure.toByteArray())
}
Comment on lines +107 to +109
authJob.join()

assertEquals(bannerText, bannerReceived.await())
Comment on lines +1653 to +1658
val ch = authResultChannel
if (ch != null) {
val message = msg.message().value()
if (ch.trySend(InternalAuthResult.Banner(message)).isFailure) {
logger.warn("Failed to deliver banner to auth channel")
}
Comment on lines +747 to +751
var noneResult = channel.receive()
while (noneResult is InternalAuthResult.Banner) {
handler.onBanner(noneResult.message)
noneResult = channel.receive()
}
Comment on lines +97 to +100
// Wait for server to receive the "none" auth request
// FakeSshServer doesn't have a way to await auth request easily, but we can just wait a bit or use yield
yield()

import kotlinx.coroutines.yield
import org.connectbot.sshlib.AuthHandler
import org.connectbot.sshlib.AuthPublicKey
import org.connectbot.sshlib.AuthResult
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants