Authentication
Authentication is essential when developing an application. Serinus provides a powerful and flexible authentication system through the serinus_frontier package, which allows you to easily implement various authentication strategies, such as JWT, OAuth, or custom token-based authentication.
In this section, we will explore how to set up authentication in your Serinus application using the serinus_frontier package with a simple example of JWT authentication.
Creating an Authentication Module
We will start by generating a new module for authentication using the Serinus CLI:
serinus generate module auth
serinus generate controller auth
serinus generate provider authLet's also create a simple User model for demonstration purposes:
class User {
final String id;
final String username;
final String password;
User({required this.id, required this.username, required this.password});
}And let's also create the module and the provider that will handle users:
serinus generate module users
serinus generate provider usersNow for the sake of simplicity, we will use an in-memory user store. In a real application, you would typically use a database to store user information.
import 'package:serinus/serinus.dart';
class UsersProvider extends Provider{
final List<User> _users = [
User(id: '1', username: 'user1', password: 'password1'),
User(id: '2', username: 'user2', password: 'password2'),
];
User? findByUsername(String username) {
return _users.firstWhere((user) => user.username == username, orElse: () => null);
}
}Let's export the UsersProvider from the UsersModule:
import 'package:serinus/serinus.dart';
import 'users_provider.dart';
class UsersModule extends Module {
UsersModule() : super(
providers: [UsersProvider()],
exports: [UsersProvider],
);
}Now we can set up the AuthModule to use the UsersProvider and configure the JWT authentication strategy.
import 'package:serinus/serinus.dart';
import 'package:serinus_frontier/serinus_frontier.dart';
import '../users/users_module.dart';
class AuthModule extends Module {
AuthModule() : super(
imports: [UsersModule()],
providers: [
Provider.composed<AuthProvider>(
(CompositionContext context) => AuthProvider(context.use<UsersProvider>()),
inject: [UsersProvider],
)
],
controllers: [AuthController()],
);
}Implementing the Authentication Logic
Next, we will implement the AuthProvider that will handle the authentication logic, including validating user credentials and generating JWT tokens.
import 'package:serinus/serinus.dart';
import 'package:serinus_frontier/serinus_frontier.dart';
import '../users/users_provider.dart';
class AuthProvider extends Provider {
final UsersProvider _usersProvider;
AuthProvider(this._usersProvider);
Map<String, dynamic> authenticate(Map<String, dynamic> credentials) {
final user = _usersProvider.findByUsername(credentials['username']);
if (user == null || user.password != credentials['password']) {
throw Exception('Invalid credentials');
}
final token = _generateToken(user);
return {
'token': token,
};
}
String _generateToken(User user) {
final jwt = JWT(
{
'id': user.id,
'username': user.username,
},
issuer: 'serinus',
);
return jwt.sign(SecretKey('default_secret'));
}
}Finally, we will implement the AuthController to expose an endpoint for authentication:
import 'package:serinus/serinus.dart';
class AuthController extends Controller {
final AuthProvider _authProvider;
AuthController(this._authProvider) {
on(Route.post('/login'), _login);
}
Future<Map<String, dynamic>> _login(RequestContext context) async {
final credentials = await context.bodyAs<Map<String, dynamic>>();
final authResult = _authProvider.authenticate(credentials);
return authResult;
}
}With this setup, you can now send a POST request to /auth/login with a JSON body containing the username and password to receive a JWT token if the credentials are valid.
This is just a basic example to get you started with authentication in Serinus. The serinus_frontier package provides many more features and options for customizing your authentication strategy, such as token expiration, refresh tokens, and support for different authentication schemes. Be sure to check the documentation for more details on how to use these features effectively in your application.
