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);