Skip to content

Commit db08fdd

Browse files
feat: auth context api proposal (#879)
Co-authored-by: Renan <[email protected]>
1 parent 67419c5 commit db08fdd

File tree

5 files changed

+451
-199
lines changed

5 files changed

+451
-199
lines changed

examples/basic_authentication/routes/users/_middleware.dart

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,23 +2,20 @@ import 'package:basic_authentication/user_repository.dart';
22
import 'package:dart_frog/dart_frog.dart';
33
import 'package:dart_frog_auth/dart_frog_auth.dart';
44

5-
Future<User?> Function(String, String) userFromCredentials(
6-
UserRepository repository,
7-
) =>
8-
(String username, String password) =>
9-
repository.userFromCredentials(username, password);
10-
115
Handler middleware(Handler handler) {
126
final userRepository = UserRepository();
137

148
return handler
15-
.use(requestLogger())
16-
.use(provider<UserRepository>((_) => userRepository))
179
.use(
1810
basicAuthentication<User>(
19-
userFromCredentials: userFromCredentials(userRepository),
11+
authenticator: (context, username, password) {
12+
final repository = context.read<UserRepository>();
13+
return repository.userFromCredentials(username, password);
14+
},
2015
applies: (RequestContext context) async =>
2116
context.request.method != HttpMethod.post,
2217
),
23-
);
18+
)
19+
.use(requestLogger())
20+
.use(provider<UserRepository>((_) => userRepository));
2421
}

examples/bearer_authentication/routes/users/_middleware.dart

Lines changed: 12 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -3,31 +3,23 @@ import 'package:bearer_authentication/user_repository.dart';
33
import 'package:dart_frog/dart_frog.dart';
44
import 'package:dart_frog_auth/dart_frog_auth.dart';
55

6-
Future<User?> Function(String) userFromToken({
7-
required UserRepository userRepository,
8-
required SessionRepository sessionRepository,
9-
}) =>
10-
(String token) async {
11-
final session = await sessionRepository.sessionFromToken(token);
12-
return session != null ? userRepository.userFromId(session.userId) : null;
13-
};
14-
156
Handler middleware(Handler handler) {
16-
final userRepository = UserRepository();
17-
const sessionRepository = SessionRepository();
18-
197
return handler
20-
.use(requestLogger())
21-
.use(provider<UserRepository>((_) => userRepository))
22-
.use(provider<SessionRepository>((_) => sessionRepository))
238
.use(
249
bearerAuthentication<User>(
25-
userFromToken: userFromToken(
26-
userRepository: userRepository,
27-
sessionRepository: sessionRepository,
28-
),
10+
authenticator: (context, token) async {
11+
final sessionRepository = context.read<SessionRepository>();
12+
final userRepository = context.read<UserRepository>();
13+
final session = await sessionRepository.sessionFromToken(token);
14+
return session != null
15+
? userRepository.userFromId(session.userId)
16+
: null;
17+
},
2918
applies: (RequestContext context) async =>
3019
context.request.method != HttpMethod.post,
3120
),
32-
);
21+
)
22+
.use(requestLogger())
23+
.use(provider<UserRepository>((_) => UserRepository()))
24+
.use(provider<SessionRepository>((_) => const SessionRepository()));
3325
}

packages/dart_frog_auth/.gitignore

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,6 @@
44
.dart_tool/
55
.packages
66
build/
7-
pubspec.lock
7+
pubspec.lock
8+
9+
coverage

packages/dart_frog_auth/lib/src/dart_frog_auth.dart

Lines changed: 76 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ Future<bool> _defaultApplies(RequestContext context) async => true;
3737
/// ```
3838
///
3939
/// In order to use this middleware, you must provide a function that will
40-
/// return a user object from the username and password.
40+
/// return a user object from the username, password and request context.
4141
///
4242
/// If the given function returns null for the given username and password,
4343
/// the middleware will return a `401 Unauthorized` response.
@@ -47,27 +47,53 @@ Future<bool> _defaultApplies(RequestContext context) async => true;
4747
/// [RequestContext]. If the function returns false, the middleware will not
4848
/// apply to the route and the call will have authentication validation.
4949
Middleware basicAuthentication<T extends Object>({
50-
required Future<T?> Function(String, String) userFromCredentials,
50+
@Deprecated(
51+
'Deprecated in favor of authenticator. '
52+
'This will be removed in future versions',
53+
)
54+
Future<T?> Function(
55+
String username,
56+
String password,
57+
)? userFromCredentials,
58+
Future<T?> Function(
59+
RequestContext context,
60+
String username,
61+
String password,
62+
)? authenticator,
5163
Applies applies = _defaultApplies,
52-
}) =>
53-
(handler) => (context) async {
54-
if (!await applies(context)) {
55-
return handler(context);
64+
}) {
65+
assert(
66+
userFromCredentials != null || authenticator != null,
67+
'You must provide either a userFromCredentials or a '
68+
'authenticator function',
69+
);
70+
return (handler) => (context) async {
71+
if (!await applies(context)) {
72+
return handler(context);
73+
}
74+
75+
Future<T?> call(String username, String password) async {
76+
if (userFromCredentials != null) {
77+
return userFromCredentials(username, password);
78+
} else {
79+
return authenticator!(context, username, password);
5680
}
81+
}
5782

58-
final authorization = context.request.headers.basic();
59-
if (authorization != null) {
60-
final [username, password] =
61-
String.fromCharCodes(base64Decode(authorization)).split(':');
83+
final authorization = context.request.headers.basic();
84+
if (authorization != null) {
85+
final [username, password] =
86+
String.fromCharCodes(base64Decode(authorization)).split(':');
6287

63-
final user = await userFromCredentials(username, password);
64-
if (user != null) {
65-
return handler(context.provide(() => user));
66-
}
88+
final user = await call(username, password);
89+
if (user != null) {
90+
return handler(context.provide(() => user));
6791
}
92+
}
6893

69-
return Response(statusCode: HttpStatus.unauthorized);
70-
};
94+
return Response(statusCode: HttpStatus.unauthorized);
95+
};
96+
}
7197

7298
/// Authentication that uses the `Authorization` header with the `Bearer`
7399
/// scheme.
@@ -80,7 +106,7 @@ Middleware basicAuthentication<T extends Object>({
80106
/// The token format is up to the user. Usually it will be an encrypted token.
81107
///
82108
/// In order to use this middleware, you must provide a function that will
83-
/// return a user object from the received token.
109+
/// return a user object from the received token and request context.
84110
///
85111
/// If the given function returns null for the given username and password,
86112
/// the middleware will return a `401 Unauthorized` response.
@@ -90,21 +116,41 @@ Middleware basicAuthentication<T extends Object>({
90116
/// [RequestContext]. If the function returns false, the middleware will not
91117
/// apply to the route and the call will have no authentication validation.
92118
Middleware bearerAuthentication<T extends Object>({
93-
required Future<T?> Function(String) userFromToken,
119+
@Deprecated(
120+
'Deprecated in favor of authenticator. '
121+
'This will be removed in future versions',
122+
)
123+
Future<T?> Function(String token)? userFromToken,
124+
Future<T?> Function(RequestContext context, String token)? authenticator,
94125
Applies applies = _defaultApplies,
95-
}) =>
96-
(handler) => (context) async {
97-
if (!await applies(context)) {
98-
return handler(context);
126+
}) {
127+
assert(
128+
userFromToken != null || authenticator != null,
129+
'You must provide either a userFromToken or a '
130+
'authenticator function',
131+
);
132+
133+
return (handler) => (context) async {
134+
if (!await applies(context)) {
135+
return handler(context);
136+
}
137+
138+
Future<T?> call(String token) async {
139+
if (userFromToken != null) {
140+
return userFromToken(token);
141+
} else {
142+
return authenticator!(context, token);
99143
}
144+
}
100145

101-
final authorization = context.request.headers.bearer();
102-
if (authorization != null) {
103-
final user = await userFromToken(authorization);
104-
if (user != null) {
105-
return handler(context.provide(() => user));
106-
}
146+
final authorization = context.request.headers.bearer();
147+
if (authorization != null) {
148+
final user = await call(authorization);
149+
if (user != null) {
150+
return handler(context.provide(() => user));
107151
}
152+
}
108153

109-
return Response(statusCode: HttpStatus.unauthorized);
110-
};
154+
return Response(statusCode: HttpStatus.unauthorized);
155+
};
156+
}

0 commit comments

Comments
 (0)