digital_login_sdk
Sign users in with Azerbaijan DigitalLogin (login.gov.az) in one call.
Supports browser sign-in and the mygov app.
Unofficial package. You need a
client_idand a registered redirect URI from DigitalLogin.
How it works
authorize()opens DigitalLogin in a secure system browser sheet.- The user signs in (in the browser or in the mygov app).
- The SDK validates the response and returns an authorization code.
- You send the code to your backend, which exchanges it for tokens. The client secret never ships in the app.
Requirements
| Minimum | |
|---|---|
| Flutter | 3.38 |
| Android | API 24 |
| iOS | 13.0 |
No runtime permissions are needed on either platform.
1. Install
flutter pub add digital_login_sdk
2. Choose a redirect URI
Use the redirect URI registered for your client, for example:
myapp://digitallogin
myappis the scheme: lowercase, unique to your app.digitalloginis the host.
Replace both values in the snippets below with your own.
3. Android setup
In android/app/src/main/AndroidManifest.xml, add this activity inside
<application>:
<activity
android:name="com.linusu.flutter_web_auth_2.CallbackActivity"
android:exported="true"
android:taskAffinity="">
<intent-filter android:label="digital_login_sdk">
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="myapp" android:host="digitallogin" />
</intent-filter>
</activity>
⚠️ Remove any other
<intent-filter>for the same scheme, for example one onMainActivityadded forapp_linksoruni_links. Otherwise the redirect will not reach the SDK.
4. iOS setup
In ios/Runner/Info.plist, add the scheme inside the top-level <dict>:
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLName</key>
<string>digitallogin</string>
<key>CFBundleURLSchemes</key>
<array>
<string>myapp</string>
</array>
</dict>
</array>
The mygov app uses this entry to return the user to your app.
5. Sign in
import 'package:digital_login_sdk/digital_login_sdk.dart';
final digitalLogin = DigitalLogin(
DigitalLoginConfig(
clientId: 'YOUR_CLIENT_ID',
redirectUri: Uri.parse('myapp://digitallogin'),
environment: DigitalLoginEnvironment.production, // or .test
),
);
Future<void> signIn() async {
try {
final result = await digitalLogin.authorize();
// Send to your backend:
// { "authorizationCode": "...", "redirectUri": "myapp://digitallogin" }
await api.post('/auth/digital-login', body: result.toJson());
} on DigitalLoginCancelledException {
// The user closed the login page. Usually no message is needed.
} on DigitalLoginException catch (e) {
// Show an error to the user.
}
}
Create DigitalLogin once (for example in your DI container) and reuse it.
Optional: ready-made button
DigitalLoginButton(onPressed: signIn, isLoading: isLoading)
Configuration
| Option | Default | Description |
|---|---|---|
clientId |
required | Client ID from DigitalLogin |
redirectUri |
required | Must match the registered URI exactly |
environment |
production |
production, test, or DigitalLoginEnvironment.custom(...) |
scopes |
openid user certificate session |
Requested permissions |
usePkce |
false |
Adds a PKCE challenge. Send result.codeVerifier to your backend |
useNonce |
false |
Adds an OIDC nonce. Verify result.nonce on your backend |
preferEphemeralSession |
false |
iOS: skips the "wants to use login.gov.az" prompt, but does not reuse browser cookies |
Errors
Every error is a DigitalLoginException:
| Exception | Meaning |
|---|---|
DigitalLoginCancelledException |
The user closed the login page |
DigitalLoginAuthorizationException |
DigitalLogin returned an error (e.isAccessDenied) |
DigitalLoginStateMismatchException |
The response failed the security check (state) |
DigitalLoginInvalidCallbackException |
The redirect was malformed or had no code |
DigitalLoginConfigurationException |
Invalid config, e.g. an http redirect URI |
DigitalLoginInProgressException |
authorize() is already running |
DigitalLoginPlatformException |
The platform could not open the login page |
Backend: exchange the code
Your backend calls the token endpoint
(DigitalLoginEnvironment.production.tokenEndpoint) with the code, your
client secret, and the same redirect URI:
POST https://apidigital.login.gov.az/ssoauth/oauth2/token
Content-Type: application/x-www-form-urlencoded
grant_type=authorization_code
&code=AUTHORIZATION_CODE
&redirect_uri=myapp://digitallogin
&client_id=YOUR_CLIENT_ID
&client_secret=YOUR_CLIENT_SECRET
Troubleshooting
| Symptom | Fix |
|---|---|
| Login page opens, but nothing happens after sign-in | The scheme and host in AndroidManifest.xml / Info.plist must match redirectUri exactly |
| Android shows an "Open with" chooser | Another intent filter uses the same scheme. Remove it (step 3) |
| iOS: nothing happens after the mygov app returns | Add CFBundleURLTypes (step 4) |
DigitalLoginConfigurationException |
redirectUri must be lowercase and must not use http |
Security
- Sign-in runs in the system browser, never in a WebView.
- Each request has a random 256-bit
state. The SDK rejects responses without it. - Codes are never logged. The client secret never touches the app.
See SECURITY.md to report a vulnerability.
License
MIT
Libraries
- digital_login_sdk
- Unofficial Flutter SDK for Azerbaijan DigitalLogin (login.gov.az).