+6
This commit is contained in:
parent
30d5293612
commit
e168ea7ba3
19 changed files with 216 additions and 199 deletions
17
.gitignore
vendored
17
.gitignore
vendored
|
@ -1,3 +1,5 @@
|
||||||
|
# Created by .ignore support plugin (hsz.mobi)
|
||||||
|
### Dart template
|
||||||
# See https://www.dartlang.org/tools/private-files.html
|
# See https://www.dartlang.org/tools/private-files.html
|
||||||
|
|
||||||
# Files and directories created by pub
|
# Files and directories created by pub
|
||||||
|
@ -70,19 +72,4 @@ com_crashlytics_export_strings.xml
|
||||||
crashlytics.properties
|
crashlytics.properties
|
||||||
crashlytics-build.properties
|
crashlytics-build.properties
|
||||||
fabric.properties
|
fabric.properties
|
||||||
### Dart template
|
|
||||||
# See https://www.dartlang.org/tools/private-files.html
|
|
||||||
|
|
||||||
# Files and directories created by pub
|
|
||||||
|
|
||||||
# Files created by dart2js
|
|
||||||
# (Most Dart developers will use pub build to compile Dart, use/modify these
|
|
||||||
# rules if you intend to use dart2js directly
|
|
||||||
# Convention is to use extension '.dart.js' for Dart compiled to Javascript to
|
|
||||||
# differentiate from explicit Javascript files)
|
|
||||||
|
|
||||||
# Directory created by dartdoc
|
|
||||||
|
|
||||||
# Don't commit pubspec lock file
|
|
||||||
# (Library packages only! Remove pattern if developing an application package)
|
|
||||||
.idea
|
|
6
.idea/runConfigurations/All_Tests.xml
Normal file
6
.idea/runConfigurations/All_Tests.xml
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
<component name="ProjectRunConfigurationManager">
|
||||||
|
<configuration default="false" name="All Tests" type="DartTestRunConfigurationType" factoryName="Dart Test" singleton="true">
|
||||||
|
<option name="filePath" value="$PROJECT_DIR$/test/all_tests.dart" />
|
||||||
|
<method />
|
||||||
|
</configuration>
|
||||||
|
</component>
|
6
.idea/runConfigurations/Local_Tests.xml
Normal file
6
.idea/runConfigurations/Local_Tests.xml
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
<component name="ProjectRunConfigurationManager">
|
||||||
|
<configuration default="false" name="Local Tests" type="DartTestRunConfigurationType" factoryName="Dart Test" singleton="true">
|
||||||
|
<option name="filePath" value="$PROJECT_DIR$/test/local.dart" />
|
||||||
|
<method />
|
||||||
|
</configuration>
|
||||||
|
</component>
|
|
@ -1,2 +1,8 @@
|
||||||
# angel_auth
|
# angel_auth
|
||||||
A complete authentication plugin for Angel. Inspired by Passport.
|
A complete authentication plugin for Angel. Inspired by Passport.
|
||||||
|
|
||||||
|
# Documentation
|
||||||
|
Coming soon!
|
||||||
|
|
||||||
|
# Supported Strategies
|
||||||
|
* Local (with and without Basic Auth)
|
|
@ -1,91 +1,8 @@
|
||||||
library angel_auth;
|
library angel_auth;
|
||||||
|
|
||||||
import 'dart:async';
|
export 'src/middleware/require_auth.dart';
|
||||||
import 'dart:convert';
|
export 'src/strategies/strategies.dart';
|
||||||
import 'dart:io';
|
export 'src/defs.dart';
|
||||||
import 'package:angel_framework/angel_framework.dart';
|
export 'src/options.dart';
|
||||||
import 'package:oauth2/oauth2.dart' as Oauth2;
|
export 'src/plugin.dart';
|
||||||
|
export 'src/strategy.dart';
|
||||||
part 'strategy.dart';
|
|
||||||
|
|
||||||
part 'middleware/require_auth.dart';
|
|
||||||
|
|
||||||
part 'middleware/serialization.dart';
|
|
||||||
|
|
||||||
part 'strategies/local.dart';
|
|
||||||
|
|
||||||
part 'strategies/token.dart';
|
|
||||||
|
|
||||||
part 'strategies/oauth2.dart';
|
|
||||||
|
|
||||||
_validateString(String str) {
|
|
||||||
return str != null && str.isNotEmpty;
|
|
||||||
}
|
|
||||||
|
|
||||||
const String FAILURE_REDIRECT = 'failureRedirect';
|
|
||||||
const String SUCCESS_REDIRECT = 'successRedirect';
|
|
||||||
|
|
||||||
class Auth {
|
|
||||||
static List<AuthStrategy> strategies = [];
|
|
||||||
static UserSerializer serializer;
|
|
||||||
static UserDeserializer deserializer;
|
|
||||||
|
|
||||||
call(Angel app) async {
|
|
||||||
app.registerMiddleware('auth', requireAuth);
|
|
||||||
app.before.add(_serializationMiddleware);
|
|
||||||
}
|
|
||||||
|
|
||||||
static authenticate(String type, [AngelAuthOptions options]) {
|
|
||||||
return (RequestContext req, ResponseContext res) async {
|
|
||||||
AuthStrategy strategy =
|
|
||||||
strategies.firstWhere((AuthStrategy x) => x.name == type);
|
|
||||||
var result = await strategy.authenticate(req, res, options);
|
|
||||||
if (result == true)
|
|
||||||
return result;
|
|
||||||
else if (result != false) {
|
|
||||||
req.session['userId'] = await serializer(result);
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
throw new AngelHttpException.NotAuthenticated();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
static logout([AngelAuthOptions options]) {
|
|
||||||
return (RequestContext req, ResponseContext res) async {
|
|
||||||
for (AuthStrategy strategy in Auth.strategies) {
|
|
||||||
if (!(await strategy.canLogout(req, res))) {
|
|
||||||
if (options != null &&
|
|
||||||
options.failureRedirect != null &&
|
|
||||||
options.failureRedirect.isNotEmpty) {
|
|
||||||
return res.redirect(options.failureRedirect);
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
req.session.remove('userId');
|
|
||||||
|
|
||||||
if (options != null &&
|
|
||||||
options.successRedirect != null &&
|
|
||||||
options.successRedirect.isNotEmpty) {
|
|
||||||
return res.redirect(options.successRedirect);
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class AngelAuthOptions {
|
|
||||||
String successRedirect;
|
|
||||||
String failureRedirect;
|
|
||||||
|
|
||||||
AngelAuthOptions({String this.successRedirect, String this.failureRedirect});
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Configures an app to use angel_auth. :)
|
|
||||||
Future AngelAuth(Angel app) async {
|
|
||||||
await app.configure(new Auth());
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,32 +0,0 @@
|
||||||
part of angel_auth;
|
|
||||||
|
|
||||||
/// Restricts access to a resource via authentication.
|
|
||||||
Future<bool> requireAuth(RequestContext req, ResponseContext res,
|
|
||||||
{bool throws: true}) async {
|
|
||||||
reject() {
|
|
||||||
if (throws) {
|
|
||||||
res.status(HttpStatus.UNAUTHORIZED);
|
|
||||||
throw new AngelHttpException.Forbidden();
|
|
||||||
} else
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (req.session.containsKey('userId'))
|
|
||||||
return true;
|
|
||||||
else if (req.headers.value("Authorization") != null) {
|
|
||||||
var jwt = req.headers
|
|
||||||
.value("Authorization")
|
|
||||||
.replaceAll(new RegExp(r"^Bearer", caseSensitive: false), "")
|
|
||||||
.trim();
|
|
||||||
|
|
||||||
var split = jwt.split(".");
|
|
||||||
if (split.length != 3) return reject();
|
|
||||||
|
|
||||||
Map header = JSON.decode(UTF8.decode(BASE64URL.decode(split[0])));
|
|
||||||
|
|
||||||
if (header['typ'] != "JWT" || header['alg'] != "HS256") return reject();
|
|
||||||
|
|
||||||
Map payload = JSON.decode(UTF8.decode(BASE64URL.decode(split[1])));
|
|
||||||
} else
|
|
||||||
return reject();
|
|
||||||
}
|
|
|
@ -1,15 +0,0 @@
|
||||||
part of angel_auth;
|
|
||||||
|
|
||||||
/// Serializes a user to the session.
|
|
||||||
typedef Future UserSerializer(user);
|
|
||||||
|
|
||||||
/// Deserializes a user from the session.
|
|
||||||
typedef Future UserDeserializer(userId);
|
|
||||||
|
|
||||||
_serializationMiddleware(RequestContext req, ResponseContext res) async {
|
|
||||||
if (await requireAuth(req, res, throws: false)) {
|
|
||||||
req.properties['user'] = await Auth.deserializer(req.session['userId']);
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
7
lib/src/defs.dart
Normal file
7
lib/src/defs.dart
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
import 'dart:async';
|
||||||
|
|
||||||
|
/// Serializes a user to the session.
|
||||||
|
typedef Future UserSerializer(user);
|
||||||
|
|
||||||
|
/// Deserializes a user from the session.
|
||||||
|
typedef Future UserDeserializer(userId);
|
42
lib/src/middleware/require_auth.dart
Normal file
42
lib/src/middleware/require_auth.dart
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
import 'dart:async';
|
||||||
|
import 'dart:convert';
|
||||||
|
import 'dart:io';
|
||||||
|
import 'package:angel_framework/angel_framework.dart';
|
||||||
|
|
||||||
|
/// Restricts access to a resource via authentication.
|
||||||
|
class RequireAuthorizationMiddleware extends BaseMiddleware {
|
||||||
|
@override
|
||||||
|
Future<bool> call(RequestContext req, ResponseContext res, {bool throwError: true}) async {
|
||||||
|
bool _reject(ResponseContext res) {
|
||||||
|
if (throwError) {
|
||||||
|
res.status(HttpStatus.FORBIDDEN);
|
||||||
|
throw new AngelHttpException.Forbidden();
|
||||||
|
} else
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (req.session.containsKey('userId'))
|
||||||
|
return true;
|
||||||
|
else if (req.headers.value("Authorization") != null) {
|
||||||
|
var jwt = req.headers
|
||||||
|
.value("Authorization")
|
||||||
|
.replaceAll(new RegExp(r"^Bearer", caseSensitive: false), "")
|
||||||
|
.trim();
|
||||||
|
|
||||||
|
var split = jwt.split(".");
|
||||||
|
if (split.length != 3) return _reject(res);
|
||||||
|
|
||||||
|
Map header = JSON.decode(UTF8.decode(BASE64URL.decode(split[0])));
|
||||||
|
|
||||||
|
if (header['typ'] != "JWT" || header['alg'] != "HS256")
|
||||||
|
return _reject(res);
|
||||||
|
|
||||||
|
Map payload = JSON.decode(UTF8.decode(BASE64URL.decode(split[1])));
|
||||||
|
|
||||||
|
// Todo: JWT
|
||||||
|
return false;
|
||||||
|
} else
|
||||||
|
return _reject(res);
|
||||||
|
}
|
||||||
|
}
|
6
lib/src/options.dart
Normal file
6
lib/src/options.dart
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
class AngelAuthOptions {
|
||||||
|
String successRedirect;
|
||||||
|
String failureRedirect;
|
||||||
|
|
||||||
|
AngelAuthOptions({String this.successRedirect, String this.failureRedirect});
|
||||||
|
}
|
73
lib/src/plugin.dart
Normal file
73
lib/src/plugin.dart
Normal file
|
@ -0,0 +1,73 @@
|
||||||
|
import 'package:angel_framework/angel_framework.dart';
|
||||||
|
import 'middleware/require_auth.dart';
|
||||||
|
import 'defs.dart';
|
||||||
|
import 'options.dart';
|
||||||
|
import 'strategy.dart';
|
||||||
|
|
||||||
|
class AngelAuth extends AngelPlugin {
|
||||||
|
RequireAuthorizationMiddleware _requireAuth = new RequireAuthorizationMiddleware();
|
||||||
|
List<AuthStrategy> strategies = [];
|
||||||
|
UserSerializer serializer;
|
||||||
|
UserDeserializer deserializer;
|
||||||
|
|
||||||
|
@override
|
||||||
|
call(Angel app) async {
|
||||||
|
app.container.singleton(this);
|
||||||
|
|
||||||
|
if (runtimeType != AngelAuth)
|
||||||
|
app.container.singleton(this, as: AngelAuth);
|
||||||
|
|
||||||
|
app.registerMiddleware('auth', _requireAuth);
|
||||||
|
app.before.add(_serializationMiddleware);
|
||||||
|
}
|
||||||
|
|
||||||
|
_serializationMiddleware(RequestContext req, ResponseContext res) async {
|
||||||
|
if (await _requireAuth(req, res, throwError: false)) {
|
||||||
|
req.properties['user'] = await deserializer(req.session['userId']);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
authenticate(String type, [AngelAuthOptions options]) {
|
||||||
|
return (RequestContext req, ResponseContext res) async {
|
||||||
|
AuthStrategy strategy =
|
||||||
|
strategies.firstWhere((AuthStrategy x) => x.name == type);
|
||||||
|
var result = await strategy.authenticate(req, res, options);
|
||||||
|
if (result == true)
|
||||||
|
return result;
|
||||||
|
else if (result != false) {
|
||||||
|
req.session['userId'] = await serializer(result);
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
throw new AngelHttpException.NotAuthenticated();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
logout([AngelAuthOptions options]) {
|
||||||
|
return (RequestContext req, ResponseContext res) async {
|
||||||
|
for (AuthStrategy strategy in strategies) {
|
||||||
|
if (!(await strategy.canLogout(req, res))) {
|
||||||
|
if (options != null &&
|
||||||
|
options.failureRedirect != null &&
|
||||||
|
options.failureRedirect.isNotEmpty) {
|
||||||
|
return res.redirect(options.failureRedirect);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
req.session.remove('userId');
|
||||||
|
|
||||||
|
if (options != null &&
|
||||||
|
options.successRedirect != null &&
|
||||||
|
options.successRedirect.isNotEmpty) {
|
||||||
|
return res.redirect(options.successRedirect);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,9 +1,18 @@
|
||||||
part of angel_auth;
|
import 'dart:async';
|
||||||
|
import 'dart:convert';
|
||||||
|
import 'dart:io';
|
||||||
|
import 'package:angel_framework/angel_framework.dart';
|
||||||
|
import '../options.dart';
|
||||||
|
import '../plugin.dart';
|
||||||
|
import '../strategy.dart';
|
||||||
|
|
||||||
|
bool _validateString(String str) => str != null && str.isNotEmpty;
|
||||||
|
|
||||||
/// Determines the validity of an incoming username and password.
|
/// Determines the validity of an incoming username and password.
|
||||||
typedef Future LocalAuthVerifier(String username, String password);
|
typedef Future LocalAuthVerifier(String username, String password);
|
||||||
|
|
||||||
class LocalAuthStrategy extends AuthStrategy {
|
class LocalAuthStrategy extends AuthStrategy {
|
||||||
|
AngelAuth _plugin;
|
||||||
RegExp _rgxBasic = new RegExp(r'^Basic (.+)$', caseSensitive: false);
|
RegExp _rgxBasic = new RegExp(r'^Basic (.+)$', caseSensitive: false);
|
||||||
RegExp _rgxUsrPass = new RegExp(r'^([^:]+):(.+)$');
|
RegExp _rgxUsrPass = new RegExp(r'^([^:]+):(.+)$');
|
||||||
|
|
||||||
|
@ -17,7 +26,7 @@ class LocalAuthStrategy extends AuthStrategy {
|
||||||
bool forceBasic;
|
bool forceBasic;
|
||||||
String realm;
|
String realm;
|
||||||
|
|
||||||
LocalAuthStrategy(LocalAuthVerifier this.verifier,
|
LocalAuthStrategy(AngelAuth this._plugin, LocalAuthVerifier this.verifier,
|
||||||
{String this.usernameField: 'username',
|
{String this.usernameField: 'username',
|
||||||
String this.passwordField: 'password',
|
String this.passwordField: 'password',
|
||||||
String this.invalidMessage:
|
String this.invalidMessage:
|
||||||
|
@ -64,7 +73,7 @@ class LocalAuthStrategy extends AuthStrategy {
|
||||||
if (options.failureRedirect != null &&
|
if (options.failureRedirect != null &&
|
||||||
options.failureRedirect.isNotEmpty) {
|
options.failureRedirect.isNotEmpty) {
|
||||||
return res.redirect(
|
return res.redirect(
|
||||||
options.failureRedirect, code: HttpStatus.UNAUTHORIZED);
|
options.failureRedirect, code: HttpStatus.FORBIDDEN);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (forceBasic) {
|
if (forceBasic) {
|
||||||
|
@ -77,7 +86,7 @@ class LocalAuthStrategy extends AuthStrategy {
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (verificationResult != null && verificationResult != false) {
|
else if (verificationResult != null && verificationResult != false) {
|
||||||
req.session['userId'] = await Auth.serializer(verificationResult);
|
req.session['userId'] = await _plugin.serializer(verificationResult);
|
||||||
if (options.successRedirect != null &&
|
if (options.successRedirect != null &&
|
||||||
options.successRedirect.isNotEmpty) {
|
options.successRedirect.isNotEmpty) {
|
||||||
return res.redirect(options.successRedirect, code: HttpStatus.OK);
|
return res.redirect(options.successRedirect, code: HttpStatus.OK);
|
|
@ -1,5 +1,8 @@
|
||||||
part of angel_auth;
|
import 'dart:async';
|
||||||
|
import 'package:angel_framework/angel_framework.dart';
|
||||||
|
import 'package:oauth2/oauth2.dart' as Oauth2;
|
||||||
|
import '../options.dart';
|
||||||
|
import '../strategy.dart';
|
||||||
/// Logs a user in based on an incoming OAuth access and refresh token.
|
/// Logs a user in based on an incoming OAuth access and refresh token.
|
||||||
typedef Future OAuth2AuthVerifier(String accessToken, String refreshToken,
|
typedef Future OAuth2AuthVerifier(String accessToken, String refreshToken,
|
||||||
Map profile);
|
Map profile);
|
3
lib/src/strategies/strategies.dart
Normal file
3
lib/src/strategies/strategies.dart
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
export 'local.dart';
|
||||||
|
export 'oauth2.dart';
|
||||||
|
export 'token.dart';
|
|
@ -1,15 +1,16 @@
|
||||||
part of angel_auth;
|
import 'dart:async';
|
||||||
|
import 'package:angel_framework/angel_framework.dart';
|
||||||
|
import '../options.dart';
|
||||||
|
import '../strategy.dart';
|
||||||
|
|
||||||
class JwtAuthStrategy extends AuthStrategy {
|
class JwtAuthStrategy extends AuthStrategy {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future authenticate(RequestContext req, ResponseContext res,
|
Future authenticate(RequestContext req, ResponseContext res,
|
||||||
[AngelAuthOptions options]) {
|
[AngelAuthOptions options]) async {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<bool> canLogout(RequestContext req, ResponseContext res) {
|
Future<bool> canLogout(RequestContext req, ResponseContext res) async => false;
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -1,4 +1,6 @@
|
||||||
part of angel_auth;
|
import 'dart:async';
|
||||||
|
import 'package:angel_framework/angel_framework.dart';
|
||||||
|
import 'options.dart';
|
||||||
|
|
||||||
/// A function that handles login and signup for an Angel application.
|
/// A function that handles login and signup for an Angel application.
|
||||||
abstract class AuthStrategy {
|
abstract class AuthStrategy {
|
|
@ -1,6 +1,6 @@
|
||||||
name: angel_auth
|
name: angel_auth
|
||||||
description: A complete authentication plugin for Angel.
|
description: A complete authentication plugin for Angel.
|
||||||
version: 1.0.0-dev+5
|
version: 1.0.0-dev+6
|
||||||
author: Tobe O <thosakwe@gmail.com>
|
author: Tobe O <thosakwe@gmail.com>
|
||||||
homepage: https://github.com/angel-dart/angel_auth
|
homepage: https://github.com/angel-dart/angel_auth
|
||||||
dependencies:
|
dependencies:
|
||||||
|
|
|
@ -3,6 +3,7 @@ import 'package:angel_framework/angel_framework.dart';
|
||||||
import 'package:angel_auth/angel_auth.dart';
|
import 'package:angel_auth/angel_auth.dart';
|
||||||
import 'package:http/http.dart' as http;
|
import 'package:http/http.dart' as http;
|
||||||
import 'package:test/test.dart';
|
import 'package:test/test.dart';
|
||||||
|
import 'local.dart' as local;
|
||||||
|
|
||||||
wireAuth(Angel app) async {
|
wireAuth(Angel app) async {
|
||||||
|
|
||||||
|
@ -29,6 +30,8 @@ main() async {
|
||||||
url = null;
|
url = null;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
group("local", local.main);
|
||||||
|
|
||||||
test('can use login as middleware', () async {
|
test('can use login as middleware', () async {
|
||||||
|
|
||||||
});
|
});
|
|
@ -6,30 +6,29 @@ import 'package:http/http.dart' as http;
|
||||||
import 'package:merge_map/merge_map.dart';
|
import 'package:merge_map/merge_map.dart';
|
||||||
import 'package:test/test.dart';
|
import 'package:test/test.dart';
|
||||||
|
|
||||||
|
final AngelAuth Auth = new AngelAuth();
|
||||||
Map headers = {HttpHeaders.ACCEPT: ContentType.JSON.mimeType};
|
Map headers = {HttpHeaders.ACCEPT: ContentType.JSON.mimeType};
|
||||||
AngelAuthOptions localOpts = new AngelAuthOptions(
|
AngelAuthOptions localOpts = new AngelAuthOptions(
|
||||||
failureRedirect: '/failure',
|
failureRedirect: '/failure', successRedirect: '/success');
|
||||||
successRedirect: '/success'
|
|
||||||
);
|
|
||||||
Map sampleUser = {'hello': 'world'};
|
Map sampleUser = {'hello': 'world'};
|
||||||
|
|
||||||
verifier(username, password) async {
|
verifier(username, password) async {
|
||||||
if (username == 'username' && password == 'password') {
|
if (username == 'username' && password == 'password') {
|
||||||
return sampleUser;
|
return sampleUser;
|
||||||
} else return false;
|
} else
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
wireAuth(Angel app) async {
|
wireAuth(Angel app) async {
|
||||||
Auth.serializer = (user) async => 1337;
|
Auth.serializer = (user) async => 1337;
|
||||||
Auth.deserializer = (id) async => sampleUser;
|
Auth.deserializer = (id) async => sampleUser;
|
||||||
|
|
||||||
Auth.strategies.add(new LocalAuthStrategy(verifier));
|
Auth.strategies.add(new LocalAuthStrategy(Auth, verifier));
|
||||||
await app.configure(AngelAuth);
|
await app.configure(Auth);
|
||||||
}
|
}
|
||||||
|
|
||||||
main() async {
|
main() async {
|
||||||
group
|
group('local', () {
|
||||||
('local', () {
|
|
||||||
Angel app;
|
Angel app;
|
||||||
http.Client client;
|
http.Client client;
|
||||||
String url;
|
String url;
|
||||||
|
@ -45,8 +44,8 @@ main() async {
|
||||||
app.get('/success', "yep", middleware: ['auth']);
|
app.get('/success', "yep", middleware: ['auth']);
|
||||||
app.get('/failure', "nope");
|
app.get('/failure', "nope");
|
||||||
|
|
||||||
HttpServer server = await app.startServer(
|
HttpServer server =
|
||||||
InternetAddress.LOOPBACK_IP_V4, 0);
|
await app.startServer(InternetAddress.LOOPBACK_IP_V4, 0);
|
||||||
url = "http://${server.address.host}:${server.port}";
|
url = "http://${server.address.host}:${server.port}";
|
||||||
basicAuthUrl =
|
basicAuthUrl =
|
||||||
"http://username:password@${server.address.host}:${server.port}";
|
"http://username:password@${server.address.host}:${server.port}";
|
||||||
|
@ -59,42 +58,36 @@ main() async {
|
||||||
basicAuthUrl = null;
|
basicAuthUrl = null;
|
||||||
});
|
});
|
||||||
|
|
||||||
test('can use login as middleware', () async {
|
test('can use "auth" as middleware', () async {
|
||||||
var response = await client.get(
|
var response = await client
|
||||||
"$url/success", headers: {'Accept': 'application/json'});
|
.get("$url/success", headers: {'Accept': 'application/json'});
|
||||||
print(response.body);
|
print(response.body);
|
||||||
expect(response.statusCode, equals(401));
|
expect(response.statusCode, equals(403));
|
||||||
});
|
});
|
||||||
|
|
||||||
test('successRedirect', () async {
|
test('successRedirect', () async {
|
||||||
Map postData = {
|
Map postData = {'username': 'username', 'password': 'password'};
|
||||||
'username': 'username',
|
var response = await client.post("$url/login",
|
||||||
'password': 'password'
|
body: JSON.encode(postData),
|
||||||
};
|
|
||||||
var response = await client.post(
|
|
||||||
"$url/login", body: JSON.encode(postData),
|
|
||||||
headers: {HttpHeaders.CONTENT_TYPE: ContentType.JSON.mimeType});
|
headers: {HttpHeaders.CONTENT_TYPE: ContentType.JSON.mimeType});
|
||||||
expect(response.statusCode, equals(200));
|
expect(response.statusCode, equals(200));
|
||||||
expect(response.headers[HttpHeaders.LOCATION], equals('/success'));
|
expect(response.headers[HttpHeaders.LOCATION], equals('/success'));
|
||||||
});
|
});
|
||||||
|
|
||||||
test('failureRedirect', () async {
|
test('failureRedirect', () async {
|
||||||
Map postData = {
|
Map postData = {'username': 'password', 'password': 'username'};
|
||||||
'username': 'password',
|
var response = await client.post("$url/login",
|
||||||
'password': 'username'
|
body: JSON.encode(postData),
|
||||||
};
|
|
||||||
var response = await client.post(
|
|
||||||
"$url/login", body: JSON.encode(postData),
|
|
||||||
headers: {HttpHeaders.CONTENT_TYPE: ContentType.JSON.mimeType});
|
headers: {HttpHeaders.CONTENT_TYPE: ContentType.JSON.mimeType});
|
||||||
expect(response.statusCode, equals(401));
|
expect(response.statusCode, equals(403));
|
||||||
expect(response.headers[HttpHeaders.LOCATION], equals('/failure'));
|
expect(response.headers[HttpHeaders.LOCATION], equals('/failure'));
|
||||||
});
|
});
|
||||||
|
|
||||||
test('allow basic', () async {
|
test('allow basic', () async {
|
||||||
String authString = BASE64.encode("username:password".runes.toList());
|
String authString = BASE64.encode("username:password".runes.toList());
|
||||||
Map auth = {HttpHeaders.AUTHORIZATION: 'Basic $authString'};
|
Map auth = {HttpHeaders.AUTHORIZATION: 'Basic $authString'};
|
||||||
var response = await client.get(
|
var response =
|
||||||
"$url/hello", headers: mergeMap([auth, headers]));
|
await client.get("$url/hello", headers: mergeMap([auth, headers]));
|
||||||
expect(response.body, equals('"Woo auth"'));
|
expect(response.body, equals('"Woo auth"'));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -105,8 +98,8 @@ main() async {
|
||||||
|
|
||||||
test('force basic', () async {
|
test('force basic', () async {
|
||||||
Auth.strategies.clear();
|
Auth.strategies.clear();
|
||||||
Auth.strategies.add(new LocalAuthStrategy(
|
Auth.strategies.add(new LocalAuthStrategy(Auth, verifier,
|
||||||
verifier, forceBasic: true, realm: 'test'));
|
forceBasic: true, realm: 'test'));
|
||||||
var response = await client.get("$url/hello", headers: headers);
|
var response = await client.get("$url/hello", headers: headers);
|
||||||
expect(response.headers[HttpHeaders.WWW_AUTHENTICATE],
|
expect(response.headers[HttpHeaders.WWW_AUTHENTICATE],
|
||||||
equals('Basic realm="test"'));
|
equals('Basic realm="test"'));
|
||||||
|
|
Loading…
Reference in a new issue