Skip to content

Commit 49e5b0a

Browse files
committed
📚 Update SASL XOAUTH2 docs and add attr_readers
This also renames `@user` to `@username`, to match other authenticators. However, the spec is unclearly worded. It arguably _should_ be renamed to (or aliased as) `authzid`. 🤷
1 parent df121ad commit 49e5b0a

File tree

2 files changed

+57
-6
lines changed

2 files changed

+57
-6
lines changed
Lines changed: 56 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,73 @@
11
# frozen_string_literal: true
22

3+
# Authenticator for the "+XOAUTH2+" SASL mechanism. This mechanism was
4+
# originally created for GMail and widely adopted by hosted email providers.
5+
# +XOAUTH2+ has been documented by
6+
# Google[https://developers.google.com/gmail/imap/xoauth2-protocol] and
7+
# Microsoft[https://learn.microsoft.com/en-us/exchange/client-developer/legacy-protocols/how-to-authenticate-an-imap-pop-smtp-application-by-using-oauth].
8+
#
9+
# This mechanism requires an OAuth2 +access_token+ which has been authorized
10+
# with the appropriate OAuth2 scopes to access IMAP. These scopes are not
11+
# standardized---consult each email service provider's documentation.
12+
#
13+
# Although this mechanism was never standardized and has been obsoleted by
14+
# "+OAUTHBEARER+", it is still very widely supported.
15+
#
16+
# See Net::IMAP::SASL:: OAuthBearerAuthenticator.
317
class Net::IMAP::SASL::XOAuth2Authenticator
418

5-
def initialize(user, oauth2_token)
6-
@user = user
19+
# It is unclear from {Google's original XOAUTH2
20+
# documentation}[https://developers.google.com/gmail/imap/xoauth2-protocol],
21+
# whether "User" refers to the authentication identity (+authcid+) or the
22+
# authorization identity (+authzid+). It appears to behave as +authzid+.
23+
#
24+
# {Microsoft's documentation for shared
25+
# mailboxes}[https://learn.microsoft.com/en-us/exchange/client-developer/legacy-protocols/how-to-authenticate-an-imap-pop-smtp-application-by-using-oauth#sasl-xoauth2-authentication-for-shared-mailboxes-in-office-365]
26+
# clearly indicate that the Office 365 server interprets it as the
27+
# authorization identity.
28+
attr_reader :username
29+
30+
# An OAuth2 access token which has been authorized with the appropriate OAuth2
31+
# scopes to use the service for #username.
32+
attr_reader :oauth2_token
33+
34+
# :call-seq:
35+
# :call-seq:
36+
# new(username, oauth2_token) -> authenticator
37+
#
38+
# Creates an Authenticator for the "+XOAUTH2+" SASL mechanism, as specified by
39+
# Google[https://developers.google.com/gmail/imap/xoauth2-protocol],
40+
# Microsoft[https://learn.microsoft.com/en-us/exchange/client-developer/legacy-protocols/how-to-authenticate-an-imap-pop-smtp-application-by-using-oauth]
41+
# and Yahoo[https://senders.yahooinc.com/developer/documentation].
42+
#
43+
# === Properties
44+
#
45+
# * #username --- the username for the account being accessed.
46+
# * #oauth2_token --- An OAuth2.0 access token which is authorized to access
47+
# the service for #username.
48+
#
49+
# See the documentation for each attribute for more details.
50+
def initialize(username, oauth2_token)
51+
@username = username
752
@oauth2_token = oauth2_token
853
end
954

55+
# :call-seq:
56+
# initial_response? -> true
57+
#
58+
# +PLAIN+ can send an initial client response.
1059
def initial_response?; true end
1160

61+
# Returns the XOAUTH2 formatted response, which combines the +username+
62+
# with the +oauth2_token+.
1263
def process(_data)
13-
build_oauth2_string(@user, @oauth2_token)
64+
build_oauth2_string(@username, @oauth2_token)
1465
end
1566

1667
private
1768

18-
def build_oauth2_string(user, oauth2_token)
19-
format("user=%s\1auth=Bearer %s\1\1", user, oauth2_token)
69+
def build_oauth2_string(username, oauth2_token)
70+
format("user=%s\1auth=Bearer %s\1\1", username, oauth2_token)
2071
end
2172

2273
end

test/net/imap/test_imap_authenticators.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ def test_xoauth2_authenticator_matches_mechanism
8888
assert_kind_of(Net::IMAP::SASL::XOAuth2Authenticator, xoauth2("user", "tok"))
8989
end
9090

91-
def test_xoauth2
91+
def test_xoauth2_response
9292
assert_equal(
9393
"user=username\1auth=Bearer token\1\1",
9494
xoauth2("username", "token").process(nil)

0 commit comments

Comments
 (0)