3

I'm trying to access my Gmail emails over IMAP using XOAUTH2 in Ruby.

I've successfully generated an access token (and refresh token) by authenticating using OAuth 2.0 with the oauth2 gem. I'm going to use gmail_xoauth to access Gmail over IMAP. So I now need to generate the SASL initial client response, as per the Gmail XOAuth2 docs:

The SASL XOAUTH2 initial client response has the following format:  

    base64("user=" {User} "^Aauth=Bearer " {Access Token} "^A^A")

using the base64 encoding mechanism defined in RFC 4648.
^A represents a Control+A (\001).

I'm not clear how I represent the "Control+A" in my string. Do I simply use ^A?

key = Base64.encode64("user=#{email}^Aauth=Bearer #{access_token_obj.token}^A^A")

This python script uses \1 in place of ^A. I've also tried \001. Whatever I try, when authenticating (in irb) with the result I get:

>> imap = Net::IMAP.new('imap.gmail.com', 993, usessl=true, certs=nil, verify=false)
>> imap.authenticate('XOAUTH2', email, key)
OpenSSL::SSL::SSLError: SSL_write:: bad write retry

That error could be entirely unrelated, but I'm not confident any option I've tried is correct.

Phil Gyford
  • 13,432
  • 14
  • 81
  • 143
  • That looks entirely like you weren't able to connect to the server. – Max Jun 05 '13 at 20:05
  • Ah, thanks. Although today I'm instead getting this error: `Net::IMAP::NoResponseError: Invalid credentials (Failure)`. I guess that's progress... – Phil Gyford Jun 06 '13 at 09:23

1 Answers1

4

Finally figured it out... I didn't need to do the Base64 encoding step at all!

gmail_xoauth adds the XOAUTH authenticator to Net::IMAP itself. I realised that this only expects the unencoded access_token from Google, rather than the longer Base64-encoded string.

So, if:

email = `fredbloggstest@gmail.com`
# The result of the OAuth2 dance (as well as a refresh_token):
access_token = 'ya13.AHES6Y3F54_5fAoz_8VuG-7pzQAo3R0_ukt7dhfgRnJh41Q'

then I don't have to Base64 encode anything. I just do:

imap = Net::IMAP.new('imap.gmail.com', 993, usessl=true, certs=nil, verify=false)
imap.authenticate('XOAUTH2', email, access_token)

and I get back:

#<struct Net::IMAP::TaggedResponse tag="RUBY0001", name="OK", data=#<struct Net::IMAP::ResponseText code=nil, text="fredbloggstest@gmail.com Fred Bloggs authenticated (Success)">, raw_data="RUBY0001 OK fredbloggstest@gmail.com Fred Bloggs authenticated (Success)\r\n">

(As a bonus, this is a handy Ruby script for getting the access_token from the OAuth dance.)

Phil Gyford
  • 13,432
  • 14
  • 81
  • 143
  • 1
    And maybe if I hadn't got distracted by Google's own docs, I would have seen that `gmail_xoauth`'s README describes how to do this. Idiot. – Phil Gyford Jun 06 '13 at 10:35
  • Does this still work? I'm getting errors here: http://stackoverflow.com/questions/31443926/oauth2-token-not-working-for-gmail-xoauth-gem – locoboy Jul 16 '15 at 02:52