feat(auth): implement dual-flow authentication (web + mobile)
Add complete authentication system supporting both web (NPM forward auth) and mobile (OIDC) authentication flows. Web flow: - Check /auth/me on startup to detect NPM forward auth session - Cookies handled by proxy, no Bearer tokens needed Mobile flow: - flutter_appauth for OIDC Authorization Code + PKCE - POST /auth/sync to get user profile and roles - Token storage in SharedPreferences Shared: - Permission system with Domain/Action enums and Role class - PermissionGate and AdminGate widgets for UI permission checks - Route guards redirecting unauthenticated users to login - Login page with platform-specific messaging Platform config: - iOS: CFBundleURLTypes for net.schweitz.tatlock:// - Android: appAuthRedirectScheme, minSdk 23 Docs: - Added Freezed 3.x sealed class documentation 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.5
parent
806b0a98c8
commit
f1b2b0430f
@@ -1,18 +1,46 @@
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
|
||||
import 'permissions.dart';
|
||||
import 'user_preferences.dart';
|
||||
|
||||
part 'auth_state.freezed.dart';
|
||||
|
||||
/// Authentication state.
|
||||
/// Authentication state including user profile, roles, and preferences.
|
||||
@freezed
|
||||
sealed class AuthState with _$AuthState {
|
||||
const factory AuthState({
|
||||
/// Whether user is authenticated.
|
||||
@Default(false) bool isAuthenticated,
|
||||
|
||||
/// OIDC access token.
|
||||
String? accessToken,
|
||||
|
||||
/// OIDC refresh token.
|
||||
String? refreshToken,
|
||||
|
||||
/// Token expiration time.
|
||||
DateTime? expiresAt,
|
||||
|
||||
/// Internal user ID (from core-api).
|
||||
String? userId,
|
||||
|
||||
/// Authentik user ID.
|
||||
String? authentikId,
|
||||
|
||||
/// User display name.
|
||||
String? userName,
|
||||
|
||||
/// User email address.
|
||||
String? userEmail,
|
||||
|
||||
/// User avatar URL.
|
||||
String? avatarUrl,
|
||||
|
||||
/// User's permission roles.
|
||||
@Default([]) List<Role> roles,
|
||||
|
||||
/// User preferences.
|
||||
UserPreferences? preferences,
|
||||
}) = _AuthState;
|
||||
|
||||
const AuthState._();
|
||||
@@ -23,4 +51,12 @@ sealed class AuthState with _$AuthState {
|
||||
// Consider expired if less than 1 minute remaining
|
||||
return DateTime.now().isAfter(expiresAt!.subtract(const Duration(minutes: 1)));
|
||||
}
|
||||
|
||||
/// Check if user has the specified permission.
|
||||
bool hasPermission(Domain domain, Action action, {String category = 'general'}) {
|
||||
return roles.hasPermission(domain, action, category: category);
|
||||
}
|
||||
|
||||
/// Check if user is a global admin.
|
||||
bool get isGlobalAdmin => roles.isGlobalAdmin;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user