Skip to content

Commit 5343336

Browse files
committed
📚 Update docs for #authenticate, #login, etc...
The documentation on these methods is meant to complement a new SASL::Authenticator base class and new documentation on each of the individual authenticator classes. See #78 and #82. That base class is added in another PR (#78), but the documentation for these methods can be updated without it. Also, somehow I misremembered `LOGINDISABLED`: it only applies to `LOGIN`, not `AUTHENTICATE`! This fixes that error.
1 parent 200757e commit 5343336

File tree

2 files changed

+109
-52
lines changed

2 files changed

+109
-52
lines changed

lib/net/imap.rb

Lines changed: 79 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -602,49 +602,55 @@ 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]
606-
# to authenticate the client.
607-
#
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.
613+
# to authenticate the client. If successful, the connection enters the
614+
# "_authenticated_" state.
632615
#
633-
# For example:
616+
# +mechanism+ is the name of the \SASL authentication mechanism to be used.
617+
# All other arguments are forwarded to the authenticator for the requested
618+
# mechanism. The listed call signatures are suggestions. <em>The
619+
# documentation for each individual mechanism must be consulted for its
620+
# specific parameters.</em>
634621
#
635-
# imap.authenticate('PLAIN', user, password)
622+
# An exception Net::IMAP::NoResponseError is raised if authentication fails.
636623
#
637-
# A Net::IMAP::NoResponseError is raised if authentication fails.
624+
# Related: #login, #starttls
638625
#
639-
# See Net::IMAP::Authenticators for more information on plugging in your
640-
# own authenticator.
626+
# ==== Supported SASL Mechanisms
641627
#
642-
# Related: #login, #starttls
628+
# +PLAIN+:: See PlainAuthenticator.
629+
# Login using clear-text username and password.
643630
#
644-
# ==== Capabilities
631+
# +XOAUTH2+:: See XOauth2Authenticator.
632+
# Login using a username and OAuth2 access token.
633+
# Non-standard and obsoleted by +OAUTHBEARER+, but widely
634+
# supported.
635+
#
636+
# >>>
637+
# *Deprecated:* <em>Obsolete mechanisms are available for backwards
638+
# compatibility.</em>
645639
#
646-
# Clients MUST NOT attempt to #authenticate or #login when +LOGINDISABLED+
647-
# is listed with the capabilities.
640+
# For +DIGEST-MD5+ see DigestMD5Authenticator.
641+
#
642+
# For +LOGIN+, see LoginAuthenticator.
643+
#
644+
# For +CRAM-MD5+, see CramMD5Authenticator.
645+
#
646+
# <em>Using a deprecated mechanism will print a warning.</em>
647+
#
648+
# See Net::IMAP::Authenticators for information on plugging in
649+
# authenticators for other mechanisms. See the {SASL mechanism
650+
# registry}[https://www.iana.org/assignments/sasl-mechanisms/sasl-mechanisms.xhtml]
651+
# for information on these and other SASL mechanisms.
652+
#
653+
# ===== Capabilities
648654
#
649655
# Clients MUST NOT attempt to authenticate with a mechanism unless
650656
# <tt>"AUTH=#{mechanism}"</tt> for that mechanism is a server capability.
@@ -654,9 +660,37 @@ def starttls(options = {}, verify = true)
654660
# The TaggedResponse to #authenticate may include updated capabilities in
655661
# its ResponseCode.
656662
#
657-
def authenticate(auth_type, *args)
658-
authenticator = self.class.authenticator(auth_type, *args)
659-
send_command("AUTHENTICATE", auth_type) do |resp|
663+
# ===== Example
664+
# If the authenticators ignore unhandled keyword arguments, the same config
665+
# can be used for multiple mechanisms:
666+
#
667+
# password = nil # saved locally, so we don't ask more than once
668+
# accesstok = nil # saved locally...
669+
# creds = {
670+
# authcid: username,
671+
# password: proc { password ||= ui.prompt_for_password },
672+
# oauth2_token: proc { accesstok ||= kms.fresh_access_token },
673+
# }
674+
# capa = imap.capability
675+
# if capa.include? "AUTH=OAUTHBEARER"
676+
# imap.authenticate "OAUTHBEARER", **creds # authcid, oauth2_token
677+
# elsif capa.include? "AUTH=XOAUTH2"
678+
# imap.authenticate "XOAUTH2", **creds # authcid, oauth2_token
679+
# elsif capa.include? "AUTH=SCRAM-SHA-256"
680+
# imap.authenticate "SCRAM-SHA-256", **creds # authcid, password
681+
# elsif capa.include? "AUTH=PLAIN"
682+
# imap.authenticate "PLAIN", **creds # authcid, password
683+
# elsif capa.include? "AUTH=DIGEST-MD5"
684+
# imap.authenticate "DIGEST-MD5", **creds # authcid, password
685+
# elsif capa.include? "LOGINDISABLED"
686+
# raise "the server has disabled login"
687+
# else
688+
# imap.login username, password
689+
# end
690+
#
691+
def authenticate(mechanism, *args, **props, &cb)
692+
authenticator = self.class.authenticator(mechanism, *args, **props, &cb)
693+
send_command("AUTHENTICATE", mechanism) do |resp|
660694
if resp.instance_of?(ContinuationRequest)
661695
data = authenticator.process(resp.data.text.unpack("m")[0])
662696
s = [data].pack("m0")
@@ -668,16 +702,19 @@ def authenticate(auth_type, *args)
668702

669703
# Sends a {LOGIN command [IMAP4rev1 §6.2.3]}[https://www.rfc-editor.org/rfc/rfc3501#section-6.2.3]
670704
# to identify the client and carries the plaintext +password+ authenticating
671-
# this +user+. Note that, unlike calling #authenticate with an +auth_type+
672-
# of "LOGIN", #login does *not* use the LoginAuthenticator.
705+
# this +user+. If successful, the connection enters the "_authenticated_"
706+
# state.
707+
#
708+
# Using #authenticate is generally preferred over #login. The LOGIN command
709+
# is not the same as #authenticate with the "LOGIN" +mechanism+.
673710
#
674711
# A Net::IMAP::NoResponseError is raised if authentication fails.
675712
#
676713
# Related: #authenticate, #starttls
677714
#
678715
# ==== Capabilities
679-
# Clients MUST NOT attempt to #authenticate or #login when +LOGINDISABLED+
680-
# is listed with the capabilities.
716+
# Clients MUST NOT call #login if +LOGINDISABLED+ is listed with the
717+
# capabilities.
681718
#
682719
# Server capabilities may change after #starttls, #login, and #authenticate.
683720
# Cached capabilities _must_ be invalidated after this method completes.

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)