import 'dart:async'; import 'dart:convert'; import 'package:angel_framework/angel_framework.dart'; import '../options.dart'; import '../strategy.dart'; bool _validateString(String str) => str != null && str.isNotEmpty; /// Determines the validity of an incoming username and password. typedef FutureOr<User> LocalAuthVerifier<User>( String username, String password); class LocalAuthStrategy<User> extends AuthStrategy<User> { RegExp _rgxBasic = new RegExp(r'^Basic (.+)$', caseSensitive: false); RegExp _rgxUsrPass = new RegExp(r'^([^:]+):(.+)$'); LocalAuthVerifier<User> verifier; String usernameField; String passwordField; String invalidMessage; final bool allowBasic; final bool forceBasic; String realm; LocalAuthStrategy(this.verifier, {String this.usernameField = 'username', String this.passwordField = 'password', String this.invalidMessage = 'Please provide a valid username and password.', bool this.allowBasic = true, bool this.forceBasic = false, String this.realm = 'Authentication is required.'}) {} @override Future<User> authenticate(RequestContext req, ResponseContext res, [AngelAuthOptions options_]) async { AngelAuthOptions options = options_ ?? new AngelAuthOptions(); User verificationResult; if (allowBasic) { String authHeader = req.headers.value('authorization') ?? ""; if (_rgxBasic.hasMatch(authHeader)) { String base64AuthString = _rgxBasic.firstMatch(authHeader).group(1); String authString = new String.fromCharCodes(base64.decode(base64AuthString)); if (_rgxUsrPass.hasMatch(authString)) { Match usrPassMatch = _rgxUsrPass.firstMatch(authString); verificationResult = await verifier(usrPassMatch.group(1), usrPassMatch.group(2)); } else throw new AngelHttpException.badRequest(errors: [invalidMessage]); if (verificationResult == false || verificationResult == null) { res ..statusCode = 401 ..headers['www-authenticate'] = 'Basic realm="$realm"'; await res.close(); return null; } return verificationResult; } } if (verificationResult == null) { var body = await req .parseBody() .then((_) => req.bodyAsMap) .catchError((_) => <String, dynamic>{}); if (_validateString(body[usernameField]?.toString()) && _validateString(body[passwordField]?.toString())) { verificationResult = await verifier( body[usernameField]?.toString(), body[passwordField]?.toString()); } } if (verificationResult == false || verificationResult == null) { if (options.failureRedirect != null && options.failureRedirect.isNotEmpty) { res.redirect(options.failureRedirect, code: 401); return null; } if (forceBasic) { res.headers['www-authenticate'] = 'Basic realm="$realm"'; throw new AngelHttpException.notAuthenticated(); } return null; } else if (verificationResult != null && verificationResult != false) { return verificationResult; } else { throw new AngelHttpException.notAuthenticated(); } } }