Repository navigation
Authentication Flow
Our authentication system is designed to support multiple login methods (standard credentials and federated login via OpenID Connect) while providing a robust mechanism for session persistence and automatic token refreshing.
The authentication flow is built upon three core abstractions located in lib/session/flows/base/:
-
SessionInitiator: Represents the intent to login. It collects necessary user input (like username/password or an OIDC token) and prepares a request. -
SessionRequest: Represents the action of authenticating. It executes the network calls to exchange credentials for a session. -
Session: Represents the state of being logged in. It holds the authentication artifacts (Cookies, Tokens, User metadata).
Located in lib/session/flows/base/initiator.dart.
The UI (login.dart) does not perform login logic directly. Instead, it creates a specific SessionInitiator based on the user's choice:
-
CredentialsSessionInitiator: Takesusernameandpassword. -
FederatedSessionInitiator: Takes an OIDCclientIdandrealm.
// Example usage in login.dart
final initiator = CredentialsSessionInitiator(
username: user,
password: pass,
);
await sessionNotifier.login(initiator, persistentSession: true);The initiator produces a SessionRequest. When request.perform() is called, the app communicates with the Sigarra API or the Identity Provider.
- Credentials Flow: Posts the username/password to the legacy HTML endpoints or API to get session cookies.
-
Federated Flow: Uses the
openid_clientlibrary to perform the OAuth2 handshake.
If successful, a Session object is returned. This is the object that is serialized to local storage for "Remember Me" functionality. It MUST implement:
-
mainFaculty: The primary faculty of the user. -
cookies: The HTTP cookies required for authenticated requests. -
createRefreshRequest(): A crucial method that allows theAuthenticationControllerto re-login the user silently when their session expires.
The AuthenticationController (lib/session/authentication_controller.dart) is the brain of the operation. It sits between the raw Session data and the rest of the application.
Sigarra sessions often expire or require refreshing. The controller ensures that:
-
Concurrency Safety: Prevents multiple parts of the app from trying to refresh the session simultaneously (using
synchronizedlocks). -
Snapshots: Provides
AuthenticationSnapshots to API clients. A snapshot guarantees that the session is valid at the moment it was requested. -
Automatic Invalidation: If a network request fails with a 401/403, the controller automatically triggers
_reauthenticate().
-
Access: An API fetcher requests
controller.snapshot. -
Usage: The fetcher uses
snapshot.sessionto make a request. -
Failure: If the request fails due to
AuthenticationException, the fetcher callssnapshot.invalidate(). -
Recovery: The controller acquires a lock, calls
currentSession.createRefreshRequest(), performs the handshake again, and notifies all listeners with the new valid session.
The sessionProvider (Riverpod) acts as the glue.
- It holds the current
Session?state. - It initializes the
NetworkRouterwith theAuthenticationControllerupon login. - It persists the session to
SharedPreferencesifpersistentSessionis true.
Flow Summary:
UI -> Initiator -> SessionNotifier.login() -> Request.perform() -> Session -> AuthenticationController initialized.
uni by NIAEFEUP
Need help? Open an Issue | Read Contributing Guidelines
Licensed under the GPL-3.0 License.