Add OAuth2 support

Use provided host for certificate verification

Remove redundant ANONYMOUS mechanism support
This commit is contained in:
Joshua Martin 2015-09-17 10:43:05 -07:00
parent 222c8f8fd0
commit 88f429802e
3 changed files with 44 additions and 35 deletions

37
xmpp.go
View File

@ -127,6 +127,16 @@ type Options struct {
// from the server. Use "" to let the server generate one for your client. // from the server. Use "" to let the server generate one for your client.
Resource string Resource string
// OAuthScope provides go-xmpp the required scope for OAuth2 authentication.
OAuthScope string
// OAuthToken provides go-xmpp with the required OAuth2 token used to authenticate
OAuthToken string
// OAuthXmlNs provides go-xmpp with the required namespaced used for OAuth2 authentication. This is
// provided to the server as the xmlns:auth attribute of the OAuth2 authentication request.
OAuthXmlNs string
// TLS Config // TLS Config
TLSConfig *tls.Config TLSConfig *tls.Config
@ -163,6 +173,9 @@ func (o Options) NewClient() (*Client, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
if strings.LastIndex(o.Host, ":") > 0 {
host = host[:strings.LastIndex(o.Host, ":")]
}
client := new(Client) client := new(Client)
if o.NoTLS { if o.NoTLS {
@ -172,15 +185,12 @@ func (o Options) NewClient() (*Client, error) {
if o.TLSConfig != nil { if o.TLSConfig != nil {
tlsconn = tls.Client(c, o.TLSConfig) tlsconn = tls.Client(c, o.TLSConfig)
} else { } else {
DefaultConfig.ServerName = strings.Split(o.User, "@")[1] DefaultConfig.ServerName = host
tlsconn = tls.Client(c, &DefaultConfig) tlsconn = tls.Client(c, &DefaultConfig)
} }
if err = tlsconn.Handshake(); err != nil { if err = tlsconn.Handshake(); err != nil {
return nil, err return nil, err
} }
if strings.LastIndex(o.Host, ":") > 0 {
host = host[:strings.LastIndex(o.Host, ":")]
}
insecureSkipVerify := DefaultConfig.InsecureSkipVerify insecureSkipVerify := DefaultConfig.InsecureSkipVerify
if o.TLSConfig != nil { if o.TLSConfig != nil {
insecureSkipVerify = o.TLSConfig.InsecureSkipVerify insecureSkipVerify = o.TLSConfig.InsecureSkipVerify
@ -274,11 +284,13 @@ func (c *Client) init(o *Options) error {
} }
var domain string var domain string
var user string
a := strings.SplitN(o.User, "@", 2) a := strings.SplitN(o.User, "@", 2)
if len(o.User) > 0 { if len(o.User) > 0 {
if len(a) != 2 { if len(a) != 2 {
return errors.New("xmpp: invalid username (want user@domain): " + o.User) return errors.New("xmpp: invalid username (want user@domain): " + o.User)
} }
user = a[0]
domain = a[1] domain = a[1]
} // Otherwise, we'll be attempting ANONYMOUS } // Otherwise, we'll be attempting ANONYMOUS
@ -315,19 +327,16 @@ func (c *Client) init(o *Options) error {
mechanism := "" mechanism := ""
for _, m := range f.Mechanisms.Mechanism { for _, m := range f.Mechanisms.Mechanism {
if m == "ANONYMOUS" { if m == "X-OAUTH2" && o.OAuthToken != "" && o.OAuthScope != "" {
mechanism = m mechanism = m
fmt.Fprintf(c.conn, "<auth xmlns='%s' mechanism='ANONYMOUS' />\n", nsSASL) // Oauth authentication: send base64-encoded \x00 user \x00 token.
raw := "\x00" + user + "\x00" + o.OAuthToken
enc := make([]byte, base64.StdEncoding.EncodedLen(len(raw)))
base64.StdEncoding.Encode(enc, []byte(raw))
fmt.Fprintf(c.conn, "<auth xmlns='%s' mechanism='X-OAUTH2' auth:service='oauth2' "+
"xmlns:auth='%s'>%s</auth>\n", nsSASL, o.OAuthXmlNs, enc)
break break
} }
a := strings.SplitN(o.User, "@", 2)
if len(a) != 2 {
return errors.New("xmpp: invalid username (want user@domain): " + o.User)
}
user := a[0]
domain := a[1]
if m == "PLAIN" { if m == "PLAIN" {
mechanism = m mechanism = m
// Plain authentication: send base64-encoded \x00 user \x00 password. // Plain authentication: send base64-encoded \x00 user \x00 password.

View File

@ -1,19 +1,19 @@
package xmpp package xmpp
import ( import (
"fmt" "fmt"
) )
func (c* Client) PingC2S(jid, server string) { func (c *Client) PingC2S(jid, server string) {
fmt.Fprintf(c.conn, "<iq from='%s' to='%s' id='c2s1' type='get'>\n" + fmt.Fprintf(c.conn, "<iq from='%s' to='%s' id='c2s1' type='get'>\n"+
"<ping xmlns='urn:xmpp:ping'/>\n" + "<ping xmlns='urn:xmpp:ping'/>\n"+
"</iq>", "</iq>",
xmlEscape(jid), xmlEscape(server)) xmlEscape(jid), xmlEscape(server))
} }
func (c* Client) PingS2S(fromServer, toServer string) { func (c *Client) PingS2S(fromServer, toServer string) {
fmt.Fprintf(c.conn, "<iq from='%s' to='%s' id='s2s1' type='get'>\n" + fmt.Fprintf(c.conn, "<iq from='%s' to='%s' id='s2s1' type='get'>\n"+
"<ping xmlns='urn:xmpp:ping'/>\n" + "<ping xmlns='urn:xmpp:ping'/>\n"+
"</iq>", "</iq>",
xmlEscape(fromServer), xmlEscape(toServer)) xmlEscape(fromServer), xmlEscape(toServer))
} }

View File

@ -1,20 +1,20 @@
package xmpp package xmpp
import ( import (
"fmt" "fmt"
) )
func (c* Client) ApproveSubscription(jid string) { func (c *Client) ApproveSubscription(jid string) {
fmt.Fprintf(c.conn, "<presence to='%s' type='subscribed'/>", fmt.Fprintf(c.conn, "<presence to='%s' type='subscribed'/>",
xmlEscape(jid)) xmlEscape(jid))
} }
func (c* Client) RevokeSubscription(jid string) { func (c *Client) RevokeSubscription(jid string) {
fmt.Fprintf(c.conn, "<presence to='%s' type='unsubscribed'/>", fmt.Fprintf(c.conn, "<presence to='%s' type='unsubscribed'/>",
xmlEscape(jid)) xmlEscape(jid))
} }
func (c* Client) RequestSubscription(jid string) { func (c *Client) RequestSubscription(jid string) {
fmt.Fprintf(c.conn, "<presence to='%s' type='subscribe'/>", fmt.Fprintf(c.conn, "<presence to='%s' type='subscribe'/>",
xmlEscape(jid)) xmlEscape(jid))
} }