From 9c8353d081ea9147d19866441eaa254de916e08d Mon Sep 17 00:00:00 2001 From: Mickael Remond Date: Tue, 1 Oct 2019 10:59:55 +0200 Subject: [PATCH] Introduce Credential structure to define auth type For now we are planning to support Password and OAuthToken. In the future, we would like to add certificate-based authentication. --- README.md | 6 ++--- _examples/xmpp_echo/xmpp_echo.go | 2 +- _examples/xmpp_jukebox/xmpp_jukebox.go | 6 ++--- auth.go | 36 ++++++++++++++++++++++---- client.go | 4 +-- client_test.go | 8 +++--- cmd/fluuxmpp/send.go | 6 ++--- cmd/go.sum | 1 + config.go | 2 +- session.go | 2 +- 10 files changed, 50 insertions(+), 23 deletions(-) diff --git a/README.md b/README.md index cf546ae..a3f4ae4 100644 --- a/README.md +++ b/README.md @@ -34,8 +34,8 @@ Here is an example code to configure a client to allow connecting to a server wi config := xmpp.Config{ Address: "localhost:5222", Jid: "test@localhost", - Password: "test", - TLSConfig: tls.Config{InsecureSkipVerify: true}, + Credential: xmpp.Password("Test"), + TLSConfig: tls.Config{InsecureSkipVerify: true}, } ``` @@ -96,7 +96,7 @@ func main() { config := xmpp.Config{ Address: "localhost:5222", Jid: "test@localhost", - Password: "test", + Credential: xmpp.Password("Test"), StreamLogger: os.Stdout, Insecure: true, } diff --git a/_examples/xmpp_echo/xmpp_echo.go b/_examples/xmpp_echo/xmpp_echo.go index 38cbe62..f987ac5 100644 --- a/_examples/xmpp_echo/xmpp_echo.go +++ b/_examples/xmpp_echo/xmpp_echo.go @@ -17,7 +17,7 @@ func main() { config := xmpp.Config{ Address: "localhost:5222", Jid: "test@localhost", - Password: "test", + Credential: xmpp.Password("test"), StreamLogger: os.Stdout, Insecure: true, // TLSConfig: tls.Config{InsecureSkipVerify: true}, diff --git a/_examples/xmpp_jukebox/xmpp_jukebox.go b/_examples/xmpp_jukebox/xmpp_jukebox.go index ed05b10..51a401d 100644 --- a/_examples/xmpp_jukebox/xmpp_jukebox.go +++ b/_examples/xmpp_jukebox/xmpp_jukebox.go @@ -32,9 +32,9 @@ func main() { // 2. Prepare XMPP client config := xmpp.Config{ - Address: *address, - Jid: *jid, - Password: *password, + Address: *address, + Jid: *jid, + Credential: xmpp.Password(*password), // StreamLogger: os.Stdout, Insecure: true, } diff --git a/auth.go b/auth.go index 926426d..0d5b79d 100644 --- a/auth.go +++ b/auth.go @@ -10,8 +10,34 @@ import ( "gosrc.io/xmpp/stanza" ) -func authSASL(socket io.ReadWriter, decoder *xml.Decoder, f stanza.StreamFeatures, user string, password string) (err error) { - // TODO: Implement other type of SASL Authentication +// Credential is used to pass the type of secret that will be used to connect to XMPP server. +// It can be either a password or an OAuth 2 bearer token. +type Credential struct { + secret string + mechanisms []string +} + +func Password(pwd string) Credential { + credential := Credential{ + secret: pwd, + mechanisms: []string{"PLAIN"}, + } + return credential +} + +func OAuthToken(token string) Credential { + credential := Credential{ + secret: token, + mechanisms: []string{"X-OAUTH2"}, + } + return credential +} + +// ============================================================================ +// Authentication flow for SASL mechanisms + +func authSASL(socket io.ReadWriter, decoder *xml.Decoder, f stanza.StreamFeatures, user string, credential Credential) (err error) { + // TODO: Implement other type of SASL mechanisms havePlain := false for _, m := range f.Mechanisms.Mechanism { if m == "PLAIN" { @@ -24,12 +50,12 @@ func authSASL(socket io.ReadWriter, decoder *xml.Decoder, f stanza.StreamFeature return NewConnError(err, true) } - return authPlain(socket, decoder, user, password) + return authPlain(socket, decoder, user, credential) } // Plain authentication: send base64-encoded \x00 user \x00 password -func authPlain(socket io.ReadWriter, decoder *xml.Decoder, user string, password string) error { - raw := "\x00" + user + "\x00" + password +func authPlain(socket io.ReadWriter, decoder *xml.Decoder, user string, credential Credential) error { + raw := "\x00" + user + "\x00" + credential.secret enc := make([]byte, base64.StdEncoding.EncodedLen(len(raw))) base64.StdEncoding.Encode(enc, []byte(raw)) fmt.Fprintf(socket, "%s", stanza.NSSASL, enc) diff --git a/client.go b/client.go index 6275920..cace5fd 100644 --- a/client.go +++ b/client.go @@ -111,8 +111,8 @@ func NewClient(config Config, r *Router) (c *Client, err error) { return nil, NewConnError(err, true) } - if config.Password == "" { - err = errors.New("missing password") + if config.Credential.secret == "" { + err = errors.New("missing credential") return nil, NewConnError(err, true) } diff --git a/client_test.go b/client_test.go index c5e575d..13bbb36 100644 --- a/client_test.go +++ b/client_test.go @@ -25,7 +25,7 @@ func TestClient_Connect(t *testing.T) { mock.Start(t, testXMPPAddress, handlerConnectSuccess) // Test / Check result - config := Config{Address: testXMPPAddress, Jid: "test@localhost", Password: "test", Insecure: true} + config := Config{Address: testXMPPAddress, Jid: "test@localhost", Credential: Password("test"), Insecure: true} var client *Client var err error @@ -47,7 +47,7 @@ func TestClient_NoInsecure(t *testing.T) { mock.Start(t, testXMPPAddress, handlerAbortTLS) // Test / Check result - config := Config{Address: testXMPPAddress, Jid: "test@localhost", Password: "test"} + config := Config{Address: testXMPPAddress, Jid: "test@localhost", Credential: Password("test")} var client *Client var err error @@ -71,7 +71,7 @@ func TestClient_FeaturesTracking(t *testing.T) { mock.Start(t, testXMPPAddress, handlerAbortTLS) // Test / Check result - config := Config{Address: testXMPPAddress, Jid: "test@localhost", Password: "test"} + config := Config{Address: testXMPPAddress, Jid: "test@localhost", Credential: Password("test")} var client *Client var err error @@ -94,7 +94,7 @@ func TestClient_RFC3921Session(t *testing.T) { mock.Start(t, testXMPPAddress, handlerConnectWithSession) // Test / Check result - config := Config{Address: testXMPPAddress, Jid: "test@localhost", Password: "test", Insecure: true} + config := Config{Address: testXMPPAddress, Jid: "test@localhost", Credential: Password("test"), Insecure: true} var client *Client var err error diff --git a/cmd/fluuxmpp/send.go b/cmd/fluuxmpp/send.go index ee01a27..5760d8d 100644 --- a/cmd/fluuxmpp/send.go +++ b/cmd/fluuxmpp/send.go @@ -32,9 +32,9 @@ func sendxmpp(cmd *cobra.Command, args []string) { var err error client, err := xmpp.NewClient(xmpp.Config{ - Jid: viper.GetString("jid"), - Address: viper.GetString("addr"), - Password: viper.GetString("password"), + Jid: viper.GetString("jid"), + Address: viper.GetString("addr"), + Credential: xmpp.Password(viper.GetString("password")), }, xmpp.NewRouter()) if err != nil { diff --git a/cmd/go.sum b/cmd/go.sum index 5d9460f..0910af6 100644 --- a/cmd/go.sum +++ b/cmd/go.sum @@ -143,6 +143,7 @@ golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGm golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522 h1:bhOzK9QyoD0ogCnFro1m2mz41+Ib0oOhfJnBp5MR4K4= golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7 h1:9zdDQZ7Thm29KFXgAX/+yaf3eVbP7djjWp/dXAppNCc= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= diff --git a/config.go b/config.go index d3327c1..84a7d29 100644 --- a/config.go +++ b/config.go @@ -10,7 +10,7 @@ type Config struct { Address string Jid string parsedJid *Jid // For easier manipulation - Password string + Credential Credential StreamLogger *os.File // Used for debugging Lang string // TODO: should default to 'en' ConnectTimeout int // Client timeout in seconds. Default to 15 diff --git a/session.go b/session.go index b429b16..1cad2b4 100644 --- a/session.go +++ b/session.go @@ -168,7 +168,7 @@ func (s *Session) auth(o Config) { return } - s.err = authSASL(s.streamLogger, s.decoder, s.Features, o.parsedJid.Node, o.Password) + s.err = authSASL(s.streamLogger, s.decoder, s.Features, o.parsedJid.Node, o.Credential) } // Attempt to resume session using stream management