Flutter SDK for authenticating Croatian users with their electronic ID card (eOsobna) via Certilia / NIAS. Works on iOS, Android, and Web.
Flutter SDK za autentifikaciju hrvatskih korisnika preko elektroničke osobne iskaznice (eOsobna) kroz Certiliju / NIAS. Radi na iOS-u, Androidu i Webu.
The SDK is proxy-only. The Flutter client never talks to Certilia
directly — all OAuth communication is mediated by a backend
(certilia-server, included in this repo) that holds the OAuth
credentials.
flowchart LR
A[Flutter client<br/>flutter_certilia] -->|HTTPS| B[Your proxy<br/>certilia-server]
B -->|OAuth 2.0| C[Certilia IDP]
C -.-> B
B -.->|JWT, user| A
Direct integration was tried and abandoned — Certilia rejects custom
URL schemes (blocking AppAuth on mobile) and Certilia's userinfo
endpoint requires server-side fallbacks to be reliable in production.
See REFACTOR_PLAN.md for the full history.
The auth flow differs slightly by platform:
sequenceDiagram
autonumber
participant App as Flutter app
participant SDK as flutter_certilia
participant Proxy as certilia-server
participant IDP as Certilia IDP
App->>SDK: authenticate(context)
SDK->>Proxy: GET /api/auth/initialize
Proxy-->>SDK: authorization_url, state, session_id
alt Mobile / desktop
SDK->>SDK: open in-app WebView
SDK->>IDP: authorize (via WebView)
IDP-->>SDK: redirect to proxy callback with code
else Web
SDK->>Proxy: POST /api/auth/polling/start
SDK->>SDK: open popup window
SDK->>IDP: authorize (via popup)
IDP->>Proxy: GET /api/auth/callback
loop every 2s
SDK->>Proxy: GET /api/auth/polling/:id/status
end
Proxy-->>SDK: status: completed, code
end
SDK->>Proxy: POST /api/auth/exchange (code, state, session_id)
Proxy->>IDP: token exchange
IDP-->>Proxy: tokens
Proxy-->>SDK: accessToken, refreshToken, idToken, user
SDK-->>App: CertiliaUser
The 0.2.0 line is not yet on pub.dev. Use a git: or path: dep:
dependencies:
flutter_certilia:
git:
url: https://github.com/stepanic/flutter_certilia.git
ref: maindependencies:
flutter_certilia:
path: ../flutter_certiliaRequirements: Dart >=3.2.0, Flutter >=3.16.0.
The SDK has one entry point. The proxy URL is the only required value — everything else is sensible defaults you can override.
import 'package:flutter_certilia/flutter_certilia.dart';
final certilia = await CertiliaSDK.initialize(
serverUrl: const String.fromEnvironment(
'CERTILIA_SERVER_URL',
defaultValue: 'https://your-proxy.example',
),
scopes: const ['openid', 'profile', 'eid', 'email', 'offline_access'],
enableLogging: true,
);Drive the auth flow. The runtime type returned by initialize differs
between web and mobile, but the methods you'll call are the same:
final user = await certilia.authenticate(context); // popup / WebView
final isAuthed = await certilia.checkAuthenticationStatus();
final extended = await certilia.getExtendedUserInfo();
await certilia.refreshToken();
await certilia.logout();Override the proxy URL per build without touching code:
flutter run --dart-define=CERTILIA_SERVER_URL=https://your-proxy.exampleflutter_certilia ships API-only. There are no opinionated widgets
or themes — your app keeps full control of its design system.
example/lib/certilia_auth/ is a working
reference UI (login button, authenticated view, user-info cards, theme
toggle) that you can copy-paste and adapt.
Deprecated typedefs (CertiliaSDKSimple, CertiliaConfigSimple) are
kept for one minor release; they map directly to the new names and
will be removed in 1.0.0.
try {
await certilia.authenticate(context);
} on CertiliaAuthenticationException catch (e) {
// User cancelled or upstream rejected the flow
} on CertiliaNetworkException catch (e) {
// HTTP error reaching the proxy
print('Proxy returned ${e.statusCode}: ${e.message}');
} on CertiliaConfigurationException catch (e) {
// SDK misconfigured
}- iOS, Android: no scheme-registration needed — auth happens in an
in-app
WebView, and the proxy's HTTPS callback is what closes the loop, not a custom URL scheme. - Web: opens a popup against the proxy. The proxy's CORS config must allow your origin. The SDK does not send custom request headers from web for this reason (custom headers trigger preflight).
- Desktop:
webview_flutterdoes not ship a desktop backend; the SDK isn't tested on macOS/Windows/Linux. Add the appropriate platform plugin if you need it.
The certilia-server proxy that the SDK talks to is in this repo at
certilia-server/ — see its README for setup,
environment variables, and the supported endpoint contract.
- "Authentication was cancelled" — popup blocked on web (allow popups for your origin) or user closed the WebView. Check DevTools console / device logs.
CertiliaNetworkExceptionon/api/auth/initialize— the proxy URL is wrong, the proxy is down, or CORS is blocking your origin.enableLogging: trueplus the browser network tab will point at the actual failing request.- Logged in but
getCurrentUser()returns null right after hot restart — was a real bug pre-0.2.0; the constructor's init future is now awaited before any public method runs. If you still see it, file an issue.
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add some amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
MIT — see LICENSE.
