1 /* oauth.h -- OAuth 2.0 implementation for XOAUTH2 in SMTP and POP3.
3 * Google defined XOAUTH2 for SMTP, and that's what we use here. If other
4 * providers implement XOAUTH2 or some similar OAuth-based SMTP authentication
5 * protocol, it should be simple to extend this.
7 * OAuth https://tools.ietf.org/html/rfc6749
8 * SMTP https://developers.google.com/gmail/xoauth2_protocol
9 * POP3 http://googleappsdeveloper.blogspot.com/2014/10/updates-on-authentication-for-gmail.html
11 * Presumably [2] should document POP3 and that is an over-sight. As it stands,
12 * that blog post is the closest we have to documentation.
14 * According to [1] 2.1 Client Types, this is a "native application", a
17 * To summarize the flow:
19 * 1. User runs mhlogin which prints a URL the user must visit, and prompts for
20 * a code retrieved from that page.
22 * 2. User visits this URL in browser, signs in with some Google account, and
23 * copies and pastes the resulting code back to mhlogin.
25 * 3. mhlogin does HTTP POST to Google to exchange the user-provided code for a
26 * short-lived access token and a long-lived refresh token.
28 * 4. send uses the access token in SMTP auth if not expired. If it is expired,
29 * it does HTTP POST to Google including the refresh token and gets back a
30 * new access token (and possibly refresh token). If the refresh token has
31 * become invalid (e.g. if the user took some reset action on the Google
32 * account), the user must use mhlogin again, then re-run send.
36 /* error loading profile */
37 MH_OAUTH_BAD_PROFILE
= OK
+ 1,
39 /* error initializing libcurl */
42 /* local error initializing HTTP request */
43 MH_OAUTH_REQUEST_INIT
,
45 /* error executing HTTP POST request */
48 /* HTTP response body is too big. */
49 MH_OAUTH_RESPONSE_TOO_BIG
,
51 /* Can't process HTTP response body. */
52 MH_OAUTH_RESPONSE_BAD
,
54 /* The authorization server rejected the grant (authorization code or
55 * refresh token); possibly the user entered a bad code, or the refresh
56 * token has become invalid, etc. */
59 /* HTTP server indicates something is wrong with our request. */
62 /* Attempting to refresh an access token without a refresh token. */
66 /* requested user not in cred file */
67 MH_OAUTH_CRED_USER_NOT_FOUND
,
69 /* error loading serialized credentials */
73 typedef struct mh_oauth_ctx mh_oauth_ctx
;
75 typedef struct mh_oauth_cred mh_oauth_cred
;
77 typedef struct mh_oauth_service_info mh_oauth_service_info
;
79 struct mh_oauth_service_info
{
80 /* Name of service, so we can search static internal services array
81 * and for determining default credential file name. */
84 /* Human-readable name of the service; in mh_oauth_ctx::svc this is not
85 * another buffer to free, but a pointer to either static SERVICE data
86 * (below) or to the name field. */
89 /* [1] 2.2 Client Identifier, 2.3.1 Client Password */
91 /* [1] 2.3.1 Client Password */
93 /* [1] 3.1 Authorization Endpoint */
95 /* [1] 3.1.2 Redirection Endpoint */
97 /* [1] 3.2 Token Endpoint */
99 /* [1] 3.3 Access Token Scope */
104 * Do the complete dance for XOAUTH2 as used by POP3 and SMTP.
106 * Load tokens for svc from disk, refresh if necessary, and return the
107 * client response in client_response and client_response_len.
109 * If refreshing, writes freshened tokens to disk.
111 * Exits via adios on any error.
113 * Always returns OK for now, but in the future could return NOTOK on error.
117 mh_oauth_do_xoauth(const char *user
, const char *svc
, unsigned char **oauth_res
,
118 size_t *oauth_res_len
, FILE *log
);
121 * Allocate and initialize a new OAuth context.
123 * Caller must call mh_oauth_free(ctx) when finished, even on error.
125 * svc_name must point to a null-terminated string identifying the service
126 * provider. Support for "gmail" is built-in; anything else must be defined in
127 * the user's profile. The profile can also override "gmail" settings.
129 * Accesses global m_defs via context_find.
131 * On error, return false and set an error in ctx; ctx is always allocated.
134 mh_oauth_new(mh_oauth_ctx
**ctx
, const char *svc_name
);
137 * Free all resources associated with ctx.
140 mh_oauth_free(mh_oauth_ctx
*ctx
);
143 * Return null-terminated human-readable name of the service, e.g. "Gmail".
145 * Never returns NULL.
148 mh_oauth_svc_display_name(const mh_oauth_ctx
*ctx
) PURE
;
151 * Enable logging for subsequent operations on ctx.
153 * log must not be closed until after mh_oauth_free.
155 * For all HTTP requests, the request is logged with each line prefixed with
156 * "< ", and the response with "> ". Other messages are prefixed with "* ".
159 mh_oauth_log_to(FILE *log
, mh_oauth_ctx
*ctx
);
162 * Return the error code after some function indicated an error.
164 * Must not be called if an error was not indicated.
167 mh_oauth_get_err_code(const mh_oauth_ctx
*ctx
) PURE
;
170 * Return null-terminated error message after some function indicated an error.
172 * Never returns NULL, but must not be called if an error was not indicated.
175 mh_oauth_get_err_string(mh_oauth_ctx
*ctx
);
178 * Return the null-terminated URL the user needs to visit to authorize access.
180 * URL may be invalidated by subsequent calls to mh_oauth_get_authorize_url,
181 * mh_oauth_authorize, or mh_oauth_refresh.
183 * On error, return NULL.
186 mh_oauth_get_authorize_url(mh_oauth_ctx
*ctx
);
189 * Exchange code provided by the user for access (and maybe refresh) token.
191 * On error, return NULL.
194 mh_oauth_authorize(const char *code
, mh_oauth_ctx
*ctx
);
197 * Refresh access (and maybe refresh) token if refresh token present.
199 * On error, return false and leave cred untouched.
202 mh_oauth_refresh(mh_oauth_cred
*cred
);
205 * Return whether access token is present and not expired at time T.
208 mh_oauth_access_token_valid(time_t t
, const mh_oauth_cred
*cred
) PURE
;
211 * Free all resources associated with cred.
214 mh_oauth_cred_free(mh_oauth_cred
*cred
);
217 * Return the null-terminated file name for storing this service's OAuth tokens.
219 * Accesses global m_defs via context_find.
221 * Never returns NULL.
224 mh_oauth_cred_fn(const char *svc_name
);
227 * Serialize OAuth tokens to file.
229 * On error, return false.
232 mh_oauth_cred_save(FILE *fp
, mh_oauth_cred
*cred
, const char *user
);
235 * Load OAuth tokens from file.
237 * Calls m_getfld2(), which writes to stderr with advise().
239 * On error, return NULL.
242 mh_oauth_cred_load(FILE *fp
, mh_oauth_ctx
*ctx
, const char *user
);
245 * Return null-terminated SASL client response for XOAUTH2 from access token.
247 * Store the length in res_len.
249 * Must not be called except after successful mh_oauth_access_token_valid or
250 * mh_oauth_refresh call; i.e. must have a valid access token.
253 mh_oauth_sasl_client_response(size_t *res_len
,
254 const char *user
, const mh_oauth_cred
*cred
);
257 * Retrieve the various entries for the OAuth mechanism
261 mh_oauth_get_service_info(const char *svc_name
, mh_oauth_service_info
*svcinfo
,
262 char *errbuf
, size_t errbuflen
);