sasl API reference
The SASL authentication mechanisms shared by the mail clients (smtp / pop / imap). These format the client tokens; the protocol clients run the mechanism-specific wire dialogue (SMTP AUTH, IMAP AUTHENTICATE, POP3 AUTH) around them. No networking; TinyGo-clean, so it runs on both binaries.
- Simple encoders (
plain,loginUser/loginPass,bearer) - one base64 token per step.bearerbuilds the SASL XOAUTH2 response from an OAuth2 bearer token (namedbearer, notxoauth2, because a Jennifer method name is letters-only; "XOAUTH2" is the string the client sends). - Challenge-response (
cram, and theSCRAMfamily) - keyed by thehash/cryptolibraries.cram(CRAM-MD5, RFC 2195) answers a server challenge in one step. SCRAM is a multi-step exchange over aScramhandle:scramStart->scramClientFirst(send), receive server-first ->scramClientFinal+scramFinalToken(send), receive server-final ->scramVerify(checks the server signature, so a MITM without the password is caught). Mechanism "sha1" is SCRAM-SHA-1, "sha256" is SCRAM-SHA-256.
Import with import "sasl.j" as sasl;. See the sasl guide for prose and examples.
Functions
sasl.bearer(user as string, token as string)
Build the SASL XOAUTH2 response for an OAuth2 bearer token: base64("user=" user 0x01 "auth=Bearer " token 0x01 0x01). This is how Google and Microsoft 365 authenticate mail (both have retired password auth).
Parameters
user{string}- the account usernametoken{string}- the OAuth2 bearer access token
Returns {string} - the base64-encoded XOAUTH2 response
sasl.cram(user as string, pass as string, challenge as string)
Build the CRAM-MD5 response (RFC 2195): base64 of "user <hex>", where <hex> is the lowercase HMAC-MD5 of the server's (base64-encoded) challenge keyed by the password. A one-step challenge-response mechanism. Named cram (not cramMd5) because a Jennifer method name is letters-only; the wire mechanism is "CRAM-MD5".
Parameters
user{string}- the login usernamepass{string}- the login passwordchallenge{string}- the server's base64-encoded challenge
Returns {string} - the base64-encoded CRAM-MD5 response
sasl.loginPass(pass as string)
Build the password step of SASL LOGIN (a server sends the "Password:" prompt).
Parameters
pass{string}- the login password
Returns {string} - the base64-encoded password
sasl.loginUser(user as string)
Build the username step of SASL LOGIN (a server sends the "Username:" prompt).
Parameters
user{string}- the login username
Returns {string} - the base64-encoded username
sasl.negotiate(advertised as list of string)
Choose the strongest password mechanism this module implements from the list a server advertises, returned as the auth token a mail client uses: "scram-sha-256" > "scram-sha-1" > "cram", or "" when the server offers none (the caller then falls back to its protocol default - PLAIN / LOGIN / USER-PASS). XOAUTH2 is never auto-selected: it needs a bearer token, not a password.
Parameters
advertised{list of string}- the mechanism names the server advertises (any case)
Returns {string} - the chosen auth token, or "" for the protocol default
sasl.plain(user as string, pass as string)
Build the SASL PLAIN response: base64 of "\0user\0pass".
Parameters
user{string}- the login usernamepass{string}- the login password
Returns {string} - the base64-encoded PLAIN token
sasl.scramClientFinal(s as Scram, serverFirst as string, password as string)
Process the server-first message and compute the client-final message. Derives the salted password (PBKDF2 in the mechanism's hash), the client proof, and the expected server signature; the returned state carries both. Throws Error{kind: "sasl"} if the server's nonce does not extend the client nonce.
Parameters
s{Scram}- the exchange stateserverFirst{string}- the base64 server-first messagepassword{string}- the login password
Returns {Scram} - the updated state; pass it to scramFinalToken and scramVerify
sasl.scramClientFirst(s as Scram)
The base64 client-first message to send to the server (SASL initial response).
Parameters
s{Scram}- the exchange state from scramStart
Returns {string} - the base64-encoded client-first message
sasl.scramFinalToken(s as Scram)
The base64 client-final message to send (after scramClientFinal).
Parameters
s{Scram}- the state returned by scramClientFinal
Returns {string} - the base64-encoded client-final message
sasl.scramStart(user as string, algo as string)
Begin a SCRAM exchange: generate a crypto-grade client nonce and build the initial state. algo selects the mechanism ("sha1" or "sha256").
Parameters
user{string}- the login usernamealgo{string}- "sha1" (SCRAM-SHA-1) or "sha256" (SCRAM-SHA-256)
Returns {Scram} - the exchange state; pass it to scramClientFirst
sasl.scramVerify(s as Scram, serverFinal as string)
Verify the server-final message: the server's signature must match the one derived in scramClientFinal (constant-time), proving the server also knows the password. Reject the session on false.
Parameters
s{Scram}- the state returned by scramClientFinalserverFinal{string}- the base64 server-final message
Returns {bool} - true if the server signature verifies
Structs
sasl.Scram
The state of an in-progress SCRAM exchange, threaded through the four calls. Value-semantic: each step returns an updated copy.
| Field | Type | Description |
|---|---|---|
algo | string | the hash mechanism: "sha1" (SCRAM-SHA-1) or "sha256" (SCRAM-SHA-256) |
clientNonce | string | the client-generated nonce |
clientFirstBare | string | the client-first message without the gs2 header |
clientFinal | string | the client-final message (set by scramClientFinal) |
serverSig | string | the base64 ServerSignature to expect (set by scramClientFinal) |