sasler

package module
v0.0.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 10, 2023 License: MIT Imports: 14 Imported by: 0

README

sasler

SASL client and server for Go

Documentation

Overview

Package sasler contains client-side and server-side implementations for the following SASL mechanisms: ANONYMOUS, ECDSA-NIST256P-CHALLENGE, EXTERNAL, OAUTHBEARER, PLAIN, SCRAM-SHA-1, and SCRAM-SHA-256.

Client-side usage

  1. Call one of the functions that returns a ClientMech implementation.
  2. Send a message to the server to signal you want to start SASL authentication, passing the mechanism name returned by calling Mech().
  3. If the server acknowledges starting SASL authentication, relay messages between the CientMech and the server, using appropriate encoding for the protocol used with the server. Note that you can call Mech() to detect whether the first message must be sent by the client, or will be sent by the server.
  4. When the server indicates authentication has finished, you're done. However, when Data() returns an error, the authentication process has failed and must be aborted.

The ClientMech documentation contains an example that demonstrates the process described above.

Server-side usage

  1. Implement the authenticator matching the desired mechanism, which is by the ServerMech to hook up with any systems you have in place for user and/or credential storage.
  2. When a client request SASL authentication, call the appropriate function that returns the ServerMech implementation requested by the client.
  3. Send a message to the client to acknowledge SASL authentication has been started.
  4. Relay messages between the ServerMech and the client, using appropriate encoding for the protocol used with the client. Note that you can call Mech() to detect whether the first message must be sent by the server, or will be sent by the client.
  5. Whenever Data() returns an error, the authentication process has failed and must be aborted. Send a message to the client notifying it about the abortion.
  6. After each call to Data(), call HasCompleted() to check whether the authentication process has been completed. If it returned true, it also returned the authorized identity.

The ServerMech documentation contains an example that demonstrates the process described above. The documentation for each authenticator interface contains an example implementation.

Index

Examples

Constants

This section is empty.

Variables

View Source
var (
	// ErrInvalidState is returned by a mechanism implementation if the function
	// is called at an inappropriate moment in the authentication process.
	ErrInvalidState = errors.New("sasler: mechanism in invalid state")
	// ErrInvalidMessage is returend by Date if the function is supplied with a
	// message that is syntactially invalid.
	ErrInvalidMessage = errors.New("sasler: invalid message")
	// ErrAuthenticationFailed can be returned by an implementation to indicate
	// that authentication has failed.
	ErrAuthenticationFailed = errors.New("sasler: authentication failed")
	// ErrUnauthorized can be returns by a server-side implementation to signal
	// that the authenticated authn is not authorized to use the requested authz.
	ErrUnauthorized = errors.New("sasler: unauthorized")
)
View Source
var ErrWrongCurve = errors.New("sasler: wrong curve")

ErrWrongCurve is returned by a function is called with an ECDSA private or public key that references a curve the function doesn't accept.

Functions

This section is empty.

Types

type AnonymousAuthenticator

type AnonymousAuthenticator interface {
	// StoreTrace is called to store trace information provided by the client.
	// Will not be called if the client didn't provide trace information.
	StoreTrace(trace string)
}

AnonymousAuthenticator is supplied to AnonymousServer to implement storing received trace values.

Example
package main

import (
	"fmt"

	"github.com/phedny/sasler"
)

func main() {
	auth := myAnonymousAuthenticator{}
	mech := sasler.AnonymousServer("anonymous-user", &auth)

	// ANONYMOUS expects one message from the client
	_, err := mech.Data([]byte("user@example.com"))
	if err != nil {
		fmt.Println(err)
	}

	// Retrieve authorized identity
	completed, authz := mech.HasCompleted()
	if completed {
		fmt.Println("Authorized identity:", authz)
		fmt.Println("Stored trace:", auth.trace)
	}

}

// myAnonymousAuthenciator is an example AnonymousAuthenticator that just
// stores the latest retrieve trace information in a field.
type myAnonymousAuthenticator struct {
	trace string
}

// StoreTrace stores the received trace information in a field of a.
func (a *myAnonymousAuthenticator) StoreTrace(trace string) {
	a.trace = trace
}
Output:

Authorized identity: anonymous-user
Stored trace: user@example.com

type ClientMech

type ClientMech interface {
	// Mech returns the full name of the mechanism as described in its
	// specification, and a bool that is true when this is a client-first
	// mechanism.
	Mech() (string, bool)
	// Data must be called each time SASL data arrived from the other party,
	// providing the bytes of the message. It returns the bytes of the message
	// that must be returned to the other party, or an error when authentication
	// failed and must be aborted. If the returned []byte is nil and no error is
	// returned, authentication has finished successfully.
	//
	// On a client-first mechanism, the first call to Data must be done with nil
	// or zero length slice. On a server-first mechanism, the first call to Data
	// must be done with the initial challenge received from the server.
	Data(data []byte) ([]byte, error)
}

ClientMech describes the functions that are implemented by the client-side implementation of a SASL mechanism.

Example
package main

import (
	"fmt"

	"github.com/phedny/sasler"
)

func main() {
	// SelectClientMech() represents a function that returns a ClientMech
	mech := SelectClientMech()
	mechName, clientFirst := mech.Mech()

	// AuthenticateToServer() and all calls to Read() and Write() represent a
	// protocol-specific communication channel.
	server := AuthenticateToServer(mechName)

	// For client-first mechanisms, send the first message
	if clientFirst {
		data, err := mech.Data(nil)
		if err != nil {
			fmt.Println("Authentication aborted.", err)
			return
		}
		server.Write(data)
	}

	// Relay authentication data until error, Success or Failure
	for {
		command, data := server.Read()
		switch command {
		case Success:
			fmt.Println("Authentication succeeded.")
			return
		case Failure:
			fmt.Println("Authentication failed.")
			return
		case Data:
			data, err := mech.Data(data)
			if err != nil {
				fmt.Println("Authentication aborted.", err)
				return
			}
			server.Write(data)
		}
	}

}

func SelectClientMech() sasler.ClientMech { return sasler.ExternalClient("") }

type exampleServer struct{}

func AuthenticateToServer(mechName string) exampleServer { return exampleServer{} }

func (*exampleServer) Write(data []byte) {}

const (
	Data = iota
	Success
	Failure
)

func (*exampleServer) Read() (int, []byte) { return Success, nil }
Output:

Authentication succeeded.

func AnonymousClient

func AnonymousClient(trace string) (ClientMech, error)

AnonymousClient returns a ClientMech implementation for the ANONYMOUS mechanism, as specified in RFC 4505. Return an error for values of trace that are invalid, i.e. when it contains prohibited characters, or fails BiDi rules. For details, see section 3 of the RFC.

func EcdsaNist256pChallengeClient

func EcdsaNist256pChallengeClient(authz, authn string, key *ecdsa.PrivateKey) (ClientMech, error)

EcdsaNist256pChallengeClient returns a SaslMech implementation for the ECDSA-NIST256P-CHALLENGE mechanism, as specified at ecdsatool. Returns ErrWrongCurve if the public key doesn't use the curve returned by elliptic.P256().

func ExternalClient

func ExternalClient(authz string) ClientMech

ExternalClient returns a ClientMech implementation for the EXTERNAL mechanism, as specified in RFC 4422, appendix A.

func OAuthBearerClient

func OAuthBearerClient(authz string, token []byte, host string, port int) ClientMech

OAuthBearerClient returns a ClientMech implementation for the OAUTHBEARER mechanism, as specified in RFC 7628.

func PlainClient

func PlainClient(authz, authn string, passwd []byte) ClientMech

PlainClient returns a ClientMech implementation for the PLAIN mechanism, as specified in RFC 4616.

func ScramSha1Client

func ScramSha1Client(authz, authn string, passwd []byte) (ClientMech, error)

ScramSha1Client returns a ClientMech implementation for the SCRAM-SHA-1 mechanism, as specified in RFC 5802. Returns an error if SASLprep on authn, or passwd fails, as described in RFC 5802, section 5.1. Also returns an error when generating a random client nonce failed.

func ScramSha256Client

func ScramSha256Client(authz, authn string, passwd []byte) (ClientMech, error)

ScramSha256Client returns a ClientMech implementation for the SCRAM-SHA-256 mechanism, as specified in RFC 7677. Returns an error if SASLprep on authn, or passwd fails, as described in RFC 5802, section 5.1. Also returns an error when generating a random client nonce failed.

type EcdsaAuthenticator

type EcdsaAuthenticator interface {
	// GetPublicKey returns the public key for an authn, or an error if the
	// public key could not be retrieved.
	GetPublicKey(authn string) (*ecdsa.PublicKey, error)
	// DeriveAuthz derives an authz from an authn. It is only called when no
	// authz has been requested by the client. Return the empty string if no
	// authz can be derived from the supplied authn.
	DeriveAuthz(authn string) string
	// Authorize verifies whether an authn is authorized to use the requested or
	// derived authz. Return false to fail authorization.
	Authorize(authz, authn string) bool
}

EcdsaAuthenticator is supplied to EcdsaNist256pChallengeServer to implement retrieving the public key for an authn, authz derivation, and authorization checking.

Example
package main

import (
	"crypto/ecdsa"
	"errors"
	"fmt"

	"github.com/phedny/sasler"
)

func main() {
	// EcdsaClientConn() represents a function that returns a client that has
	// requested authentication using the ECDSA-NIST256P-CHALLENGE mechanism.
	conn := EcdsaClientConn()
	auth := myEcdsaAuthenticator{}
	mech := sasler.EcdsaNist256pChallengeServer(&auth)

	// ECDSA-NIST256P-CHALLENGE requires a couple of messages to be exchanged
	// between client and server.
	for {
		data, err := mech.Data(conn.Read())
		if err != nil {
			// Abort() represents a function that uses a protocol-specific way to
			// signal to the client that authentication has failed.
			conn.Abort()
			fmt.Println(err)
			return
		}
		// Relay data from mech to client
		if data != nil {
			conn.Write(data)
		}
		// Test if the conversation has completed and show the authz
		completed, authz := mech.HasCompleted()
		if completed {
			// Success() represents a function that uses a protocol-specific way to
			// signal to the client that authentication has succeeded.
			conn.Success()
			fmt.Println("Authorized identity:", authz)
			return
		}
	}

}

// myEcdsaAuthenticator is an example EcdsaAuthenticator that accepts only a
// single username.
type myEcdsaAuthenticator struct{}

// GetPublicKey retrieves the public key, if authn is "user".
func (a *myEcdsaAuthenticator) GetPublicKey(authn string) (*ecdsa.PublicKey, error) {
	if authn != "user" {
		return nil, errors.New("unknown authn")
	}
	return RetrievePublicKey(authn), nil
}

// DeriveAuthz derives an authz from an authn.
func (a *myEcdsaAuthenticator) DeriveAuthz(authn string) string {
	return authn
}

// Authorize checks whether the authn is authorized to act on behalf of the
// authz. This implementation also allows anyone authenticated as Admin to be
// authorized for any identity.
func (a *myEcdsaAuthenticator) Authorize(authz, authn string) bool {
	return authz == authn || authn == "Admin"
}
Output:

Authorized identity: user

type ExternalAuthenticator

type ExternalAuthenticator interface {
	// DeriveAuthz derives an authz from external sources. It is only called when
	// no authz has been requested by the client. Return the empty string if no
	// authz can be derived from external sources.
	DeriveAuthz() string
	// Authorize verifies whether an externally derived identity is authorized to
	// use the requested or derived authz. Return false to fail authorization.
	Authorize(authz string) bool
}

ExternalAuthenticator is supplied to ExternalServer to implement authz derivation and authorization checking.

Example
package main

import (
	"crypto/x509"
	"fmt"

	"github.com/phedny/sasler"
)

func main() {
	// TLSConn() represents a function that returns a *tls.Conn with a client
	// that has presented a client certificate and has requested authentication
	// using the EXTERNAL mechanism.
	certs := TLSConn().ConnectionState().PeerCertificates
	if len(certs) == 0 {
		fmt.Println("No client TLS certificate.")
		return
	}
	auth := myExternalAuthenticator{certs[0]}
	mech := sasler.ExternalServer(&auth)

	// EXTERNAL expects one message from the client
	_, err := mech.Data([]byte(""))
	if err != nil {
		fmt.Println(err)
	}

	// Retrieve authorized identity
	completed, authz := mech.HasCompleted()
	if completed {
		fmt.Println("Authorized identity:", authz)
	}

}

// myExternalAuthenticator is an example ExternalAuthenticator that accepts
// any authentication request when a certificate is present, because it assumes
// that connections to clients that present invalid or untrusted certificates
// have been denied.
type myExternalAuthenticator struct {
	cert *x509.Certificate
}

// DeriveAuthz derives an authz from the common name of the certificate.
func (a *myExternalAuthenticator) DeriveAuthz() string {
	return "cn=" + a.cert.Subject.CommonName
}

// Authorize checks whether the authz matches the common name of the
// certificate. This implementation also allows anyone authenticated as
// admin@example.com to be authorized for any identity.
func (a *myExternalAuthenticator) Authorize(authz string) bool {
	cn := a.cert.Subject.CommonName
	return authz == "cn="+cn || authz == "cn=admin@example.com"
}
Output:

Authorized identity: cn=user@example.com

type OAuthBearerAuthenticator

type OAuthBearerAuthenticator interface {
	// VerifyToken verifies whether the supplied token is valid. The host and/or
	// port values default to "" and 0 respectively, if not provided by the
	// client. Return false to fail authentication.
	VerifyToken(token []byte, host string, port int) bool
	// DeriveAuthz derives an authz from a token. It is only called when no authz
	// has been requested by the client. Return the empty string if no authz can
	// be derived from the supplied token.
	DeriveAuthz(token []byte) string
	// Authorize verifies whether the provided token is authorized to use the
	// requested or derived authz. Return false to fail authorization.
	Authorize(authz string, token []byte) bool
}

OAuthBearerAuthenticator is supplied to OAuthBearerServer to implement token verification, authz derivation and authorization checking.

Example
package main

import (
	"bytes"
	"fmt"

	"github.com/phedny/sasler"
)

func main() {
	auth := myOAuthBearerAuthenticator{
		host:      "example.com",
		port:      143,
		signature: []byte("SiGNeD_By_auTHoRiTy"),
	}
	mech := sasler.OAuthBearerServer(&auth)

	// OAUTHBEARER expects one message from the client
	_, err := mech.Data([]byte("n,\x01host=example.com\x01port=143\x01auth=Bearer username,SiGNeD_By_auTHoRiTy\x01\x01"))
	if err != nil {
		fmt.Println(err)
	}

	// Retrieve authorized identity
	completed, authz := mech.HasCompleted()
	if completed {
		fmt.Println("Authorized identity:", authz)
	}

}

// myOAuthBearerAuthentication is an example OAuthBearerAuthentication that
// accepts only authentication requets for a specific host and port, signed by
// a static signature.
type myOAuthBearerAuthenticator struct {
	host      string
	port      int
	signature []byte
}

// VerifyToken verifies the host, port and signature of the provided token.
func (a *myOAuthBearerAuthenticator) VerifyToken(token []byte, host string, port int) bool {
	signature := bytes.Split(token, []byte(","))[1]
	return host == a.host && port == a.port && bytes.Equal(signature, a.signature)
}

// DeriveAuthz derives an authz from the token.
func (a *myOAuthBearerAuthenticator) DeriveAuthz(token []byte) string {
	return string(bytes.Split(token, []byte(","))[0])
}

// Authorize checks whether the authn in the token matches the authz. This
// implementation also allows anyone authenticated as Admin to be authorized
// for any identity.
func (a *myOAuthBearerAuthenticator) Authorize(authz string, token []byte) bool {
	authn := string(bytes.Split(token, []byte(","))[0])
	return authz == authn || authn == "Admin"
}
Output:

Authorized identity: username

type PlainAuthenticator

type PlainAuthenticator interface {
	// VerifyPasswd verifies whether the supplied combination of authn and passwd
	// is valid. Return false to fail authentication.
	VerifyPasswd(authn string, passwd []byte) bool
	// DerivceAuthz derives an authz from an authn. It is only called when no
	// authz has been requested by the client. Return the empty string if no
	// authz can be derived from the supplied authn.
	DeriveAuthz(authn string) string
	// Authorize verifies whether an authn is authorized to use the requested or
	// derived authz. Return false to fail authorization.
	Authorize(authz, authn string) bool
}

PlainAuthenticator is supplied to PlainServer to implement password verification, authz derivation and authorization checking.

Example
package main

import (
	"bytes"
	"fmt"

	"github.com/phedny/sasler"
)

func main() {
	auth := myPlainAuthenticator{
		user:   "user",
		passwd: []byte("pencil"),
	}
	mech := sasler.PlainServer(&auth)

	// PLAIN expects one message from the client
	_, err := mech.Data([]byte("\x00user\x00pencil"))
	if err != nil {
		fmt.Println(err)
	}

	// Retrieve authorized identity
	completed, authz := mech.HasCompleted()
	if completed {
		fmt.Println("Authorized identity:", authz)
	}

}

// myPlainAuthenticator is an example PlainAuthenticator that accepts only a
// single username/password combination.
type myPlainAuthenticator struct {
	user   string
	passwd []byte
}

// VerifyPasswd verifies the username and password.
func (a *myPlainAuthenticator) VerifyPasswd(authn string, passwd []byte) bool {
	return authn == a.user && bytes.Equal(passwd, a.passwd)
}

// DeriveAuthz derives an authz from an authn.
func (a *myPlainAuthenticator) DeriveAuthz(authn string) string {
	return authn
}

// Authorize checks whether the authn is authorized to act on behalf of the
// authz. This implementation also allows anyone authenticatd as Admin to be
// authorized for any identity.
func (a *myPlainAuthenticator) Authorize(authz, authn string) bool {
	return authz == authn || authn == "Admin"
}
Output:

Authorized identity: user

type ScramAuthenticator

type ScramAuthenticator interface {
	// GetCredentials returns the credentials for an authn, or an error if the
	// credentials could not be retrieved. The salt and iCount are parameters for
	// the SCRAM algorithm. If isSalted is true, passwd is salted using the salt
	// and iCount parameters. If isSalted is false, passwd is return plaintext.
	// It is advised to store salted passwords and therefore implementations
	// should always return a salted password and isSalted = true.
	GetCredentials(authn string) (passwd []byte, isSalted bool, salt []byte, iCount int, err error)
	// DeriveAuthz derives an authz from an authn. It is only called when no
	// authz has been requested by the client. Return the empty string if no
	// authz can be derived from the supplied authn.
	DeriveAuthz(authn string) string
	// Authorize verifies whether an authn is authorized to use the requested or
	// derived authz. Return false to fail authorization.
	Authorize(authz, authn string) bool
}

ScramAuthenticator is passed to ScramSha1Server or ScramSha256Server to implement credential retrieval, authz derivation and authorization checking.

Example
package main

import (
	"errors"
	"fmt"

	"github.com/phedny/sasler"
)

func main() {
	// ScramClientConn() represents a function that returns a client that has
	// requested authentication using the SCRAM-SHA-1 mechanism.
	conn := ScramClientConn()
	auth := myScramAuthenticator{}
	mech, err := sasler.ScramSha1Server(&auth)
	if err != nil {
		fmt.Println(err)
		return
	}

	// SCRAM-* requires a couple of messages to be exchanged between client and
	// server.
	for {
		data, err := mech.Data(conn.Read())
		if err != nil {
			// Abort() represents a function that uses a protocol-specific way to
			// signal to the client that authentication has failed.
			conn.Abort()
			fmt.Println(err)
			return
		}
		// Relay data from mech to client
		if data != nil {
			conn.Write(data)
		}
		// Test if the conversation has completed and show the authz
		completed, authz := mech.HasCompleted()
		if completed {
			// Success() represents a function that uses a protocol-specific way to
			// signal to the client that authentication has succeeded.
			conn.Success()
			fmt.Println("Authorized identity:", authz)
			return
		}
	}

}

// myScramAuthenticator is an example ScramAuthenticator that accepts only a
// single username/password combination.
type myScramAuthenticator struct{}

// GetCredentials returns the credentials that belong to the requested authn.
func (a *myScramAuthenticator) GetCredentials(authn string) (passwd []byte, isSalted bool, salt []byte, iCount int, err error) {
	if authn != "user" {
		return nil, false, nil, 0, errors.New("unknown authn")
	}
	// passwd is stored in a database in salted form, which is recommended.
	passwd = []byte("\x1d\x96\xee:R\x9bZ_\x9eG\xc0\x1f\"\x9a,\xb8\xa6\xe1_}")
	// If a plaintext password is returned, return isSalted = false.
	isSalted = true
	// Both salt and iCount must be retrieved from database if isSalted = true,
	// otherwise they might be random. If salts are stored in the database, it is
	// recommended to use a different salt for every entry.
	salt = []byte("A%\xc2G\xe4:\xb1\xe9<m\xffv")
	iCount = 4096
	return
}

// DeriveAuthz derives an authz from an authn.
func (a *myScramAuthenticator) DeriveAuthz(authn string) string {
	return authn
}

// Authorize checks whether the authn is authorized to act on behalf of the
// authz. This implementation also allows anyone authenticated as Admin to be
// authorized for any identity.
func (a *myScramAuthenticator) Authorize(authz, authn string) bool {
	return authz == authn || authn == "Admin"
}
Output:

Authorized identity: user

type ServerMech

type ServerMech interface {
	// Mech returns the full name of the mechanism as described in its
	// specification, and a bool that is true when this is a client-first
	// mechanism.
	Mech() (string, bool)
	// Data must be called each time SASL data arrived from the other party,
	// providing the bytes of the message. It returns the bytes of the message
	// that must be returned to the other party, or an error when authentication
	// failed and must be aborted. If the returned []byte is nil and no error is
	// returned, authentication has finished successfully.
	//
	// On a client-first mechanism, the first call to Data must be done with the
	// initial response received from the client. On a server-first mechanism,
	// the first call to Data must be done with nil or a zero length slice.
	Data(data []byte) ([]byte, error)
	// HasCompleted returns (true, authz) if the authentication proccess has
	// completed successfully, or (true, "") if it has failed, or (false, "") if
	// it's still in progress.
	HasCompleted() (bool, string)
}

ServerMech describes the functions that are implemented by the server-side implementation of a SASL mechanism.

Example
// ClientConn() represents retrieving a connection with a client that
// initiated SASL authentication, including the mechanism name it requested.
mechName, conn := ClientConn()

// CreateMechanism() represents a function that returns a ServerMech based on
// the requested mechanism name.
mech := CreateMechanism(mechName)
_, clientFirst := mech.Mech()

// For server-first mechanisms, send the first message
if !clientFirst {
	data, err := mech.Data(nil)
	if err != nil {
		fmt.Println("Authentication aborted.")
		return
	}
	conn.Write(Data, data)
}

// Relay authentication data until error or completion
for {
	data := conn.Read()
	data, err := mech.Data(data)
	if err != nil {
		conn.Write(Failure, nil)
		switch err {
		case sasler.ErrAuthenticationFailed:
			fmt.Println("Authentication failed.")
		case sasler.ErrUnauthorized:
			fmt.Println("Unauthorized.")
		default:
			fmt.Println("Authentication aborted.", err)
		}
		return
	}
	if data != nil {
		conn.Write(Data, data)
	}
	completed, authz := mech.HasCompleted()
	if completed {
		conn.Write(Success, nil)
		fmt.Println("Authentication succeeded, authorised id:", authz)
		return
	}
}
Output:

Authentication succeeded, authorised id: username

func AnonymousServer

func AnonymousServer(authz string, auth AnonymousAuthenticator) ServerMech

AnonymousServer returns a ServerMech implementation for the ANONYMOUS mechanism, as specified in RFC 4505.

func EcdsaNist256pChallengeServer

func EcdsaNist256pChallengeServer(auth EcdsaAuthenticator) ServerMech

EcdsaNist256pChallengeServer returns a SaslMech implementation for the ECDSA-NIST256P-CHALLENGE mechanism, as specified in ecdsatool.

func ExternalServer

func ExternalServer(auth ExternalAuthenticator) ServerMech

ExternalServer returns a ServerMech implementation for the EXTERNAL mechanism, as specified in RFC 4422, appendix A.

func OAuthBearerServer

func OAuthBearerServer(auth OAuthBearerAuthenticator) ServerMech

OAuthBearerServer returns a ServerMech implementation for the OAUTHBEARER mechanism, as specified in RFC 7628.

func PlainServer

func PlainServer(auth PlainAuthenticator) ServerMech

PlainServer returns a ServerMech implementation for the PLAIN mechanism, as specified in RFC 4616.

func ScramSha1Server

func ScramSha1Server(auth ScramAuthenticator) (ServerMech, error)

ScramSha1Server returns a server-side SaslMech implementation for the SCRAM-SHA-1 mechanism, as specified in RFC 5802. Returns an error when generating a random server nonce failed.

func ScramSha256Server

func ScramSha256Server(auth ScramAuthenticator) (ServerMech, error)

ScramSha256Server returns a server-side SaslMech implementation for the SCRAM-SHA-256 mechanism, as specified in RFC 7677. Returns an error when generating a random server nonce failed.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL