Skip to content

Commit 679dcef

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 679dcef

File tree

2 files changed

+101
-43
lines changed

2 files changed

+101
-43
lines changed

lib/net/imap.rb

Lines changed: 71 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -602,46 +602,57 @@ 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:
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>
611620
#
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.
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.
632625
#
633-
# For example:
626+
# An exception Net::IMAP::NoResponseError is raised if authentication fails.
634627
#
635-
# imap.authenticate('PLAIN', user, password)
628+
# Related: #login, #starttls
636629
#
637-
# A Net::IMAP::NoResponseError is raised if authentication fails.
630+
# ==== Supported SASL Mechanisms
638631
#
639-
# See Net::IMAP::Authenticators for more information on plugging in your
640-
# own authenticator.
632+
# Net::IMAP currently supports the following mechanisms:
641633
#
642-
# Related: #login, #starttls
634+
# PLAIN:: Login using clear-text user and password. Secure with TLS.
635+
# See PlainAuthenticator.
636+
# XOAUTH2:: Login using a username and OAuth2 access token. Non-standard
637+
# and obsoleted by +OAUTHBEARER+, but still widely supported.
638+
# See XOauth2Authenticator.
643639
#
644-
# ==== Capabilities
640+
# See Net::IMAP::Authenticators for information on plugging in
641+
# authenticators for other mechanisms. See the {SASL mechanism
642+
# registry}[https://www.iana.org/assignments/sasl-mechanisms/sasl-mechanisms.xhtml]
643+
# for information on these and other SASL mechanisms.
644+
#
645+
# ===== Deprecated mechanisms
646+
#
647+
# <em>Obsolete mechanisms are available for backwards compatibility.
648+
# Using a deprecated mechanism will print a warning.</em>
649+
#
650+
# DIGEST-MD5:: DEPRECATED by RFC6331. Must be secured using TLS.
651+
# See DigestMD5Authenticator.
652+
# CRAM-MD5:: DEPRECATED: Use +PLAIN+ (or SCRAM-*)
653+
# LOGIN:: DEPRECATED: Use +PLAIN+ with TLS.
654+
#
655+
# ===== Capabilities
645656
#
646657
# Clients MUST NOT attempt to #authenticate or #login when +LOGINDISABLED+
647658
# is listed with the capabilities.
@@ -654,9 +665,36 @@ def starttls(options = {}, verify = true)
654665
# The TaggedResponse to #authenticate may include updated capabilities in
655666
# its ResponseCode.
656667
#
657-
def authenticate(auth_type, *args)
658-
authenticator = self.class.authenticator(auth_type, *args)
659-
send_command("AUTHENTICATE", auth_type) do |resp|
668+
# ===== Example
669+
# Most mechanisms ignore unhandled keyword arguments, so the same config can
670+
# be used for multiple authenticator types:
671+
# password = nil # saved locally, so we don't ask more than once
672+
# accesstok = nil
673+
# creds = {
674+
# authcid: username,
675+
# password: proc { password ||= ui.prompt_for_password },
676+
# oauth2_token: proc { accesstok ||= kms.lookup(username, :access_token).refreshed },
677+
# }
678+
# capa = imap.capability
679+
# if capa.include? "LOGINDISABLED"
680+
# raise "the server has disabled login"
681+
# elsif capa.include? "AUTH=OAUTHBEARER"
682+
# imap.authenticate "OAUTHBEARER", **creds # authcid, oauth2_token
683+
# elsif capa.include? "AUTH=XOAUTH2"
684+
# imap.authenticate "XOAUTH2", **creds # authcid, oauth2_token
685+
# elsif capa.include? "AUTH=SCRAM-SHA-256"
686+
# imap.authenticate "SCRAM-SHA-256", **creds # authcid, password
687+
# elsif capa.include? "AUTH=PLAIN"
688+
# imap.authenticate "PLAIN", **creds # authcid, password
689+
# elsif capa.include? "AUTH=DIGEST-MD5"
690+
# imap.authenticate "DIGEST-MD5", **creds # authcid, password
691+
# else
692+
# raise "no acceptable authentication mechanism is available"
693+
# end
694+
#
695+
def authenticate(mechanism, *args, **props, &cb)
696+
authenticator = self.class.authenticator(mechanism, *args, **props, &cb)
697+
send_command("AUTHENTICATE", mechanism) do |resp|
660698
if resp.instance_of?(ContinuationRequest)
661699
data = authenticator.process(resp.data.text.unpack("m")[0])
662700
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)