1 /**
2 	Implements HTTP Basic Auth.
3 
4 	Copyright: © 2012 Sönke Ludwig
5 	License: Subject to the terms of the MIT license, as written in the included LICENSE.txt file.
6 	Authors: Sönke Ludwig
7 */
8 module vibe.http.auth.basic_auth;
9 
10 import vibe.http.server;
11 import vibe.core.log;
12 
13 import std.base64;
14 import std.exception;
15 import std.string;
16 
17 @safe:
18 
19 
20 /**
21 	Returns a request handler that enforces request to be authenticated using HTTP Basic Auth.
22 */
23 HTTPServerRequestDelegateS performBasicAuth(string realm, PasswordVerifyCallback pwcheck)
24 {
25 	void handleRequest(scope HTTPServerRequest req, scope HTTPServerResponse res)
26 	@safe {
27 		if (!checkBasicAuth(req, pwcheck)) {
28 			res.statusCode = HTTPStatus.unauthorized;
29 			res.contentType = "text/plain";
30 			res.headers["WWW-Authenticate"] = "Basic realm=\""~realm~"\"";
31 			res.bodyWriter.write("Authorization required");
32 		}
33 	}
34 	return &handleRequest;
35 }
36 /// Scheduled for deprecation - use a `@safe` callback instead.
37 HTTPServerRequestDelegateS performBasicAuth(string realm, bool delegate(string, string) @system pwcheck)
38 @system {
39 	return performBasicAuth(realm, (u, p) @trusted => pwcheck(u, p));
40 }
41 
42 
43 /**
44 	Enforces HTTP Basic Auth authentication on the given req/res pair.
45 
46 	Params:
47 		req = Request object that is to be checked
48 		res = Response object that will be used for authentication errors
49 		realm = HTTP Basic Auth realm reported to the client
50 		pwcheck = A delegate queried for validating user/password pairs
51 
52 	Returns: Returns the name of the authenticated user.
53 
54 	Throws: Throws a HTTPStatusExeption in case of an authentication failure.
55 */
56 string performBasicAuth(scope HTTPServerRequest req, scope HTTPServerResponse res, string realm, scope PasswordVerifyCallback pwcheck)
57 {
58 	if (checkBasicAuth(req, pwcheck))
59 		return req.username;
60 
61 	res.headers["WWW-Authenticate"] = "Basic realm=\""~realm~"\"";
62 	throw new HTTPStatusException(HTTPStatus.unauthorized);
63 }
64 /// Scheduled for deprecation - use a `@safe` callback instead.
65 string performBasicAuth(scope HTTPServerRequest req, scope HTTPServerResponse res, string realm, scope bool delegate(string, string) @system pwcheck)
66 @system {
67 	return performBasicAuth(req, res, realm, (u, p) @trusted => pwcheck(u, p));
68 }
69 
70 
71 /**
72 	Checks for valid HTTP Basic Auth authentication on the given request.
73 
74 	Upon successful authorization, the name of the authorized user will
75 	be stored in `req.username`.
76 
77 	Params:
78 		req = Request object that is to be checked
79 		pwcheck = A delegate queried for validating user/password pairs
80 
81 	Returns: Returns `true` $(I iff) a valid Basic Auth header is present
82 		and the credentials were verified successfully by the validation
83 		callback.
84 
85 	Throws: Throws a `HTTPStatusExeption` with `HTTPStatusCode.badRequest`
86 		if the "Authorization" header is malformed.
87 */
88 bool checkBasicAuth(scope HTTPServerRequest req, scope PasswordVerifyCallback pwcheck)
89 {
90 	auto pauth = "Authorization" in req.headers;
91 	if (pauth && (*pauth).startsWith("Basic ")) {
92 		string user_pw = () @trusted { return cast(string)Base64.decode((*pauth)[6 .. $]); } ();
93 
94 		auto idx = user_pw.indexOf(":");
95 		enforceBadRequest(idx >= 0, "Invalid auth string format!");
96 		string user = user_pw[0 .. idx];
97 		string password = user_pw[idx+1 .. $];
98 
99 		if (pwcheck(user, password)) {
100 			req.username = user;
101 			return true;
102 		}
103 	}
104 
105 	return false;
106 }
107 
108 static import vibe.http.internal.basic_auth_client;
109 
110 alias addBasicAuth = vibe.http.internal.basic_auth_client.addBasicAuth;
111 
112 alias PasswordVerifyCallback = bool delegate(string user, string password);