Skip to content

Commit 1e6b243

Browse files
committed
📚 Update #authenticate and #authenticator docs
These two pieces of documentation are meant to complement a new SASL::Authenticator base class and new documentation on each of the individual authenticator classes. That base class is added in another PR, but the documentation for these can be updated without it.
1 parent 200757e commit 1e6b243

File tree

2 files changed

+104
-44
lines changed

2 files changed

+104
-44
lines changed

lib/net/imap.rb

Lines changed: 74 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -602,46 +602,59 @@ def starttls(options = {}, verify = true)
602602
end
603603
end
604604

605+
# :call-seq:
606+
# authenticate(mechanism, ...) -> ok_resp
607+
# authenticate(mech, *creds, **props) {|prop, auth| val } -> ok_resp
608+
# authenticate(mechanism, authnid, credentials, authzid=nil) -> ok_resp
609+
# authenticate(mechanism, **properties) -> ok_resp
610+
# authenticate(mechanism) {|propname, authctx| prop_value } -> ok_resp
611+
#
605612
# Sends an {AUTHENTICATE command [IMAP4rev1 §6.2.2]}[https://www.rfc-editor.org/rfc/rfc3501#section-6.2.2]
606613
# to authenticate the client.
607614
#
608-
# The +auth_type+ parameter is a string that
609-
# represents the authentication mechanism to be used. Currently Net::IMAP
610-
# supports the following mechanisms:
611-
#
612-
# PLAIN:: Login using cleartext user and password. Secure with TLS.
613-
# See PlainAuthenticator.
614-
# CRAM-MD5:: DEPRECATED: Use PLAIN (or DIGEST-MD5) with TLS.
615-
# DIGEST-MD5:: DEPRECATED by RFC6331. Must be secured using TLS.
616-
# See DigestMD5Authenticator.
617-
# LOGIN:: DEPRECATED: Use PLAIN.
618-
#
619-
# Most mechanisms require two args: authentication identity (e.g. username)
620-
# and credentials (e.g. a password). But each mechanism requires and allows
621-
# different arguments; please consult the documentation for the specific
622-
# mechanisms you are using. <em>Several obsolete mechanisms are available
623-
# for backwards compatibility. Using deprecated mechanisms will issue
624-
# warnings.</em>
625-
#
626-
# Servers do not support all mechanisms and clients must not attempt to use
627-
# a mechanism unless "AUTH=#{mechanism}" is listed as a #capability.
628-
# Clients must not attempt to authenticate or #login when +LOGINDISABLED+ is
629-
# listed with the capabilities. Server capabilities, especially auth
630-
# mechanisms, do change after calling #starttls so they need to be checked
631-
# again.
615+
# +mechanism+ is the name of the \SASL authentication mechanism to be used.
616+
# All other arguments are forwarded to the authenticator for the requested
617+
# mechanism. The listed call signatures are suggestions. <em>The
618+
# documentation for each individual mechanism must be consulted for its
619+
# specific parameters.</em>
632620
#
633-
# For example:
621+
# <em>In general</em>, all of a mechanism's properties can be set by keyword
622+
# argument or callback, but mechanisms may allow common properties to be set
623+
# with positional arguments. See SASL::Authenticator@Properties and
624+
# SASL::Authenticator@Callbacks for more details.
634625
#
635-
# imap.authenticate('PLAIN', user, password)
626+
# An exception Net::IMAP::NoResponseError is raised if authentication fails.
636627
#
637-
# A Net::IMAP::NoResponseError is raised if authentication fails.
628+
# Related: #login, #starttls
638629
#
639-
# See Net::IMAP::Authenticators for more information on plugging in your
640-
# own authenticator.
630+
# ==== Supported SASL Mechanisms
641631
#
642-
# Related: #login, #starttls
632+
# +PLAIN+:: See PlainAuthenticator.
633+
# Login using clear-text username and password.
643634
#
644-
# ==== Capabilities
635+
# +XOAUTH2+:: See XOauth2Authenticator.
636+
# Login using a username and OAuth2 access token.
637+
# Non-standard and obsoleted by +OAUTHBEARER+, but widely
638+
# supported.
639+
#
640+
# >>>
641+
# *Deprecated:* <em>Obsolete mechanisms are available for backwards
642+
# compatibility.</em>
643+
#
644+
# For +DIGEST-MD5+ see DigestMD5Authenticator.
645+
#
646+
# For +LOGIN+, see LoginAuthenticator.
647+
#
648+
# For +CRAM-MD5+, see CramMD5Authenticator.
649+
#
650+
# <em>Using a deprecated mechanism will print a warning.</em>
651+
#
652+
# See Net::IMAP::Authenticators for information on plugging in
653+
# authenticators for other mechanisms. See the {SASL mechanism
654+
# registry}[https://www.iana.org/assignments/sasl-mechanisms/sasl-mechanisms.xhtml]
655+
# for information on these and other SASL mechanisms.
656+
#
657+
# ===== Capabilities
645658
#
646659
# Clients MUST NOT attempt to #authenticate or #login when +LOGINDISABLED+
647660
# is listed with the capabilities.
@@ -654,9 +667,36 @@ def starttls(options = {}, verify = true)
654667
# The TaggedResponse to #authenticate may include updated capabilities in
655668
# its ResponseCode.
656669
#
657-
def authenticate(auth_type, *args)
658-
authenticator = self.class.authenticator(auth_type, *args)
659-
send_command("AUTHENTICATE", auth_type) do |resp|
670+
# ===== Example
671+
# Most mechanisms ignore unhandled keyword arguments, so the same config can
672+
# be used for multiple authenticator types:
673+
# password = nil # saved locally, so we don't ask more than once
674+
# accesstok = nil
675+
# creds = {
676+
# authcid: username,
677+
# password: proc { password ||= ui.prompt_for_password },
678+
# oauth2_token: proc { accesstok ||= kms.lookup(username, :access_token).refreshed },
679+
# }
680+
# capa = imap.capability
681+
# if capa.include? "LOGINDISABLED"
682+
# raise "the server has disabled login"
683+
# elsif capa.include? "AUTH=OAUTHBEARER"
684+
# imap.authenticate "OAUTHBEARER", **creds # authcid, oauth2_token
685+
# elsif capa.include? "AUTH=XOAUTH2"
686+
# imap.authenticate "XOAUTH2", **creds # authcid, oauth2_token
687+
# elsif capa.include? "AUTH=SCRAM-SHA-256"
688+
# imap.authenticate "SCRAM-SHA-256", **creds # authcid, password
689+
# elsif capa.include? "AUTH=PLAIN"
690+
# imap.authenticate "PLAIN", **creds # authcid, password
691+
# elsif capa.include? "AUTH=DIGEST-MD5"
692+
# imap.authenticate "DIGEST-MD5", **creds # authcid, password
693+
# else
694+
# raise "no acceptable authentication mechanism is available"
695+
# end
696+
#
697+
def authenticate(mechanism, *args, **props, &cb)
698+
authenticator = self.class.authenticator(mechanism, *args, **props, &cb)
699+
send_command("AUTHENTICATE", mechanism) do |resp|
660700
if resp.instance_of?(ContinuationRequest)
661701
data = authenticator.process(resp.data.text.unpack("m")[0])
662702
s = [data].pack("m0")

lib/net/imap/authenticators.rb

Lines changed: 30 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,22 +3,42 @@
33
# Registry for SASL authenticators used by Net::IMAP.
44
module Net::IMAP::Authenticators
55

6-
# Adds an authenticator for use with Net::IMAP#authenticate. +auth_type+ is the
6+
# Adds an authenticator for Net::IMAP#authenticate to use. +mechanism+ is the
77
# {SASL mechanism}[https://www.iana.org/assignments/sasl-mechanisms/sasl-mechanisms.xhtml]
8-
# supported by +authenticator+ (for instance, "+PLAIN+"). The +authenticator+
9-
# is an object which defines a +#process+ method to handle authentication with
10-
# the server. See Net::IMAP::PlainAuthenticator, Net::IMAP::LoginAuthenticator,
11-
# Net::IMAP::CramMD5Authenticator, and Net::IMAP::DigestMD5Authenticator for
12-
# examples.
8+
# implemented by +authenticator+ (for instance, <tt>"PLAIN"</tt>).
9+
#
10+
# The +authenticator+ must respond to +#new+ (or #call), receiving the
11+
# authenticator configuration and return a configured authentication session.
12+
# The authenticator session must respond to +#process+, receiving the server's
13+
# challenge and returning the client's response.
1314
#
14-
# If +auth_type+ refers to an existing authenticator, it will be
15-
# replaced by the new one.
15+
# See PlainAuthenticator, XOauth2Authenticator, and DigestMD5Authenticator for
16+
# examples.
1617
def add_authenticator(auth_type, authenticator)
1718
authenticators[auth_type] = authenticator
1819
end
1920

20-
# Builds an authenticator for Net::IMAP#authenticate. +args+ will be passed
21-
# directly to the chosen authenticator's +#initialize+.
21+
# :call-seq:
22+
# authenticator(mechanism, ...) -> authenticator
23+
# authenticator(mech, *creds, **props) {|prop, auth| val } -> authenticator
24+
# authenticator(mechanism, authnid, creds, authzid=nil) -> authenticator
25+
# authenticator(mechanism, **properties) -> authenticator
26+
# authenticator(mechanism) {|propname, authctx| value } -> authenticator
27+
#
28+
# Builds a new authentication session context for +mechanism+.
29+
#
30+
# [Note]
31+
# This method is intended for internal use by connection protocol code only.
32+
# Protocol client users should see refer to their client's documentation,
33+
# e.g. Net::IMAP#authenticate for Net::IMAP.
34+
#
35+
# The call signatures documented for this method are recommendations for
36+
# authenticator implementors. All arguments (other than +mechanism+) are
37+
# forwarded to the registered authenticator's +#new+ (or +#call+) method, and
38+
# each authenticator must document its own arguments.
39+
#
40+
# The returned object represents a single authentication exchange and <em>must
41+
# not</em> be reused for multiple authentication attempts.
2242
def authenticator(mechanism, *authargs, **properties, &callback)
2343
authenticator = authenticators.fetch(mechanism.upcase) do
2444
raise ArgumentError, 'unknown auth type - "%s"' % mechanism

0 commit comments

Comments
 (0)