#11 fix SASL login failures with non-ASCII passwords
iOS CI / Build and Test SwiftUI App (push) Canceled after 0s
Build Unsigned iOS and macOS Apps / Build Unsigned iOS IPA (push) Canceled after 0s
Build Unsigned iOS and macOS Apps / Build macOS ZIP (push) Canceled after 0s

- Normalize SCRAM passwords with RFC 4013 SASLprep before the
  challenge response, so they match SASLprep-compliant servers
  (Martin hashes raw UTF-8) and PLAIN still sends the password
  untouched.
- Capture the raw <failure/> condition with LumaSaslFailureModule and
  map SASL errors to actionable Russian messages instead of the
  cryptic OS-localized "Martin.SaslError, error 5".
- Cover SASLprep and failure message mapping with unit tests and new
  verify.sh invariants.
This commit is contained in:
wt committed 2026-08-29 01:26:03 +07:00
1 parent 2884810ec4
commit fc07855036
13 files changed
+410 -28

No files matched your search

@@ -0,0 +1,37 @@
import Foundation
import Martin
/// Observes the raw SASL `<failure/>` stanza before `SaslModule` collapses
/// it into its `SaslError` enum, so the UI can show the real RFC 6120
/// condition (`account-disabled`, `credentials-expired`, …) and the
/// optional server `<text/>` instead of a catch-all "authentication
/// failure". Registered before `SaslModule` in the module list.
final class LumaSaslFailureModule: XmppModuleBase, XmppModule {
static let ID = "lumaSaslFailure"
static let saslXMLNS = "urn:ietf:params:xml:ns:xmpp-sasl"
struct Failure: Equatable, Sendable {
let condition: String?
let text: String?
}
let criteria = Criteria.name("failure", xmlns: LumaSaslFailureModule.saslXMLNS)
let features: [String] = []
private let lock = NSLock()
private var stored: Failure?
var lastFailure: Failure? {
lock.lock()
defer { lock.unlock() }
return stored
}
func process(stanza: Stanza) throws {
let condition = stanza.findChild()?.name
let text = stanza.findChild(name: "text")?.value
lock.lock()
stored = Failure(condition: condition, text: text)
lock.unlock()
}
}