1 module vibeauth.authenticators.BasicAuth; 2 3 import vibe.http.router; 4 import vibe.data.json; 5 6 import std.algorithm.searching, std.base64, std..string, std.stdio; 7 import vibeauth.authenticators.BaseAuth; 8 9 /// Basic auth credential pair 10 struct BasicAuthCredentials { 11 /// 12 string username; 13 14 /// 15 string password; 16 } 17 18 /// Basic auth handler RFC 7617. It's not safe to use it without https. 19 class BasicAuth(string realm) : BaseAuth { 20 21 /// Instantiate the authenticator with an user collection 22 this(UserCollection collection) { 23 super(collection); 24 } 25 26 override { 27 /// Auth handler that will fail if a successfull auth was not performed. 28 /// This handler is usefull for routes that want to hide information to the 29 /// public. 30 void mandatoryAuth(HTTPServerRequest req, HTTPServerResponse res) { 31 super.mandatoryAuth(req, res); 32 } 33 34 /// ditto 35 AuthResult mandatoryAuth(HTTPServerRequest req) { 36 auto pauth = "Authorization" in req.headers; 37 38 if(pauth && (*pauth).startsWith("Basic ")) { 39 auto auth = parseBasicAuth((*pauth)[6 .. $]); 40 41 if(collection.contains(auth.username) && collection[auth.username].isValidPassword(auth.password)) { 42 req.username = auth.username; 43 return AuthResult.success; 44 } 45 } 46 47 return AuthResult.unauthorized; 48 } 49 50 /// Auth handler that fails only if the auth fields are present and are not valid. 51 /// This handler is usefull when a route should return different data when the user is 52 /// logged in 53 void permisiveAuth(HTTPServerRequest req, HTTPServerResponse res) { 54 super.permisiveAuth(req, res); 55 } 56 57 /// ditto 58 AuthResult permisiveAuth(HTTPServerRequest req) { 59 auto pauth = "Authorization" in req.headers; 60 61 if(pauth && (*pauth).startsWith("Basic ")) { 62 auto auth = parseBasicAuth((*pauth)[6 .. $]); 63 64 if(collection.contains(auth.username) && collection[auth.username].isValidPassword(auth.password)) { 65 req.username = auth.username; 66 return AuthResult.success; 67 } 68 } 69 70 return AuthResult.unauthorized; 71 } 72 73 /// 74 void respondUnauthorized(HTTPServerResponse res) { 75 res.statusCode = HTTPStatus.unauthorized; 76 res.contentType = "text/plain"; 77 res.headers["WWW-Authenticate"] = "Basic realm=\""~realm~"\""; 78 res.bodyWriter.write("Authorization required"); 79 } 80 81 /// 82 void respondInvalidToken(HTTPServerResponse res) { 83 respondUnauthorized(res); 84 } 85 } 86 87 private { 88 /// Parse user input 89 BasicAuthCredentials parseBasicAuth(string data) { 90 string decodedData = cast(string) Base64.decode(data); 91 auto idx = decodedData.indexOf(":"); 92 enforceBadRequest(idx >= 0, "Invalid auth string format!"); 93 94 return BasicAuthCredentials(decodedData[0 .. idx], decodedData[idx+1 .. $]); 95 } 96 } 97 }