Updated auth

This commit is contained in:
thomashii 2021-09-29 15:40:27 +08:00
parent 7652c02183
commit 7cbbc686ba
31 changed files with 182 additions and 146 deletions

View file

@ -1,5 +1,11 @@
# Change Log
## 4.1.1
* Changed `userId` field of `AuthToken` to String type
* Changed `serializer` return value to String type
* Changed `deserializer` input parameter to String type
## 4.1.0
* Updated linter to `package:lints`

View file

@ -18,9 +18,10 @@ Ensure you have read the [User Guide](https://angel3-docs.dukefirehawk.com/guide
```dart
configureServer(Angel app) async {
var auth = AngelAuth<User>();
auth.serializer = ...;
auth.deserializer = ...;
var auth = AngelAuth<User>(
serializer: (user) => user.id ?? '',
deserializer: (id) => fetchAUserByIdSomehow(id)
);
auth.strategies['local'] = LocalAuthStrategy(...);
// POST route to handle username+password

View file

@ -6,7 +6,7 @@ import 'package:angel3_framework/http.dart';
void main() async {
var app = Angel();
var auth = AngelAuth<User>(
serializer: (user) => user.id,
serializer: (user) => user.id ?? '',
deserializer: (id) => fetchAUserByIdSomehow(id));
// Middleware to decode JWT's and inject a user object...
@ -31,7 +31,7 @@ class User {
String? id, username, password;
}
Future<User> fetchAUserByIdSomehow(id) async {
Future<User> fetchAUserByIdSomehow(String id) async {
// Fetch a user somehow...
throw UnimplementedError();
}

View file

@ -32,41 +32,35 @@ class AuthToken {
SplayTreeMap.from({'alg': 'HS256', 'typ': 'JWT'});
String? ipAddress;
late DateTime issuedAt;
num lifeSpan;
dynamic userId;
String userId;
late DateTime issuedAt;
Map<String, dynamic> payload = {};
AuthToken(
{this.ipAddress,
this.lifeSpan = -1,
this.userId,
required this.userId,
DateTime? issuedAt,
Map payload = const {}}) {
Map<String, dynamic>? payload}) {
this.issuedAt = issuedAt ?? DateTime.now();
this.payload.addAll(payload.keys
.fold({}, ((out, k) => out?..[k.toString()] = payload[k])) ??
{});
/*
this.payload.addAll(payload.keys.fold(
{},
((out, k) => out..[k.toString()] = payload[k])
as Map<String, dynamic>? Function(
Map<String, dynamic>?, dynamic)) ??
{});
*/
if (payload != null) {
this.payload.addAll(payload.keys
.fold({}, ((out, k) => out?..[k.toString()] = payload[k])) ??
{});
}
}
factory AuthToken.fromJson(String jsons) =>
AuthToken.fromMap(json.decode(jsons) as Map);
AuthToken.fromMap(json.decode(jsons) as Map<String, dynamic>);
factory AuthToken.fromMap(Map data) {
factory AuthToken.fromMap(Map<String, dynamic> data) {
return AuthToken(
ipAddress: data['aud'].toString(),
lifeSpan: data['exp'] as num,
issuedAt: DateTime.parse(data['iat'].toString()),
userId: data['sub'],
payload: data['pld'] as Map);
payload: data['pld']);
}
factory AuthToken.parse(String jwt) {
@ -78,7 +72,8 @@ class AuthToken {
}
var payloadString = decodeBase64(split[1]);
return AuthToken.fromMap(json.decode(payloadString) as Map);
return AuthToken.fromMap(
json.decode(payloadString) as Map<String, dynamic>);
}
factory AuthToken.validate(String jwt, Hmac hmac) {
@ -100,7 +95,8 @@ class AuthToken {
message: 'JWT payload does not match hashed version.');
}
return AuthToken.fromMap(json.decode(payloadString) as Map);
return AuthToken.fromMap(
json.decode(payloadString) as Map<String, dynamic>);
}
String serialize(Hmac hmac) {
@ -111,7 +107,7 @@ class AuthToken {
return data + '.' + base64Url.encode(signature);
}
Map toJson() {
Map<String, dynamic> toJson() {
return _splayify({
'iss': 'angel_auth',
'aud': ipAddress,
@ -123,7 +119,7 @@ class AuthToken {
}
}
SplayTreeMap _splayify(Map map) {
Map<String, dynamic> _splayify(Map<String, dynamic> map) {
var data = {};
map.forEach((k, v) {
data[k] = _splay(v);
@ -131,11 +127,11 @@ SplayTreeMap _splayify(Map map) {
return SplayTreeMap.from(data);
}
dynamic _splay(value) {
dynamic _splay(dynamic value) {
if (value is Iterable) {
return value.map(_splay).toList();
} else if (value is Map) {
return _splayify(value);
return _splayify(value as Map<String, dynamic>);
} else {
return value;
}

View file

@ -46,7 +46,7 @@ class ExternalAuthOptions {
/// * `client_id`
/// * `client_secret`
/// * `redirect_uri`
factory ExternalAuthOptions.fromMap(Map map) {
factory ExternalAuthOptions.fromMap(Map<String, dynamic> map) {
var clientId = map['client_id'];
var clientSecret = map['client_secret'];
if (clientId == null || clientSecret == null) {
@ -55,8 +55,8 @@ class ExternalAuthOptions {
}
return ExternalAuthOptions(
clientId: clientId as String,
clientSecret: clientSecret as String,
clientId: clientId,
clientSecret: clientSecret,
redirectUri: map['redirect_uri'],
scopes: map['scopes'] is Iterable
? ((map['scopes'] as Iterable).map((x) => x.toString()))

View file

@ -52,10 +52,10 @@ class AngelAuth<User> {
Map<String, AuthStrategy<User>> strategies = {};
/// Serializes a user into a unique identifier associated only with one identity.
FutureOr Function(User) serializer;
FutureOr<String> Function(User) serializer;
/// Deserializes a unique identifier into its associated identity. In most cases, this is a user object or model instance.
FutureOr<User> Function(Object) deserializer;
FutureOr<User> Function(String) deserializer;
/// Fires the result of [deserializer] whenever a user signs in to the application.
Stream<User> get onLogin => _onLogin.stream;
@ -200,6 +200,7 @@ class AngelAuth<User> {
/// String getUsername(User user) => user.name
/// }
/// ```
/*
@deprecated
Future decodeJwt(RequestContext req, ResponseContext res) async {
if (req.method == 'POST' && req.path == reviveTokenEndpoint) {
@ -209,6 +210,7 @@ class AngelAuth<User> {
return true;
}
}
*/
Future<_AuthResult<User>?> _decodeJwt(
RequestContext req, ResponseContext res) async {
@ -431,7 +433,7 @@ class AngelAuth<User> {
req.accepts('application/json')) {
var user = hasExisting
? result
: await deserializer((await serializer(result)) as Object);
: await deserializer(await serializer(result));
_onLogin.add(user);
return {'data': user, 'token': jwt};
}
@ -469,8 +471,8 @@ class AngelAuth<User> {
/// Log a user in on-demand.
Future loginById(
dynamic userId, RequestContext req, ResponseContext res) async {
var user = await deserializer(userId as Object);
String userId, RequestContext req, ResponseContext res) async {
var user = await deserializer(userId);
var token =
AuthToken(userId: userId, lifeSpan: _jwtLifeSpan, ipAddress: req.ip);
_apply(req, res, token, user);

View file

@ -38,8 +38,8 @@ class LocalAuthStrategy<User> extends AuthStrategy<User> {
@override
Future<User?> authenticate(RequestContext req, ResponseContext res,
[AngelAuthOptions? options_]) async {
var options = options_ ?? AngelAuthOptions();
[AngelAuthOptions? options]) async {
var _options = options ?? AngelAuthOptions();
User? verificationResult;
if (allowBasic) {
@ -88,9 +88,9 @@ class LocalAuthStrategy<User> extends AuthStrategy<User> {
if (verificationResult == null ||
(verificationResult is Map && verificationResult.isEmpty)) {
if (options.failureRedirect != null &&
options.failureRedirect!.isNotEmpty) {
await res.redirect(options.failureRedirect, code: 401);
if (_options.failureRedirect != null &&
_options.failureRedirect!.isNotEmpty) {
await res.redirect(_options.failureRedirect, code: 401);
return null;
}

View file

@ -1,6 +1,6 @@
name: angel3_auth
description: A complete authentication plugin for Angel3. Includes support for stateless JWT tokens, Basic Auth, and more.
version: 4.1.0
version: 4.1.1
homepage: https://angel3-framework.web.app/
repository: https://github.com/dukefirehawk/angel/tree/master/packages/auth
environment:
@ -21,5 +21,5 @@ dev_dependencies:
test: ^1.17.4
lints: ^1.0.0
#dependency_overrides:
# angel3_container:
# path: ../container/angel_container
# angel3_framework:
# path: ../framework

View file

@ -73,7 +73,7 @@ void main() {
?.create({'username': 'jdoe1', 'password': 'password'});
auth = AngelAuth<User>(
serializer: (u) => u.id,
serializer: (u) => u.id ?? '',
deserializer: (id) async =>
await app.findService('users')?.read(id) as User);
//auth.serializer = (u) => u.id;

View file

@ -8,7 +8,7 @@ import 'package:logging/logging.dart';
import 'package:test/test.dart';
final AngelAuth<Map<String, String>> auth = AngelAuth<Map<String, String>>(
serializer: (user) async => 1337, deserializer: (id) async => sampleUser);
serializer: (user) async => '1337', deserializer: (id) async => sampleUser);
var headers = <String, String>{'accept': 'application/json'};
var localOpts = AngelAuthOptions<Map<String, String>>(
failureRedirect: '/failure', successRedirect: '/success');

View file

@ -11,7 +11,7 @@ void main() {
secureCookies: true,
cookieDomain: 'SECURE',
jwtLifeSpan: threeDays.inMilliseconds,
serializer: (u) => u,
serializer: (u) => u as String,
deserializer: (u) => u);
setUp(() => defaultCookie = Cookie('a', 'b'));

View file

@ -1,5 +1,9 @@
# Change Log
## 4.1.0
* Updated linter to `package:lints`
## 4.0.2
* Updated example

View file

@ -1,21 +1,29 @@
MIT License (MIT)
BSD 3-Clause License
Copyright (c) 2021 dukefirehawk.com
Copyright (c) 2021, dukefirehawk.com
All rights reserved.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View file

@ -1,10 +1,9 @@
# Angel3 OAuth2 Handler
[![version](https://img.shields.io/badge/pub-v4.0.2-brightgreen)](https://pub.dartlang.org/packages/angel3_auth_oauth2)
[![Null Safety](https://img.shields.io/badge/null-safety-brightgreen)](https://dart.dev/null-safety)
![Pub Version (including pre-releases)](https://img.shields.io/pub/v/angel3_auth_oauth2?include_prereleases)
![Null Safety](https://img.shields.io/badge/null-safety-brightgreen)](<https://dart.dev/null-safety>)
[![Gitter](https://img.shields.io/gitter/room/angel_dart/discussion)](https://gitter.im/angel_dart/discussion)
[![License](https://img.shields.io/github/license/dukefirehawk/angel)](https://github.com/dukefirehawk/angel/tree/angel3/packages/auth_oauth2/LICENSE)
[![License](https://img.shields.io/github/license/dukefirehawk/angel)](https://github.com/dukefirehawk/angel/tree/master/packages/auth_oauth2/LICENSE)
Angel3 library for authenticating users with remote identity providers via OAuth2, i.e. Facebook, Google, Azure AD, etc.

View file

@ -1,4 +1 @@
include: package:pedantic/analysis_options.yaml
analyzer:
strong-mode:
implicit-casts: false
include: package:lints/recommended.yaml

View file

@ -48,8 +48,8 @@ void main() async {
// Set up the authenticator plugin.
var auth = AngelAuth<User>(
serializer: (user) async => user.id,
deserializer: (id) => mappedUserService.read(id.toString()),
serializer: (user) async => user.id ?? '',
deserializer: (id) => mappedUserService.read(id),
jwtKey: 'oauth2 example secret',
allowCookie: false);
await app.configure(auth.configureServer);
@ -120,7 +120,7 @@ class User extends Model {
User({this.id, this.githubId});
static User parse(Map map) =>
static User parse(Map<String, dynamic> map) =>
User(id: map['id'] as String?, githubId: map['github_id'] as int?);
static Map<String, dynamic> serialize(User user) => user.toJson();

View file

@ -1,15 +1,19 @@
name: angel3_auth_oauth2
version: 4.0.2
version: 4.1.0
description: Angel3 library for authenticating users with external identity providers via OAuth2.
homepage: https://angel3-framework.web.app/
repository: https://github.com/dukefirehawk/angel/tree/angel3/packages/auth_oauth2
repository: https://github.com/dukefirehawk/angel/tree/master/packages/auth_oauth2
environment:
sdk: '>=2.12.0 <3.0.0'
dependencies:
angel3_auth: ^4.0.0
angel3_framework: ^4.0.0
angel3_auth: ^4.1.0
angel3_framework: ^4.2.0
http_parser: ^4.0.0
oauth2: ^2.0.0
dev_dependencies:
logging: ^1.0.1
pedantic: ^1.11.0
lints: ^1.0.0
#dependency_overrides:
# angel3_auth:
# path: ../auth

View file

@ -5,7 +5,7 @@ import 'dart:async';
import 'package:collection/collection.dart';
import 'dart:convert';
import 'package:http/http.dart' as http;
import 'package:logging/logging.dart';
//import 'package:logging/logging.dart';
export 'package:angel3_http_exception/angel3_http_exception.dart';
/// A function that configures an [Angel] client in some way.

View file

@ -25,6 +25,6 @@ dev_dependencies:
test: ^1.17.5
lints: ^1.0.0
#dependency_overrides:
# angel3_container:
# path: ../container/angel_container
# angel3_auth:
# path: ../auth

View file

@ -6,8 +6,8 @@ repository: https://github.com/dukefirehawk/angel/tree/angel3/packages/hot
environment:
sdk: '>=2.12.0 <3.0.0'
dependencies:
angel3_framework: ^4.1.0
angel3_websocket: ^4.0.0
angel3_framework: ^4.2.0
angel3_websocket: ^4.1.0
belatuk_html_builder: ^3.0.0
charcode: ^1.2.0
glob: ^2.0.1
@ -19,3 +19,7 @@ dev_dependencies:
http: ^0.13.2
logging: ^1.0.1
lints: ^1.0.0
#dependency_overrides:
# angel3_websocket:
# path: ../websocket

View file

@ -17,3 +17,6 @@ dev_dependencies:
belatuk_pretty_logging: ^4.0.0
test: ^1.17.5
lints: ^1.0.0
#dependency_overrides:
# angel3_auth:
# path: ../auth

View file

@ -6,11 +6,14 @@ repository: https://github.com/dukefirehawk/angel/tree/angel3/packages/sync
environment:
sdk: '>=2.12.0 <3.0.0'
dependencies:
angel3_framework: ^4.1.0
angel3_websocket: ^4.0.0
angel3_framework: ^4.2.0
angel3_websocket: ^4.1.0
belatuk_pub_sub: ^4.0.0
stream_channel: ^2.1.0
dev_dependencies:
angel3_test: ^4.0.0
test: ^1.17.8
lints: ^1.0.0
angel3_test: ^4.1.0
test: ^1.17.5
lints: ^1.0.0
#dependency_overrides:
# angel3_websocket:
# path: ../websocket

View file

@ -1,5 +1,9 @@
# Change Log
## 4.1.1
* Fixed NNBD issues
## 4.1.0
* Updated linter to `package:lints`

View file

@ -6,7 +6,7 @@ import 'package:angel3_websocket/server.dart';
import 'package:test/test.dart';
void main() {
Angel? app;
Angel app;
late TestClient client;
setUp(() async {
@ -46,17 +46,16 @@ void main() {
create: (dynamic data, [params]) async => {'foo': 'bar'}));
var ws = AngelWebSocket(app);
await app!.configure(ws.configureServer);
app!.all('/ws', ws.handleRequest);
await app.configure(ws.configureServer);
app.all('/ws', ws.handleRequest);
app!.errorHandler = (e, req, res) => e.toJson();
app.errorHandler = (e, req, res) => e.toJson();
client = await connectTo(app!);
client = await connectTo(app);
});
tearDown(() async {
await client.close();
app = null;
});
group('matchers', () {

View file

@ -1,5 +1,5 @@
name: angel3_test
version: 4.1.0
version: 4.1.1
description: Testing utility library for the Angel3 framework. Use with package:test.
homepage: https://angel3-framework.web.app/
repository: https://github.com/dukefirehawk/angel/tree/master/packages/test
@ -19,7 +19,7 @@ dependencies:
dev_dependencies:
test: ^1.17.5
lints: ^1.0.0
#dependency_overrides:
# angel3_container:
# path: ../container/angel_container
dependency_overrides:
angel3_websocket:
path: ../websocket

View file

@ -6,7 +6,7 @@ import 'package:angel3_websocket/server.dart';
import 'package:test/test.dart';
void main() {
Angel? app;
Angel app;
late TestClient client;
setUp(() async {
@ -48,17 +48,16 @@ void main() {
<String, dynamic>{'foo': 'bar'}));
var ws = AngelWebSocket(app);
await app!.configure(ws.configureServer);
app!.all('/ws', ws.handleRequest);
await app.configure(ws.configureServer);
app.all('/ws', ws.handleRequest);
app!.errorHandler = (e, req, res) => e.toJson();
app.errorHandler = (e, req, res) => e.toJson();
client = await connectTo(app!, useZone: false);
client = await connectTo(app, useZone: false);
});
tearDown(() async {
await client.close();
app = null;
});
group('matchers', () {

View file

@ -1,5 +1,10 @@
# Change Log
## 4.1.1
* Fixed issue with type casting
* Changed `app` parameter of `AngelWebSocket` to non-nullable
## 4.1.0
* Updated to use `package:belatuk_merge_map`

View file

@ -40,17 +40,17 @@ class AngelWebSocket {
final StreamController<WebSocketContext> _onDisconnect =
StreamController<WebSocketContext>.broadcast();
final Angel? app;
final Angel app;
/// If this is not `true`, then all client-side service parameters will be
/// discarded, other than `params['query']`.
final bool allowClientParams;
/// An optional whitelist of allowed client origins, or [:null:].
final List<String>? allowedOrigins;
final List<String> allowedOrigins;
/// An optional whitelist of allowed client protocols, or [:null:].
final List<String>? allowedProtocols;
final List<String> allowedProtocols;
/// If `true`, then clients can authenticate their WebSockets by sending a valid JWT.
final bool allowAuth;
@ -93,8 +93,8 @@ class AngelWebSocket {
this.synchronizationChannel,
this.serializer,
this.deserializer,
this.allowedOrigins,
this.allowedProtocols}) {
this.allowedOrigins = const [],
this.allowedProtocols = const []}) {
serializer ??= json.encode;
deserializer ??= (params) => params;
}
@ -161,7 +161,7 @@ class AngelWebSocket {
if (e.service.configuration.containsKey('ws:filter')) {
return e.service.configuration['ws:filter'](e, socket);
} else if (e.params != null && e.params!.containsKey('ws:filter')) {
return e.params!['ws:filter'](e, socket);
return e.params?['ws:filter'](e, socket);
} else {
return true;
}
@ -175,13 +175,15 @@ class AngelWebSocket {
Future<void> batchEvent(WebSocketEvent event,
{Function(WebSocketContext socket)? filter, bool notify = true}) async {
// Default implementation will just immediately fire events
_clients.forEach((client) async {
for (var client in _clients) {
dynamic result = true;
if (filter != null) result = await filter(client);
if (filter != null) {
result = await filter(client);
}
if (result == true) {
client.channel.sink.add((serializer ?? json.encode)(event.toJson()));
}
});
}
if (synchronizationChannel != null && notify != false) {
synchronizationChannel!.sink.add(event);
@ -200,11 +202,11 @@ class AngelWebSocket {
return null;
}
var service = app!.findService(split[0]);
var service = app.findService(split[0]);
if (service == null) {
socket.sendError(AngelHttpException.notFound(
message: 'No service \"${split[0]}\" exists.'));
message: 'No service "${split[0]}" exists.'));
return null;
}
@ -222,7 +224,7 @@ class AngelWebSocket {
var params = mergeMap<String, dynamic>([
(((deserializer ?? (params) => params)(action.params))
as Map<String, dynamic>?)!,
as Map<String, dynamic>),
{
'provider': Providers.websocket,
'__requestctx': socket.request,
@ -257,7 +259,7 @@ class AngelWebSocket {
data: await service.remove(action.id, params));
} else {
socket.sendError(AngelHttpException.methodNotAllowed(
message: 'Method Not Allowed: \"$actionName\"'));
message: 'Method Not Allowed: $actionName'));
return null;
}
} catch (e, st) {
@ -278,7 +280,7 @@ class AngelWebSocket {
AuthToken token;
token = AuthToken.validate(jwt, auth.hmac);
var user = await auth.deserializer(token.userId as Object);
var user = await auth.deserializer(token.userId);
socket.request
..container!.registerSingleton<AuthToken>(token)
..container!.registerSingleton(user, as: user.runtimeType);
@ -359,16 +361,16 @@ class AngelWebSocket {
// Send an error
if (e is AngelHttpException) {
socket.sendError(e);
app?.logger?.severe(e.message, e.error ?? e, e.stackTrace);
app.logger?.severe(e.message, e.error ?? e, e.stackTrace);
} else if (sendErrors) {
var err = AngelHttpException(e,
message: e.toString(), stackTrace: st, errors: [st.toString()]);
socket.sendError(err);
app?.logger?.severe(err.message, e, st);
app.logger?.severe(err.message, e, st);
} else {
var err = AngelHttpException(e);
socket.sendError(err);
app?.logger?.severe(e.toString(), e, st);
app.logger?.severe(e.toString(), e, st);
}
}
@ -413,7 +415,7 @@ class AngelWebSocket {
/// Handles an incoming [WebSocketContext].
Future<void> handleClient(WebSocketContext socket) async {
var origin = socket.request.headers?.value('origin');
if (allowedOrigins != null && !allowedOrigins!.contains(origin)) {
if (allowedOrigins.isNotEmpty && !allowedOrigins.contains(origin)) {
throw AngelHttpException.forbidden(
message:
'WebSocket connections are not allowed from the origin "$origin".');
@ -479,8 +481,8 @@ class AngelWebSocket {
throw AngelHttpException.badRequest(
message: 'Missing `sec-websocket-key` header.');
} else if (protocol != null &&
allowedProtocols != null &&
!allowedProtocols!.contains(protocol)) {
allowedProtocols.isNotEmpty &&
!allowedProtocols.contains(protocol)) {
throw AngelHttpException.badRequest(
message: 'Disallowed `sec-websocket-protocol` header "$protocol".');
} else {

View file

@ -1,13 +1,13 @@
name: angel3_websocket
version: 4.1.0
version: 4.1.1
description: This library provides WebSockets support for Angel3 framework.
homepage: https://angel3-framework.web.app/
repository: https://github.com/dukefirehawk/angel/tree/master/packages/websocket
environment:
sdk: '>=2.12.0 <3.0.0'
dependencies:
angel3_auth: ^4.0.0
angel3_client: ^4.0.0
angel3_auth: ^4.1.0
angel3_client: ^4.1.0
angel3_framework: ^4.2.0
angel3_http_exception: ^3.0.0
belatuk_merge_map: ^3.0.0
@ -22,7 +22,7 @@ dev_dependencies:
angel3_model: ^3.1.0
test: ^1.17.5
lints: ^1.0.0
#dependency_overrides:
# angel3_container:
# path: ../container/angel_container
dependency_overrides:
angel3_auth:
path: ../auth

View file

@ -9,7 +9,7 @@ import 'package:test/test.dart';
import 'common.dart';
void main() {
srv.Angel? app;
srv.Angel app;
late srv.AngelHttp http;
ws.WebSockets? client;
srv.AngelWebSocket websockets;
@ -18,17 +18,17 @@ void main() {
setUp(() async {
app = srv.Angel(reflector: const MirrorsReflector());
http = srv.AngelHttp(app!, useZone: false);
http = srv.AngelHttp(app, useZone: false);
websockets = srv.AngelWebSocket(app)
..onData.listen((data) {
print('Received by server: $data');
});
await app!.configure(websockets.configureServer);
app!.all('/ws', websockets.handleRequest);
await app!.configure(GameController(websockets).configureServer);
app!.logger = Logger('angel_auth')..onRecord.listen(print);
await app.configure(websockets.configureServer);
app.all('/ws', websockets.handleRequest);
await app.configure(GameController(websockets).configureServer);
app.logger = Logger('angel_auth')..onRecord.listen(print);
server = await http.startServer();
url = 'ws://${server!.address.address}:${server!.port}/ws';
@ -53,7 +53,7 @@ void main() {
tearDown(() async {
await client!.close();
await http.close();
app = null;
//app = null;
client = null;
server = null;
url = null;

View file

@ -9,7 +9,7 @@ import 'package:test/test.dart';
import 'common.dart';
void main() {
srv.Angel? app;
srv.Angel app;
late srv.AngelHttp http;
ws.WebSockets? client;
srv.AngelWebSocket websockets;
@ -19,16 +19,16 @@ void main() {
setUp(() async {
app = srv.Angel(reflector: MirrorsReflector())
..use('/api/todos', TodoService());
http = srv.AngelHttp(app!, useZone: false);
http = srv.AngelHttp(app, useZone: false);
websockets = srv.AngelWebSocket(app)
..onData.listen((data) {
print('Received by server: $data');
});
await app!.configure(websockets.configureServer);
app!.all('/ws', websockets.handleRequest);
app!.logger = Logger('angel_auth')..onRecord.listen(print);
await app.configure(websockets.configureServer);
app.all('/ws', websockets.handleRequest);
app.logger = Logger('angel_auth')..onRecord.listen(print);
server = await http.startServer();
url = 'ws://${server!.address.address}:${server!.port}/ws';
@ -51,7 +51,7 @@ void main() {
await client!.close();
await http.server!.close(force: true);
app = null;
//app = null;
client = null;
server = null;
url = null;