Changeset 67ea361


Ignore:
Timestamp:
2016-08-30T20:40:19Z (4 years ago)
Author:
dequis <dx@…>
Branches:
master
Children:
15c4cd4, a33ee0f
Parents:
2dc394c
Message:

hipchat: Add basic support for personal oauth tokens

Fixes trac ticket 1265 - see the comments on that ticket for reasons on
why personal tokens and not the usual oauth flow. TL;DR hipchat doesn't
allow third party apps to own oauth client secrets. Instead, those are
generated when the "addon" is "installed" which requires a flow that is
either impossible or too awkward to use in bitlbee.

So, after giving up on the right way and telling the users to manage
tokens the ugly way, what's left to do is easy, just a few tweaks in the
sasl blob and short-circuit most of the actual oauth stuff. I didn't
even bother changing the service struct from google. It's not used.

This also updates the gtalk SASL X-OAUTH2 code to use GStrings instead of
juggling with malloc/strlen/strcpy, and simplifies a bit the way
GStrings are used in the equivalent SASL PLAIN code.

Location:
protocols/jabber
Files:
3 edited

Legend:

Unmodified
Added
Removed
  • protocols/jabber/jabber.c

    r2dc394c r67ea361  
    8282        s->flags |= SET_NOSAVE | ACC_SET_OFFLINE_ONLY | SET_NULL_OK;
    8383
     84        set_add(&acc->set, "oauth", "false", set_eval_oauth, acc);
     85
    8486        if (strcmp(acc->prpl->name, "hipchat") == 0) {
    8587                set_setstr(&acc->set, "server", "chat.hipchat.com");
    8688        } else {
    87                 set_add(&acc->set, "oauth", "false", set_eval_oauth, acc);
    88 
    8989                /* this reuses set_eval_oauth, which clears the password */
    9090                set_add(&acc->set, "anonymous", "false", set_eval_oauth, acc);
     
    397397        if (g_strcasecmp(who, JABBER_OAUTH_HANDLE) == 0 &&
    398398            !(jd->flags & OPT_LOGGED_IN) && jd->fd == -1) {
    399                 if (sasl_oauth2_get_refresh_token(ic, message)) {
     399
     400                if (jd->flags & JFLAG_HIPCHAT) {
     401                        sasl_oauth2_got_token(ic, message, NULL, NULL);
     402                        return 1;
     403                } else if (sasl_oauth2_get_refresh_token(ic, message)) {
    400404                        return 1;
    401405                } else {
  • protocols/jabber/jabber.h

    r2dc394c r67ea361  
    340340int sasl_oauth2_get_refresh_token(struct im_connection *ic, const char *msg);
    341341int sasl_oauth2_refresh(struct im_connection *ic, const char *refresh_token);
     342void sasl_oauth2_got_token(gpointer data, const char *access_token, const char *refresh_token, const char *error);
    342343
    343344extern const struct oauth2_service oauth2_service_google;
  • protocols/jabber/sasl.c

    r2dc394c r67ea361  
    3939};
    4040
     41/* """"""""""""""""""""""""""""""oauth"""""""""""""""""""""""""""""" */
     42#define HIPCHAT_SO_CALLED_OAUTH_URL "https://hipchat.com/account/api"
     43
    4144xt_status sasl_pkt_mechanisms(struct xt_node *node, gpointer data)
    4245{
     
    4548        struct xt_node *c, *reply;
    4649        char *s;
    47         int sup_plain = 0, sup_digest = 0, sup_gtalk = 0, sup_anonymous = 0;
     50        int sup_plain = 0, sup_digest = 0, sup_gtalk = 0, sup_anonymous = 0, sup_hipchat_oauth = 0;
    4851        int want_oauth = FALSE, want_hipchat = FALSE, want_anonymous = FALSE;
    4952        GString *mechs;
     
    8083                } else if (c->text && g_strcasecmp(c->text, "X-OAUTH2") == 0) {
    8184                        sup_gtalk = 1;
     85                } else if (c->text && g_strcasecmp(c->text, "X-HIPCHAT-OAUTH2") == 0) {
     86                        sup_hipchat_oauth = 1;
    8287                }
    8388
     
    9095
    9196        if (!want_oauth && !sup_plain && !sup_digest) {
    92                 if (sup_gtalk) {
     97                if (sup_gtalk || sup_hipchat_oauth) {
    9398                        imcb_error(ic, "This server requires OAuth "
    9499                                   "(supported schemes:%s)", mechs->str);
     
    110115        }
    111116
    112         if (sup_gtalk && want_oauth) {
    113                 int len;
    114 
    115                 /* X-OAUTH2 is, not *the* standard OAuth2 SASL/XMPP implementation.
    116                    It's currently used by GTalk and vaguely documented on
    117                    http://code.google.com/apis/cloudprint/docs/rawxmpp.html . */
    118                 xt_add_attr(reply, "mechanism", "X-OAUTH2");
    119 
    120                 len = strlen(jd->username) + strlen(jd->oauth2_access_token) + 2;
    121                 s = g_malloc(len + 1);
    122                 s[0] = 0;
    123                 strcpy(s + 1, jd->username);
    124                 strcpy(s + 2 + strlen(jd->username), jd->oauth2_access_token);
    125                 reply->text = base64_encode((unsigned char *) s, len);
     117        if ((sup_gtalk || sup_hipchat_oauth) && want_oauth) {
     118                GString *gs;
     119
     120                gs = g_string_sized_new(128);
     121
     122                g_string_append_c(gs, '\0');
     123
     124                if (sup_gtalk) {
     125                        /* X-OAUTH2 is not *the* standard OAuth2 SASL/XMPP implementation.
     126                           It's currently used by GTalk and vaguely documented on
     127                           http://code.google.com/apis/cloudprint/docs/rawxmpp.html */
     128                        xt_add_attr(reply, "mechanism", "X-OAUTH2");
     129
     130                        g_string_append(gs, jd->username);
     131                        g_string_append_c(gs, '\0');
     132                        g_string_append(gs, jd->oauth2_access_token);
     133                } else if (sup_hipchat_oauth) {
     134                        /* Hipchat's variant, not standard either, is documented here:
     135                           https://docs.atlassian.com/hipchat.xmpp/latest/xmpp/auth.html */
     136                        xt_add_attr(reply, "mechanism", "oauth2");
     137
     138                        g_string_append(gs, jd->oauth2_access_token);
     139                        g_string_append_c(gs, '\0');
     140                        g_string_append(gs, set_getstr(&ic->acc->set, "resource"));
     141                }
     142
     143                reply->text = base64_encode((unsigned char *) gs->str, gs->len);
    126144                reply->text_len = strlen(reply->text);
    127                 g_free(s);
     145                g_string_free(gs, TRUE);
     146
    128147        } else if (want_oauth) {
    129148                imcb_error(ic, "OAuth requested, but not supported by server");
     
    149168                /* The rest will be done later, when we receive a <challenge/>. */
    150169        } else if (sup_plain) {
    151                 int len;
    152170                GString *gs;
    153171                char *username;
     
    174192                }
    175193
    176                 len = gs->len;
    177                 s = g_string_free(gs, FALSE);
    178 
    179                 reply->text = base64_encode((unsigned char *) s, len);
     194                reply->text = base64_encode((unsigned char *) gs->str, gs->len);
    180195                reply->text_len = strlen(reply->text);
    181                 g_free(s);
     196                g_string_free(gs, TRUE);
    182197        }
    183198
     
    428443{
    429444        struct jabber_data *jd = ic->proto_data;
    430         char *msg, *url;
    431445
    432446        imcb_log(ic, "Starting OAuth authentication");
     
    434448        /* Temporary contact, just used to receive the OAuth response. */
    435449        imcb_add_buddy(ic, JABBER_OAUTH_HANDLE, NULL);
    436         url = oauth2_url(jd->oauth2_service);
    437         msg = g_strdup_printf("Open this URL in your browser to authenticate: %s", url);
    438         imcb_buddy_msg(ic, JABBER_OAUTH_HANDLE, msg, 0, 0);
     450
     451        if (jd->flags & JFLAG_HIPCHAT) {
     452                imcb_buddy_msg(ic, JABBER_OAUTH_HANDLE,
     453                        "Open this URL and generate a token with 'View Group' and 'Send Message' scopes: "
     454                        HIPCHAT_SO_CALLED_OAUTH_URL, 0, 0);
     455        } else {
     456                char *msg, *url;
     457
     458                url = oauth2_url(jd->oauth2_service);
     459                msg = g_strdup_printf("Open this URL in your browser to authenticate: %s", url);
     460                imcb_buddy_msg(ic, JABBER_OAUTH_HANDLE, msg, 0, 0);
     461
     462                g_free(msg);
     463                g_free(url);
     464        }
    439465        imcb_buddy_msg(ic, JABBER_OAUTH_HANDLE, "Respond to this message with the returned "
    440466                       "authorization token.", 0, 0);
    441467
    442         g_free(msg);
    443         g_free(url);
    444468}
    445469
     
    453477        return FALSE;
    454478}
    455 
    456 static void sasl_oauth2_got_token(gpointer data, const char *access_token, const char *refresh_token,
    457                                   const char *error);
    458479
    459480int sasl_oauth2_get_refresh_token(struct im_connection *ic, const char *msg)
     
    486507}
    487508
    488 static void sasl_oauth2_got_token(gpointer data, const char *access_token, const char *refresh_token, const char *error)
     509void sasl_oauth2_got_token(gpointer data, const char *access_token, const char *refresh_token, const char *error)
    489510{
    490511        struct im_connection *ic = data;
Note: See TracChangeset for help on using the changeset viewer.