aboutsummaryrefslogtreecommitdiff
path: root/doc/chapters/basicauthentication.inc
diff options
context:
space:
mode:
Diffstat (limited to 'doc/chapters/basicauthentication.inc')
-rw-r--r--doc/chapters/basicauthentication.inc148
1 files changed, 49 insertions, 99 deletions
diff --git a/doc/chapters/basicauthentication.inc b/doc/chapters/basicauthentication.inc
index 2b68fc4c..8c7c241c 100644
--- a/doc/chapters/basicauthentication.inc
+++ b/doc/chapters/basicauthentication.inc
@@ -69,112 +69,62 @@ Let us assume we had only files not intended to be handed out without the correc
69so every "GET" request will be challenged. 69so every "GET" request will be challenged.
70@emph{RFC 2617} describes how the server shall ask for authentication by adding a 70@emph{RFC 2617} describes how the server shall ask for authentication by adding a
71@emph{WWW-Authenticate} response header with the name of the @emph{realm} protected. 71@emph{WWW-Authenticate} response header with the name of the @emph{realm} protected.
72 72MHD can generate and queue such a failure response for you using
73We let an extra function function do this. 73the @code{MHD_queue_basic_auth_fail_response} API. The only thing you need to do
74is construct a response with the error page to be shown to the user
75if he aborts basic authentication. But first, you should check if the
76proper credentials were already supplied using the
77@code{MHD_basic_auth_get_username_password} call.
78
79Your code would then look like this:
74@verbatim 80@verbatim
75static int 81static int
76ask_for_authentication (struct MHD_Connection *connection, const char *realm) 82answer_to_connection (void *cls, struct MHD_Connection *connection,
83 const char *url, const char *method,
84 const char *version, const char *upload_data,
85 size_t *upload_data_size, void **con_cls)
77{ 86{
78 int ret; 87 char *user;
88 char *pass;
89 int fail;
79 struct MHD_Response *response; 90 struct MHD_Response *response;
80 char *headervalue; 91
81 const char *strbase = "Basic realm="; 92 if (0 != strcmp (method, MHD_HTTP_METHOD_GET))
82 93 return MHD_NO;
83 response = MHD_create_response_from_data (0, NULL, MHD_NO, MHD_NO); 94 if (NULL == *con_cls)
84 if (!response) return MHD_NO; 95 {
85 96 *con_cls = connection;
86 headervalue = malloc (strlen (strbase) + strlen (realm) + 1); 97 return MHD_YES;
87 if (!headervalue) return MHD_NO; 98 }
88 99 pass = NULL;
89 strcpy (headervalue, strbase); 100 user = MHD_basic_auth_get_username_password (connection, &pass);
90 strcat (headervalue, realm); 101 fail = ( (user == NULL) ||
91 102 (0 != strcmp (user, "root")) ||
92 ret = MHD_add_response_header (response, "WWW-Authenticate", headervalue); 103 (0 != strcmp (pass, "pa$$w0rd") ) );
93 free (headervalue); 104 if (user != NULL) free (user);
94 if (!ret) {MHD_destroy_response (response); return MHD_NO;} 105 if (pass != NULL) free (pass);
95 106 if (fail)
96 ret = MHD_queue_response (connection, MHD_HTTP_UNAUTHORIZED, response); 107 {
108 const char *page = "<html><body>Go away.</body></html>";
109 response =
110 MHD_create_response_from_data (strlen (page), (void *) page, MHD_NO,
111 MHD_NO);
112 ret = MHD_queue_basic_auth_fail_response (connection,
113 "my realm",
114 response);
115 }
116 else
117 {
118 const char *page = "<html><body>A secret.</body></html>";
119 response =
120 MHD_create_response_from_data (strlen (page), (void *) page, MHD_NO,
121 MHD_NO);
122 ret = MHD_queue_response (connection, MHD_HTTP_OK, response);
123 }
97 MHD_destroy_response (response); 124 MHD_destroy_response (response);
98 return ret; 125 return ret;
99} 126}
100@end verbatim 127@end verbatim
101@noindent
102
103@code{#define} the realm name according to your own taste, e.g. "Maintenance" or "Area51" but
104it will need to have extra quotes.
105
106Since the client may send the authentication right away, it would be wrong to ask for
107it without checking the request's header--where the authentication is expected to be found.
108
109@heading Authentication in detail
110Checking @emph{RFC 2617} again, we find that the client will pack the username and password, by
111whatever means he might have obtained them, in a line separated by a colon---and then encodes
112them to @emph{Base64}. The actual implementation of this encoding are not within the scope of
113this tutorial although a working function is included in the complete source file of the example.
114
115An unencoded word describing the authentication method (here "Basic") will precede the code
116and the resulting line is the value of a request header of the type "Authorization".
117
118This header line thus is of interest to the function checking a connection for a given username/password:
119@verbatim
120static int
121is_authenticated (struct MHD_Connection *connection,
122 const char *username, const char *password)
123{
124 const char *headervalue;
125 ...
126
127 headervalue = MHD_lookup_connection_value (connection, MHD_HEADER_KIND,
128 "Authorization");
129 if (NULL == headervalue) return 0;
130@end verbatim
131@noindent
132
133where, firstly, the authentication method will be checked.
134@verbatim
135const char *strbase = "Basic ";
136...
137if (0 != strncmp (headervalue, strbase, strlen (strbase))) return 0;
138@end verbatim
139@noindent
140
141Of course, we could decode the passed credentials in the next step and compare them directly to
142the given strings. But as this would involve string parsing, which is more complicated then string
143composing, it is done the other way around---the clear text credentials will be encoded to @emph{Base64}
144and then compared against the headerline. The authentication method string will be left out here as
145it has been checked already at this point.
146@verbatim
147 char *expected_b64, *expected;
148 int authenticated;
149
150 ...
151 strcpy (expected, username);
152 strcat (expected, ":");
153 strcat (expected, password);
154
155 expected_b64 = string_to_base64 (expected);
156 if (NULL == expected_b64) return 0;
157
158 strcpy (expected, strbase);
159 authenticated = (strcmp (headervalue + strlen (strbase), expected_b64) == 0);
160
161 free (expected_b64);
162
163 return authenticated;
164}
165@end verbatim
166@noindent
167
168These two functions---together with a response function in case of positive authentication doing little
169new---allow the rest of the callback function to be rather short.
170@verbatim
171 if (!is_authenticated (connection, USER, PASSWORD))
172 return ask_for_authentication (connection, REALM);
173
174 return secret_page (connection);
175}
176@end verbatim
177@noindent
178 128
179See the @code{examples} directory for the complete example file. 129See the @code{examples} directory for the complete example file.
180 130