Add X-OAUTH2 authentication and example

This commit is contained in:
Mickael Remond 2019-10-01 11:30:23 +02:00 committed by Mickaël Rémond
parent 9c8353d081
commit 4f68c5eee2
3 changed files with 72 additions and 15 deletions

View file

@ -48,6 +48,3 @@ func handleMessage(s xmpp.Sender, p stanza.Packet) {
reply := stanza.Message{Attrs: stanza.Attrs{To: msg.From}, Body: msg.Body} reply := stanza.Message{Attrs: stanza.Attrs{To: msg.From}, Body: msg.Body}
_ = s.Send(reply) _ = s.Send(reply)
} }
// TODO create default command line client to send message or to send an arbitrary XMPP sequence from a file,
// (using templates ?)

View file

@ -0,0 +1,48 @@
/*
xmpp_oauth2 is a demo client that connect on an XMPP server using OAuth2 and prints received messages.
*/
package main
import (
"fmt"
"log"
"os"
"gosrc.io/xmpp"
"gosrc.io/xmpp/stanza"
)
func main() {
config := xmpp.Config{
Address: "localhost:5222",
Jid: "test@localhost",
Credential: xmpp.OAuthToken("OdAIsBlY83SLBaqQoClAn7vrZSHxixT8"),
StreamLogger: os.Stdout,
// Insecure: true,
// TLSConfig: tls.Config{InsecureSkipVerify: true},
}
router := xmpp.NewRouter()
router.HandleFunc("message", handleMessage)
client, err := xmpp.NewClient(config, router)
if err != nil {
log.Fatalf("%+v", err)
}
// If you pass the client to a connection manager, it will handle the reconnect policy
// for you automatically.
cm := xmpp.NewStreamManager(client, nil)
log.Fatal(cm.Run())
}
func handleMessage(s xmpp.Sender, p stanza.Packet) {
msg, ok := p.(stanza.Message)
if !ok {
_, _ = fmt.Fprintf(os.Stdout, "Ignoring packet: %T\n", p)
return
}
_, _ = fmt.Fprintf(os.Stdout, "Body = %s - from = %s\n", msg.Body, msg.From)
}

36
auth.go
View file

@ -37,28 +37,30 @@ func OAuthToken(token string) Credential {
// Authentication flow for SASL mechanisms // Authentication flow for SASL mechanisms
func authSASL(socket io.ReadWriter, decoder *xml.Decoder, f stanza.StreamFeatures, user string, credential Credential) (err error) { func authSASL(socket io.ReadWriter, decoder *xml.Decoder, f stanza.StreamFeatures, user string, credential Credential) (err error) {
// TODO: Implement other type of SASL mechanisms var matchingMech string
havePlain := false for _, mech := range credential.mechanisms {
for _, m := range f.Mechanisms.Mechanism { if isSupportedMech(mech, f.Mechanisms.Mechanism) {
if m == "PLAIN" { matchingMech = mech
havePlain = true
break break
} }
} }
if !havePlain {
err := fmt.Errorf("PLAIN authentication is not supported by server: %v", f.Mechanisms.Mechanism) switch matchingMech {
case "PLAIN", "X-OAUTH2":
// TODO: Implement other type of SASL mechanisms
return authPlain(socket, decoder, matchingMech, user, credential.secret)
default:
err := fmt.Errorf("no matching authentication (%v) supported by server: %v", credential.mechanisms, f.Mechanisms.Mechanism)
return NewConnError(err, true) return NewConnError(err, true)
} }
return authPlain(socket, decoder, user, credential)
} }
// Plain authentication: send base64-encoded \x00 user \x00 password // Plain authentication: send base64-encoded \x00 user \x00 password
func authPlain(socket io.ReadWriter, decoder *xml.Decoder, user string, credential Credential) error { func authPlain(socket io.ReadWriter, decoder *xml.Decoder, mech string, user string, secret string) error {
raw := "\x00" + user + "\x00" + credential.secret raw := "\x00" + user + "\x00" + secret
enc := make([]byte, base64.StdEncoding.EncodedLen(len(raw))) enc := make([]byte, base64.StdEncoding.EncodedLen(len(raw)))
base64.StdEncoding.Encode(enc, []byte(raw)) base64.StdEncoding.Encode(enc, []byte(raw))
fmt.Fprintf(socket, "<auth xmlns='%s' mechanism='PLAIN'>%s</auth>", stanza.NSSASL, enc) fmt.Fprintf(socket, "<auth xmlns='%s' mechanism='%s'>%s</auth>", stanza.NSSASL, mech, enc)
// Next message should be either success or failure. // Next message should be either success or failure.
val, err := stanza.NextPacket(decoder) val, err := stanza.NextPacket(decoder)
@ -77,3 +79,13 @@ func authPlain(socket io.ReadWriter, decoder *xml.Decoder, user string, credenti
} }
return err return err
} }
// isSupportedMech returns true if the mechanism is supported in the provided list.
func isSupportedMech(mech string, mechanisms []string) bool {
for _, m := range mechanisms {
if mech == m {
return true
}
}
return false
}