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>
63 lines
1.5 KiB
Dart
63 lines
1.5 KiB
Dart
import 'package:freezed_annotation/freezed_annotation.dart';
|
|
|
|
import 'permissions.dart';
|
|
import 'user_preferences.dart';
|
|
|
|
part 'auth_state.freezed.dart';
|
|
|
|
/// 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._();
|
|
|
|
/// Check if token is expired or will expire soon.
|
|
bool get isTokenExpired {
|
|
if (expiresAt == null) return true;
|
|
// 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;
|
|
}
|