Auth Controller
AuthController is een abstracte klasse die Controller uitbreidt. Het biedt het authenticatie- en autorisatiecontract voor het beveiligen van routes. Je implementeert het één keer per applicatie en koppelt het aan elke route die inloggen vereist.
Abstracte methoden
| Methode | Wanneer wordt dit uitgevoerd | Wat moet worden geretourneerd |
|---|---|---|
auth() |
Bij elk verzoek naar een beveiligde route | true om toe te staan, false om te weigeren (en het antwoord zelf af te handelen) |
authApi() |
Bij verzoeken naar beveiligde /api/-endpoints |
true om toe te staan |
checkLogin() |
Wordt aangeroepen door auth() om de sessie te valideren |
Een record ({bool success, String message, T? user}) |
checkPermission() |
Nadat auth() is geslaagd, controleert dit route.permissions |
true als de gebruiker de vereiste permissie heeft |
loginPost() |
Handelt POST naar de inlogformulierroute af | HTML-antwoord |
register() |
Handelt de inzending van het registratieformulier af, als je app zelfregistratie ondersteunt | HTML-antwoord |
newUser() |
Maakt een nieuw gebruikersaccount aan (bijv. vanuit een adminpaneel) | HTML-antwoord |
logout() |
Handelt de uitlogroute af | Doorverwijzing of HTML |
updateAuth(email, password, user) |
Na succesvolle login | Sessie/cookie opslaan |
removeAuth() |
Bij uitloggen | Sessie/cookie wissen |
index() |
Overgeërfd van Controller — de standaardhandler als de route geen eigen index-/controller-actie heeft |
Wat deze route ook moet tonen |
Als je app geen registratie nodig heeft, is het gebruikelijk om register()/newUser() een UnimplementedError() te laten werpen, zoals het voorbeeldproject doet — zie het voorbeeld hieronder.
Volledig voorbeeld
Dit is de auth-controller uit het Finch-voorbeeldproject:
import 'package:finch/finch_route.dart';
import 'package:finch/finch_tools.dart';
import '../models/mock_user_model.dart';
import '../route/web_route.dart';
class AppAuthController extends AuthController<MockUserModel> {
MockUserModel? userLogined;
@override
Future<bool> auth() async {
var res = await checkLogin();
if (!res.success) {
// API-verzoeken krijgen een JSON 403, webverzoeken krijgen een doorverwijzing
if (rq.isApiEndpoint) {
await rq.renderError(
403,
toData: true,
params: {'message': 'Please login.', 'success': false},
);
} else {
await rq.redirect('/example/form');
}
return false;
}
updateAuth(res.user!.email, res.user!.password, res.user!);
return true;
}
@override
Future<bool> authApi() async {
var auth = rq.authorization;
var mockUser = MockUserModel();
if (auth.type == AuthType.basic) {
String email = auth.getBasicUsername();
String password = auth.getBasicPassword();
return email == mockUser.email && password == mockUser.password;
} else if (auth.type == AuthType.bearer) {
return auth.value == '${mockUser.email} ${mockUser.password}';
}
return false;
}
@override
Future<({bool success, String message, MockUserModel? user})>
checkLogin() async {
var mockUser = MockUserModel();
var userSession = rq.getSession('user', def: '');
if (userSession == mockUser.email) {
return (success: true, message: 'Success.', user: mockUser);
}
return (success: false, message: 'Please login.', user: mockUser);
}
@override
Future<bool> checkPermission() async {
if (rq.route == null || userLogined == null) return false;
var permission = userLogined!.permission;
if (rq.route!.permissions.isNotEmpty &&
!rq.route!.permissions.contains(permission)) {
return false;
}
return true;
}
@override
Future<String> loginPost() async {
var formLogin = LoginForm();
await formLogin.check(
onInvalid: (p0) {},
onValid: (p0) {
var mockUser = MockUserModel();
var email = formLogin.get<String>('email', def: '');
var password = formLogin.get<String>('password', def: '');
if (email == mockUser.email && password == mockUser.password) {
updateAuth(email, password, mockUser);
} else {
rq.addParam('errorLogin', 'form.validation.loginError'.tr);
}
},
);
return homeController.renderView('example/form');
}
@override
Future<String> logout() {
removeAuth();
return rq.redirect('/example/form');
}
@override
Future<String> newUser() => throw UnimplementedError();
@override
Future<String> register() => throw UnimplementedError();
@override
void removeAuth() {
rq.session.remove('user');
rq.removeCookie('user');
userLogined = null;
}
@override
void updateAuth(String email, String password, MockUserModel user) {
userLogined = user;
rq.addSession('user', email);
}
}
Koppelen aan een route
final authController = AppAuthController();
FinchRoute(
key: 'example.panel',
path: 'panel',
methods: Methods.ALL,
auth: authController,
permissions: ['admin'],
index: homeController.exampleAuth,
),
Verzoekstroom voor een beveiligde route:
- Route komt overeen →
auth()wordt aangeroepen → als ditfalseretourneert, stopt het verzoekcheckPermission()wordt aangeroepen → als ditfalseretourneert, stopt het verzoek- Controller-/index-handler wordt uitgevoerd
Authorization-header
Voor API-routes lees je de Authorization-header via rq.authorization:
var auth = rq.authorization;
if (auth.type == AuthType.bearer) {
String token = auth.value;
// valideer het token
}
if (auth.type == AuthType.basic) {
String user = auth.getBasicUsername();
String pass = auth.getBasicPassword();
// valideer de inloggegevens
}
AuthType declareert ook de waarden digest, hawk, aws en akamai met het oog op toekomstige compatibiliteit, maar op dit moment worden alleen basic en bearer daadwerkelijk geparsed — elk ander schema (of een ontbrekende/onjuist gevormde header) resulteert in AuthType.none.
Permissies
Permissions is een kleine klasse met vooraf gedefinieerde permissiestring-constanten die je kunt gebruiken in plaats van overal strings hard te coderen:
class Permissions {
static final String none = 'none';
static final String superAdmin = 'super-admin';
}
FinchRoute(
key: 'admin.panel',
path: 'admin/panel',
auth: authController,
permissions: [Permissions.superAdmin],
index: adminController.panel,
),
Het definieert alleen deze twee constanten — voor alles buiten none/super-admin definieer je je eigen permissiestrings (of breid je Permissions uit) en controleer je ze op dezelfde manier binnen checkPermission(), zoals de AppAuthController.checkPermission() van het voorbeeldproject hierboven doet.