mail_xoauth2 1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/LICENSE +21 -0
- data/README.markdown +30 -0
- data/lib/mail_xoauth2/imap_xoauth2_authenticator.rb +24 -0
- data/lib/mail_xoauth2/oauth2_string.rb +17 -0
- data/lib/mail_xoauth2/smtp_xoauth2_authenticator.rb +38 -0
- data/lib/mail_xoauth2/version.rb +3 -0
- data/lib/mail_xoauth2.rb +5 -0
- data/test/helper.rb +46 -0
- data/test/test_imap_xoauth2_authenticator.rb +30 -0
- data/test/test_oauth2_string.rb +35 -0
- data/test/test_smtp_xoauth2_authenticator.rb +56 -0
- metadata +84 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 27a0ef9770c2172e574541dbb950bc4f7b074163a42903acfeadf2570c95609c
|
4
|
+
data.tar.gz: 499e917ef4edf4e6f4f3a3f8f51f5305f60c1d67bd6706ce94f8644ef331a2ff
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: b0dcd0f7f2c8fbd565b680866e7a29e72e9c8acb3891be7a36e521913dd53ff392a9490d5c3ef94354324f127efe0e78c881ddc9294ecd65cadf56529c7c0ba0
|
7
|
+
data.tar.gz: 9a4f954d7350be1d603de16aa1cd813c2046d1516ffe99cb7b4ee13b5e4bece972a0fcd9b778777f166e39a44fc71a5049f755bd1acaecf1a201b6db66518d14
|
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2018 Mailbutler
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
data/README.markdown
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
# mail_xoauth2
|
2
|
+
Get access to IMAP and STMP via OAuth2, using the standard Ruby Net libraries.
|
3
|
+
|
4
|
+
This gem is based on and inspired by [gmail_xoauth](https://github.com/nfo/gmail_xoauth).
|
5
|
+
|
6
|
+
## Usage
|
7
|
+
|
8
|
+
### IMAP OAuth 2.0
|
9
|
+
|
10
|
+
```ruby
|
11
|
+
require 'mail_xoauth2'
|
12
|
+
imap = Net::IMAP.new('imap.gmail.com', 993, usessl = true, certs = nil, verify = false)
|
13
|
+
imap.authenticate('XOAUTH2', 'myemail@gmail.com', my_oauth2_token)
|
14
|
+
messages_count = imap.status('INBOX', ['MESSAGES'])['MESSAGES']
|
15
|
+
puts "Seeing #{messages_count} messages in INBOX"
|
16
|
+
```
|
17
|
+
|
18
|
+
### SMTP OAuth 2.0
|
19
|
+
|
20
|
+
```ruby
|
21
|
+
require 'mail_xoauth2'
|
22
|
+
smtp = Net::SMTP.new('smtp.gmail.com', 587)
|
23
|
+
smtp.enable_starttls_auto
|
24
|
+
smtp.start('gmail.com', 'myemail@gmail.com', my_oauth2_token, :xoauth2)
|
25
|
+
smtp.finish
|
26
|
+
```
|
27
|
+
|
28
|
+
## License
|
29
|
+
|
30
|
+
See LICENSE for details.
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'net/imap'
|
4
|
+
|
5
|
+
module MailXoauth2
|
6
|
+
class ImapXoauth2Authenticator
|
7
|
+
def process(_data)
|
8
|
+
build_oauth2_string(@user, @oauth2_token, true)
|
9
|
+
end
|
10
|
+
|
11
|
+
private
|
12
|
+
|
13
|
+
# +user+ is an email address: roger@gmail.com
|
14
|
+
# +oauth2_token+ is the OAuth2 token
|
15
|
+
def initialize(user, oauth2_token)
|
16
|
+
@user = user
|
17
|
+
@oauth2_token = oauth2_token
|
18
|
+
end
|
19
|
+
|
20
|
+
include Oauth2String
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
Net::IMAP.add_authenticator('XOAUTH2', MailXoauth2::ImapXoauth2Authenticator)
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module MailXoauth2
|
2
|
+
module Oauth2String
|
3
|
+
private
|
4
|
+
|
5
|
+
#
|
6
|
+
# Builds the "oauth2 protocol authentication string". See https://developers.google.com/google-apps/gmail/xoauth2_protocol
|
7
|
+
#
|
8
|
+
# +user+ is an email address: roger@gmail.com
|
9
|
+
# +oauth2_token+ is the oauth2 token
|
10
|
+
def build_oauth2_string(user, oauth2_token, encode_base64 = false)
|
11
|
+
oauth2_string = format("user=%<user>s\1auth=Bearer %<token>s\1\1".encode('us-ascii'), user: user, token: oauth2_token)
|
12
|
+
oauth2_string = Base64.strict_encode64(oauth2_string) if encode_base64
|
13
|
+
|
14
|
+
oauth2_string
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'net/smtp'
|
2
|
+
require 'base64'
|
3
|
+
|
4
|
+
module MailXoauth2
|
5
|
+
module SmtpXoauth2Authenticator
|
6
|
+
def send_xoauth2(auth_token)
|
7
|
+
critical do
|
8
|
+
get_response("AUTH XOAUTH2 #{auth_token}")
|
9
|
+
end
|
10
|
+
end
|
11
|
+
private :send_xoauth2
|
12
|
+
|
13
|
+
def get_final_status
|
14
|
+
critical do
|
15
|
+
get_response('')
|
16
|
+
end
|
17
|
+
end
|
18
|
+
private :get_final_status
|
19
|
+
|
20
|
+
def auth_xoauth2(user, oauth2_token)
|
21
|
+
check_auth_args user, oauth2_token
|
22
|
+
|
23
|
+
auth_string = build_oauth2_string(user, oauth2_token, true)
|
24
|
+
res = send_xoauth2(auth_string)
|
25
|
+
|
26
|
+
# See note about SMTP protocol exchange in https://developers.google.com/gmail/xoauth2_protocol
|
27
|
+
res = get_final_status if res.continue?
|
28
|
+
|
29
|
+
check_auth_response res
|
30
|
+
res
|
31
|
+
end
|
32
|
+
|
33
|
+
include Oauth2String
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
# Not pretty, right ?
|
38
|
+
Net::SMTP.__send__('include', MailXoauth2::SmtpXoauth2Authenticator)
|
data/lib/mail_xoauth2.rb
ADDED
data/test/helper.rb
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'test/unit'
|
5
|
+
require 'mocha/test_unit'
|
6
|
+
|
7
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
8
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
9
|
+
require 'mail_xoauth2'
|
10
|
+
|
11
|
+
# Wanna debug ? Activate the IMAP debug mode, it will show the client/server conversation
|
12
|
+
# Net::IMAP.debug = true
|
13
|
+
|
14
|
+
# SMTP debugging can only be enabled on Net::SMTP instances
|
15
|
+
# Net::SMTP.class_eval do
|
16
|
+
# def initialize_with_debug(*args)
|
17
|
+
# initialize_without_debug(*args)
|
18
|
+
# @debug_output = STDERR
|
19
|
+
# end
|
20
|
+
# alias_method :initialize_without_debug, :initialize
|
21
|
+
# alias_method :initialize, :initialize_with_debug
|
22
|
+
# end
|
23
|
+
|
24
|
+
VALID_CREDENTIALS = begin
|
25
|
+
YAML.load_file(File.join(File.dirname(__FILE__), 'valid_credentials.yml'))
|
26
|
+
rescue Errno::ENOENT
|
27
|
+
STDERR.puts '
|
28
|
+
Warning: some tests are disabled because they require valid credentials.
|
29
|
+
To enable them, create a file "test/valid_credentials.yml".
|
30
|
+
It should also ontain a valid OAuth2 access token.
|
31
|
+
Of course, this file is .gitignored. Template:
|
32
|
+
|
33
|
+
---
|
34
|
+
:email: someuser@gmail.com
|
35
|
+
:consumer_key: anonymous # "anonymous" is a valid value for testing
|
36
|
+
:consumer_secret: anonymous # "anonymous" is a valid value for testing
|
37
|
+
:token: 1/nE2xBCDOU0429bTeJySE11kRE95qzKQNlfTaaBcDeFg
|
38
|
+
:token_secret: 123Z/bMsi9fFhN6qHFWOabcd
|
39
|
+
:oauth2_token: ya29.AHES6ZTIpsLuSyMwnh-3C40WWcuiOe4N7he0a8xnkvDk_6Q_6yUg7E
|
40
|
+
|
41
|
+
'
|
42
|
+
false
|
43
|
+
end
|
44
|
+
|
45
|
+
class Test::Unit::TestCase
|
46
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
class TestImapXoauth2Authenticator < Test::Unit::TestCase
|
4
|
+
def setup; end
|
5
|
+
|
6
|
+
def test_xoauth2_authenticator_is_enabled
|
7
|
+
authenticators = Net::IMAP.__send__('class_variable_get', '@@authenticators')
|
8
|
+
assert_not_nil authenticators['XOAUTH2']
|
9
|
+
assert_equal authenticators['XOAUTH2'], GmailXoauth::ImapXoauth2Authenticator
|
10
|
+
end
|
11
|
+
|
12
|
+
def test_authenticate_with_invalid_credentials
|
13
|
+
imap = Net::IMAP.new('imap.gmail.com', 993, usessl = true, certs = nil, verify = false)
|
14
|
+
assert_raise(Net::IMAP::NoResponseError) do
|
15
|
+
imap.authenticate('XOAUTH2', 'roger@moore.com', 'a')
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def test_authenticate_with_valid_credentials
|
20
|
+
return unless VALID_CREDENTIALS
|
21
|
+
|
22
|
+
imap = Net::IMAP.new('imap.gmail.com', 993, usessl = true, certs = nil, verify = false)
|
23
|
+
imap.authenticate('XOAUTH2', VALID_CREDENTIALS[:email], VALID_CREDENTIALS[:oauth2_token])
|
24
|
+
mailboxes = imap.list('', '*')
|
25
|
+
assert_instance_of Array, mailboxes
|
26
|
+
assert_instance_of Net::IMAP::MailboxList, mailboxes.first
|
27
|
+
ensure
|
28
|
+
imap.disconnect if imap
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'helper'
|
4
|
+
|
5
|
+
class TestOauth2String < Test::Unit::TestCase
|
6
|
+
def setup; end
|
7
|
+
|
8
|
+
def test_build_oauth2_string
|
9
|
+
user = 'somebody@gmail.com'
|
10
|
+
oauth2_access_token = SecureRandom.urlsafe_base64
|
11
|
+
|
12
|
+
oauth2_string = SomeNetClass.new.__send__('build_oauth2_string', user, oauth2_access_token)
|
13
|
+
|
14
|
+
assert_equal(
|
15
|
+
"user=#{user}\1auth=Bearer #{oauth2_access_token}\1\1",
|
16
|
+
oauth2_string
|
17
|
+
)
|
18
|
+
end
|
19
|
+
|
20
|
+
def test_build_encoded_oauth2_string
|
21
|
+
user = 'somebody@gmail.com'
|
22
|
+
oauth2_access_token = SecureRandom.urlsafe_base64
|
23
|
+
|
24
|
+
oauth2_string = SomeNetClass.new.__send__('build_oauth2_string', user, oauth2_access_token, true)
|
25
|
+
|
26
|
+
assert_equal(
|
27
|
+
Base64.strict_encode64("user=#{user}\1auth=Bearer #{oauth2_access_token}\1\1"),
|
28
|
+
oauth2_string
|
29
|
+
)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
class SomeNetClass
|
34
|
+
include MailXoauth2::Oauth2String
|
35
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
class TestSmtpXoauth2Authenticator < Test::Unit::TestCase
|
4
|
+
def setup; end
|
5
|
+
|
6
|
+
def test_smtp_authenticator_is_enabled
|
7
|
+
assert Net::SMTP.new(nil).respond_to?(:auth_xoauth2), 'The Net::SMTP class should define the method :auth_xoauth2'
|
8
|
+
end
|
9
|
+
|
10
|
+
def test_authenticate_with_invalid_credentials
|
11
|
+
smtp = Net::SMTP.new('smtp.gmail.com', 587)
|
12
|
+
smtp.enable_starttls_auto
|
13
|
+
assert_raise(Net::SMTPAuthenticationError) do
|
14
|
+
smtp.start('gmail.com', 'roger@moore.com', 'a', :xoauth2)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def test_authenticate_with_valid_credentials
|
19
|
+
return unless VALID_CREDENTIALS
|
20
|
+
|
21
|
+
smtp = Net::SMTP.new('smtp.gmail.com', 587)
|
22
|
+
smtp.enable_starttls_auto
|
23
|
+
|
24
|
+
assert_nothing_raised do
|
25
|
+
smtp.start('gmail.com', VALID_CREDENTIALS[:email], VALID_CREDENTIALS[:oauth2_token], :xoauth2)
|
26
|
+
end
|
27
|
+
ensure
|
28
|
+
smtp.finish if smtp&.started?
|
29
|
+
end
|
30
|
+
|
31
|
+
# Test the handling of 3xx response to an "AUTH XOAUTH2" request... client
|
32
|
+
# should send a "\r\n" to get the actual error that occurred.
|
33
|
+
#
|
34
|
+
# See note about SMTP protocol exchange in https://developers.google.com/
|
35
|
+
# gmail/xoauth2_protocol
|
36
|
+
#
|
37
|
+
def test_authenticate_with_continuation
|
38
|
+
smtp = Net::SMTP.new('smtp.gmail.com', 587)
|
39
|
+
|
40
|
+
# Stub return from initial "AUTH XOAUTH2" request as well as the empty line sent to get final error
|
41
|
+
smtp.stubs(:send_xoauth2).returns(Net::SMTP::Response.parse('334 eyJzdGF0dXMiOiI0MDAiLCJzY2hlbWVzIjoiQmVhcmVyIiwic2NvcGUiOiJodHRwczovL21haWwuZ29vZ2xlLmNvbS8ifQ=='))
|
42
|
+
smtp.stubs(:get_final_status).returns(Net::SMTP::Response.parse('454 4.7.0 Too many login attempts, please try again later. j63sm3521185itj.19 - gsmtp'))
|
43
|
+
|
44
|
+
smtp.enable_starttls_auto
|
45
|
+
|
46
|
+
# Validate an error is still raised
|
47
|
+
ex = assert_raise(Net::SMTPAuthenticationError) do
|
48
|
+
smtp.start('gmail.com', 'roger@moore.com', 'a', :xoauth2)
|
49
|
+
end
|
50
|
+
|
51
|
+
# ...and that the 334 is not passed back to the caller.
|
52
|
+
assert_equal('454 4.7.0 Too many login attempts, please try again later. j63sm3521185itj.19 - gsmtp', ex.message)
|
53
|
+
ensure
|
54
|
+
smtp.finish if smtp&.started?
|
55
|
+
end
|
56
|
+
end
|
metadata
ADDED
@@ -0,0 +1,84 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: mail_xoauth2
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: '1.0'
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Fabian Jäger
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2018-06-07 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: mocha
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: shoulda
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
description: Get access to IMAP and STMP via OAuth2, using the standard Ruby Net libraries
|
42
|
+
email:
|
43
|
+
- fabian@mailbutler.io
|
44
|
+
executables: []
|
45
|
+
extensions: []
|
46
|
+
extra_rdoc_files: []
|
47
|
+
files:
|
48
|
+
- LICENSE
|
49
|
+
- README.markdown
|
50
|
+
- lib/mail_xoauth2.rb
|
51
|
+
- lib/mail_xoauth2/imap_xoauth2_authenticator.rb
|
52
|
+
- lib/mail_xoauth2/oauth2_string.rb
|
53
|
+
- lib/mail_xoauth2/smtp_xoauth2_authenticator.rb
|
54
|
+
- lib/mail_xoauth2/version.rb
|
55
|
+
- test/helper.rb
|
56
|
+
- test/test_imap_xoauth2_authenticator.rb
|
57
|
+
- test/test_oauth2_string.rb
|
58
|
+
- test/test_smtp_xoauth2_authenticator.rb
|
59
|
+
homepage: https://github.com/Mailbutler/mail_xoauth2
|
60
|
+
licenses:
|
61
|
+
- MIT
|
62
|
+
metadata: {}
|
63
|
+
post_install_message:
|
64
|
+
rdoc_options:
|
65
|
+
- "--charset=UTF-8"
|
66
|
+
require_paths:
|
67
|
+
- lib
|
68
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
69
|
+
requirements:
|
70
|
+
- - ">="
|
71
|
+
- !ruby/object:Gem::Version
|
72
|
+
version: '0'
|
73
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
74
|
+
requirements:
|
75
|
+
- - ">="
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: 1.3.6
|
78
|
+
requirements: []
|
79
|
+
rubyforge_project:
|
80
|
+
rubygems_version: 2.7.6
|
81
|
+
signing_key:
|
82
|
+
specification_version: 4
|
83
|
+
summary: Get access to IMAP and STMP via OAuth2, using the standard Ruby Net libraries
|
84
|
+
test_files: []
|