lib/net/imap/sasl/xoauth2_authenticator.rb in net-imap-0.4.1 vs lib/net/imap/sasl/xoauth2_authenticator.rb in net-imap-0.4.2

- old
+ new

@@ -4,66 +4,84 @@ # originally created for GMail and widely adopted by hosted email providers. # +XOAUTH2+ has been documented by # Google[https://developers.google.com/gmail/imap/xoauth2-protocol] and # Microsoft[https://learn.microsoft.com/en-us/exchange/client-developer/legacy-protocols/how-to-authenticate-an-imap-pop-smtp-application-by-using-oauth]. # -# This mechanism requires an OAuth2 +access_token+ which has been authorized -# with the appropriate OAuth2 scopes to access IMAP. These scopes are not -# standardized---consult each email service provider's documentation. +# This mechanism requires an OAuth2 access token which has been authorized +# with the appropriate OAuth2 scopes to access the user's services. Most of +# these scopes are not standardized---consult each service provider's +# documentation for their scopes. # # Although this mechanism was never standardized and has been obsoleted by # "+OAUTHBEARER+", it is still very widely supported. # # See Net::IMAP::SASL:: OAuthBearerAuthenticator. class Net::IMAP::SASL::XOAuth2Authenticator # It is unclear from {Google's original XOAUTH2 # documentation}[https://developers.google.com/gmail/imap/xoauth2-protocol], # whether "User" refers to the authentication identity (+authcid+) or the - # authorization identity (+authzid+). It appears to behave as +authzid+. + # authorization identity (+authzid+). The authentication identity is + # established for the client by the OAuth token, so it seems that +username+ + # must be the authorization identity. # # {Microsoft's documentation for shared # 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] - # clearly indicate that the Office 365 server interprets it as the + # _clearly_ indicates that the Office 365 server interprets it as the # authorization identity. + # + # Although they _should_ validate that the token has been authorized to access + # the service for +username+, _some_ servers appear to ignore this field, + # relying only the identity and scope authorized by the token. attr_reader :username + # Note that, unlike most other authenticators, #username is an alias for the + # authorization identity and not the authentication identity. The + # authenticated identity is established for the client by the #oauth2_token. + alias authzid username + # An OAuth2 access token which has been authorized with the appropriate OAuth2 # scopes to use the service for #username. attr_reader :oauth2_token + alias secret oauth2_token # :call-seq: # new(username, oauth2_token, **) -> authenticator # new(username:, oauth2_token:, **) -> authenticator + # new(authzid:, oauth2_token:, **) -> authenticator # # Creates an Authenticator for the "+XOAUTH2+" SASL mechanism, as specified by # Google[https://developers.google.com/gmail/imap/xoauth2-protocol], # Microsoft[https://learn.microsoft.com/en-us/exchange/client-developer/legacy-protocols/how-to-authenticate-an-imap-pop-smtp-application-by-using-oauth] # and Yahoo[https://senders.yahooinc.com/developer/documentation]. # # === Properties # # * #username --- the username for the account being accessed. + # + # #authzid --- an alias for #username. + # + # Note that, unlike some other authenticators, +username+ sets the + # _authorization_ identity and not the _authentication_ identity. The + # authenticated identity is established for the client with the OAuth token. + # # * #oauth2_token --- An OAuth2.0 access token which is authorized to access # the service for #username. # - # See the documentation for each attribute for more details. - def initialize(user = nil, token = nil, username: nil, oauth2_token: nil, **) - @username = username || user or - raise ArgumentError, "missing username" - @oauth2_token = oauth2_token || token or + # Any other keyword parameters are quietly ignored. + def initialize(user = nil, token = nil, username: nil, oauth2_token: nil, + authzid: nil, secret: nil, **) + @username = authzid || username || user or + raise ArgumentError, "missing username (authzid)" + @oauth2_token = oauth2_token || secret || token or raise ArgumentError, "missing oauth2_token" - [username, user].compact.count == 1 or - raise ArgumentError, "conflicting values for username" - [oauth2_token, token].compact.count == 1 or - raise ArgumentError, "conflicting values for oauth2_token" @done = false end # :call-seq: # initial_response? -> true # - # +PLAIN+ can send an initial client response. + # +XOAUTH2+ can send an initial client response. def initial_response?; true end # Returns the XOAUTH2 formatted response, which combines the +username+ # with the +oauth2_token+. def process(_data)