]> diplodocus.org Git - nmh/blob - h/oauth.h
Fix tests with oauth disabled.
[nmh] / h / oauth.h
1 /*
2 * Implementation of OAuth 2.0 [1] for XOAUTH2 in SMTP [2] and POP3 [3].
3 *
4 * Google defined XOAUTH2 for SMTP, and that's what we use here. If other
5 * providers implement XOAUTH2 or some similar OAuth-based SMTP authentication
6 * protocol, it should be simple to extend this.
7 *
8 * [1] https://tools.ietf.org/html/rfc6749
9 * [2] https://developers.google.com/gmail/xoauth2_protocol
10 * [3] http://googleappsdeveloper.blogspot.com/2014/10/updates-on-authentication-for-gmail.html
11 *
12 * Presumably [2] should document POP3 and that is an over-sight. As it stands,
13 * that blog post is the closest we have to documentation.
14 *
15 * According to [1] 2.1 Client Types, this is a "native application", a
16 * "public" client.
17 *
18 * To summarize the flow:
19 *
20 * 1. User runs mhlogin which prints a URL the user must visit, and prompts for
21 * a code retrieved from that page.
22 *
23 * 2. User vists this URL in browser, signs in with some Google account, and
24 * copies and pastes the resulting code back to mhlogin.
25 *
26 * 3. mhlogin does HTTP POST to Google to exchange the user-provided code for a
27 * short-lived access token and a long-lived refresh token.
28 *
29 * 4. send uses the access token in SMTP auth if not expired. If it is expired,
30 * it does HTTP POST to Google including the refresh token and gets back a
31 * new access token (and possibly refresh token). If the refresh token has
32 * become invalid (e.g. if the user took some reset action on the Google
33 * account), the user must use mhlogin again, then re-run send.
34 */
35
36 typedef enum {
37 /* error loading profile */
38 MH_OAUTH_BAD_PROFILE = OK + 1,
39
40 /* error initializing libcurl */
41 MH_OAUTH_CURL_INIT,
42
43 /* local error initializing HTTP request */
44 MH_OAUTH_REQUEST_INIT,
45
46 /* error executing HTTP POST request */
47 MH_OAUTH_POST,
48
49 /* HTTP response body is too big. */
50 MH_OAUTH_RESPONSE_TOO_BIG,
51
52 /* Can't process HTTP response body. */
53 MH_OAUTH_RESPONSE_BAD,
54
55 /* The authorization server rejected the grant (authorization code or
56 * refresh token); possibly the user entered a bad code, or the refresh
57 * token has become invalid, etc. */
58 MH_OAUTH_BAD_GRANT,
59
60 /* HTTP server indicates something is wrong with our request. */
61 MH_OAUTH_REQUEST_BAD,
62
63 /* Attempting to refresh an access token without a refresh token. */
64 MH_OAUTH_NO_REFRESH,
65
66
67 /* requested user not in cred file */
68 MH_OAUTH_CRED_USER_NOT_FOUND,
69
70 /* error loading serialized credentials */
71 MH_OAUTH_CRED_FILE
72 } mh_oauth_err_code;
73
74 typedef struct mh_oauth_ctx mh_oauth_ctx;
75
76 typedef struct mh_oauth_cred mh_oauth_cred;
77
78 /*
79 * Do the complete dance for XOAUTH2 as used by POP3 and SMTP.
80 *
81 * Load tokens for svc from disk, refresh if necessary, and return the
82 * base64-encoded client response.
83 *
84 * If refreshing, writes freshened tokens to disk.
85 *
86 * Exits via adios on any error.
87 */
88 char *
89 mh_oauth_do_xoauth(const char *user, const char *svc, FILE *log);
90
91 /*
92 * Allocate and initialize a new OAuth context.
93 *
94 * Caller must call mh_oauth_free(ctx) when finished, even on error.
95 *
96 * svc_name must point to a null-terminated string identifying the service
97 * provider. Support for "gmail" is built-in; anything else must be defined in
98 * the user's profile. The profile can also override "gmail" settings.
99 *
100 * Accesses global m_defs via context_find.
101 *
102 * On error, return FALSE and set an error in ctx; ctx is always allocated.
103 */
104 boolean
105 mh_oauth_new(mh_oauth_ctx **ctx, const char *svc_name);
106
107 /*
108 * Free all resources associated with ctx.
109 */
110 void
111 mh_oauth_free(mh_oauth_ctx *ctx);
112
113 /*
114 * Return null-terminated human-readable name of the service, e.g. "Gmail".
115 *
116 * Never returns NULL.
117 */
118 const char *
119 mh_oauth_svc_display_name(const mh_oauth_ctx *ctx);
120
121 /*
122 * Enable logging for subsequent operations on ctx.
123 *
124 * log must not be closed until after mh_oauth_free.
125 *
126 * For all HTTP requests, the request is logged with each line prefixed with
127 * "< ", and the response with "> ". Other messages are prefixed with "* ".
128 */
129 void
130 mh_oauth_log_to(FILE *log, mh_oauth_ctx *ctx);
131
132 /*
133 * Return the error code after some function indicated an error.
134 *
135 * Must not be called if an error was not indicated.
136 */
137 mh_oauth_err_code
138 mh_oauth_get_err_code(const mh_oauth_ctx *ctx);
139
140 /*
141 * Return null-terminated error message after some function indicated an error.
142 *
143 * Never returns NULL, but must not be called if an error was not indicated.
144 */
145 const char *
146 mh_oauth_get_err_string(mh_oauth_ctx *ctx);
147
148 /*
149 * Return the null-terminated URL the user needs to visit to authorize access.
150 *
151 * URL may be invalidated by subsequent calls to mh_oauth_get_authorize_url,
152 * mh_oauth_authorize, or mh_oauth_refresh.
153 *
154 * On error, return NULL.
155 */
156 const char *
157 mh_oauth_get_authorize_url(mh_oauth_ctx *ctx);
158
159 /*
160 * Exchange code provided by the user for access (and maybe refresh) token.
161 *
162 * On error, return NULL.
163 */
164 mh_oauth_cred *
165 mh_oauth_authorize(const char *code, mh_oauth_ctx *ctx);
166
167 /*
168 * Refresh access (and maybe refresh) token if refresh token present.
169 *
170 * On error, return FALSE and leave cred untouched.
171 */
172 boolean
173 mh_oauth_refresh(mh_oauth_cred *cred);
174
175 /*
176 * Return whether access token is present and not expired at time T.
177 */
178 boolean
179 mh_oauth_access_token_valid(time_t t, const mh_oauth_cred *cred);
180
181 /*
182 * Free all resources associated with cred.
183 */
184 void
185 mh_oauth_cred_free(mh_oauth_cred *cred);
186
187 /*
188 * Return the null-terminated file name for storing this service's OAuth tokens.
189 *
190 * Accesses global m_defs via context_find.
191 *
192 * Never returns NULL.
193 */
194 const char *
195 mh_oauth_cred_fn(mh_oauth_ctx *ctx);
196
197 /*
198 * Serialize OAuth tokens to file.
199 *
200 * On error, return FALSE.
201 */
202 boolean
203 mh_oauth_cred_save(FILE *fp, mh_oauth_cred *cred, const char *user);
204
205 /*
206 * Load OAuth tokens from file.
207 *
208 * Calls m_getfld(), which writes to stderr with advise().
209 *
210 * On error, return NULL.
211 */
212 mh_oauth_cred *
213 mh_oauth_cred_load(FILE *fp, mh_oauth_ctx *ctx, const char *user);
214
215 /*
216 * Return null-terminated SASL client response for XOAUTH2 from access token.
217 *
218 * Store the length in res_len.
219 *
220 * Must not be called except after successful mh_oauth_access_token_valid or
221 * mh_oauth_refresh call; i.e. must have a valid access token.
222 */
223 const char *
224 mh_oauth_sasl_client_response(size_t *res_len,
225 const char *user, const mh_oauth_cred *cred);