Skip to content

Commit 1a606e5

Browse files
authored
Version reporting (#173)
1 parent 94f54d4 commit 1a606e5

10 files changed

Lines changed: 338 additions & 35 deletions

File tree

README.md

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,16 @@ The **DefGuard Mobile Client** is a secure, self-hosted **WireGuard VPN mobile c
55
This open-source, cross-platform VPN app supports easy QR code onboarding and flexible traffic routing to meet diverse secure remote access needs. DefGuard is part of a modular ecosystem built for VPN orchestration and identity management . Defguard provides a mobile VPN client with biometrics and TOTP.
66

77
## Key Features
8-
- Secure **WireGuard VPN mobile client** with **Multi-Factor Authentication (MFA)**
9-
- Internal SSO/OIDC support with biometrics, TOTP, email verification
10-
- External SSO support: Google, Okta, Microsoft EntraID, JumpCloud, and more
11-
- Quick and easy onboarding via secure **QR code VPN onboarding** or URL/token
12-
- Flexible traffic routing: all traffic via VPN or selective routing
13-
- Real-time synchronization of VPN configurations with the DefGuard server
14-
- Native **cross-platform VPN app** support for **Android VPN client** and iOS VPN client
15-
- Fully **self-hosted VPN solution** for ultimate privacy and control
16-
- Open-source codebase for transparency and customization
8+
9+
- Secure **WireGuard VPN mobile client** with **Multi-Factor Authentication (MFA)**
10+
- Internal SSO/OIDC support with biometrics, TOTP, email verification
11+
- External SSO support: Google, Okta, Microsoft EntraID, JumpCloud, and more
12+
- Quick and easy onboarding via secure **QR code VPN onboarding** or URL/token
13+
- Flexible traffic routing: all traffic via VPN or selective routing
14+
- Real-time synchronization of VPN configurations with the DefGuard server
15+
- Native **cross-platform VPN app** support for **Android VPN client** and iOS VPN client
16+
- Fully **self-hosted VPN solution** for ultimate privacy and control
17+
- Open-source codebase for transparency and customization
1718

1819
## Screenshots
1920

@@ -24,24 +25,26 @@ This open-source, cross-platform VPN app supports easy QR code onboarding and fl
2425
<img width="250" alt="Instance list" src="https://github.com/user-attachments/assets/8fb7c5ba-4d11-4452-997a-b2bd17b468d0" />
2526
<img width="250" alt="MFA defguard IdP" src="https://github.com/user-attachments/assets/d08d008e-22f2-4f14-9b1b-db6eccb3c3dc" />
2627

27-
2828
## Getting Started
2929

30-
You need to have a running [Defguard Server](https://github.com/DefGuard/defguard) to use the mobile app.
30+
You need to have a running [Defguard Core](https://github.com/DefGuard/defguard) to use the mobile app.
3131

3232
### Install the App
3333

3434
Join closed beta for iOS or Android.
3535

3636
#### Android
37+
3738
- Download from [Google Play](https://play.google.com/store/apps/details?id=net.defguard.mobile)
3839

3940
#### iOS
40-
- Available soon on the [App Store](https://testflight.apple.com/join/Jvdhkt7h)
4141

42-
Documentation available at : [https://docs.defguard.net/help/mobile-client](https://docs.defguard.net/help/mobile-client)
42+
- Available soon on the [App Store](https://apps.apple.com/us/app/defguard-vpn-client/id6748068630)
43+
44+
Documentation available at : [https://docs.defguard.net/using-defguard-for-end-users/mobile-client](https://docs.defguard.net/using-defguard-for-end-users/mobile-client)
4345

4446
## About DefGuard
45-
DefGuard is a comprehensive platform offering **secure remote access**, **identity management**, and VPN orchestration with a focus on security using **multi-factor authentication for VPN**.
4647

47-
Visit defguard.net for more information.
48+
DefGuard is a comprehensive platform offering **secure remote access**, **identity management**, and VPN orchestration with a focus on security using **multi-factor authentication for VPN**.
49+
50+
Visit defguard.net for more information.

client/ios/Podfile.lock

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -107,22 +107,22 @@ EXTERNAL SOURCES:
107107
:path: ".symlinks/plugins/wireguard_plugin/darwin"
108108

109109
SPEC CHECKSUMS:
110-
app_links: 585674be3c6661708e6cd794ab4f39fb9d8356f9
111-
device_info_plus: bf2e3232933866d73fe290f2942f2156cdd10342
110+
app_links: 3dbc685f76b1693c66a6d9dd1e9ab6f73d97dc0a
111+
device_info_plus: 21fcca2080fbcd348be798aa36c3e5ed849eefbe
112112
Flutter: cabc95a1d2626b1b06e7179b784ebcf0c0cde467
113-
flutter_local_notifications: ff50f8405aaa0ccdc7dcfb9022ca192e8ad9688f
114-
flutter_native_splash: df59bb2e1421aa0282cb2e95618af4dcb0c56c29
115-
flutter_secure_storage: d33dac7ae2ea08509be337e775f6b59f1ff45f12
116-
local_auth_darwin: 63c73d6d28cc3e239be2b6aa460ea6e317cd5100
117-
mobile_scanner: 77265f3dc8d580810e91849d4a0811a90467ed5e
118-
package_info_plus: c0502532a26c7662a62a356cebe2692ec5fe4ec4
119-
permission_handler_apple: 9878588469a2b0d0fc1e048d9f43605f92e6cec2
120-
share_plus: 8b6f8b3447e494cca5317c8c3073de39b3600d1f
121-
shared_preferences_foundation: 5086985c1d43c5ba4d5e69a4e8083a389e2909e6
113+
flutter_local_notifications: a5a732f069baa862e728d839dd2ebb904737effb
114+
flutter_native_splash: c32d145d68aeda5502d5f543ee38c192065986cf
115+
flutter_secure_storage: 1ed9476fba7e7a782b22888f956cce43e2c62f13
116+
local_auth_darwin: c3ee6cce0a8d56be34c8ccb66ba31f7f180aaebb
117+
mobile_scanner: 9157936403f5a0644ca3779a38ff8404c5434a93
118+
package_info_plus: af8e2ca6888548050f16fa2f1938db7b5a5df499
119+
permission_handler_apple: 4ed2196e43d0651e8ff7ca3483a069d469701f2d
120+
share_plus: 50da8cb520a8f0f65671c6c6a99b3617ed10a58a
121+
shared_preferences_foundation: 7036424c3d8ec98dfe75ff1667cb0cd531ec82bb
122122
sqlite3: 73513155ec6979715d3904ef53a8d68892d4032b
123-
sqlite3_flutter_libs: 7bea6d85399aebaeb54e4f9845dcac6f5033cf22
124-
url_launcher_ios: bb13df5870e8c4234ca12609d04010a21be43dfa
125-
wireguard_plugin: 4d2720563b180d23101f7162ecdbf57203e82e8e
123+
sqlite3_flutter_libs: 52ecc4dfaae71f496da86159263dbce5d23a051a
124+
url_launcher_ios: 7a95fa5b60cc718a708b8f2966718e93db0cef1b
125+
wireguard_plugin: c2f4d5382eecd7bcd07c027642c75e0569f91ff8
126126

127127
PODFILE CHECKSUM: ae9e65fc23486119b8e977fd41d9213f251e537a
128128

Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
// This is a generated file - do not edit.
2+
//
3+
// Generated from client_platform_info.proto.
4+
5+
// @dart = 3.3
6+
7+
// ignore_for_file: annotate_overrides, camel_case_types, comment_references
8+
// ignore_for_file: constant_identifier_names
9+
// ignore_for_file: curly_braces_in_flow_control_structures
10+
// ignore_for_file: deprecated_member_use_from_same_package, library_prefixes
11+
// ignore_for_file: non_constant_identifier_names, prefer_relative_imports
12+
13+
import 'dart:core' as $core;
14+
15+
import 'package:protobuf/protobuf.dart' as $pb;
16+
17+
export 'package:protobuf/protobuf.dart' show GeneratedMessageGenericExtensions;
18+
19+
class ClientPlatformInfo extends $pb.GeneratedMessage {
20+
factory ClientPlatformInfo({
21+
$core.String? osFamily,
22+
$core.String? osType,
23+
$core.String? version,
24+
$core.String? edition,
25+
$core.String? codename,
26+
$core.String? bitness,
27+
$core.String? architecture,
28+
}) {
29+
final result = create();
30+
if (osFamily != null) result.osFamily = osFamily;
31+
if (osType != null) result.osType = osType;
32+
if (version != null) result.version = version;
33+
if (edition != null) result.edition = edition;
34+
if (codename != null) result.codename = codename;
35+
if (bitness != null) result.bitness = bitness;
36+
if (architecture != null) result.architecture = architecture;
37+
return result;
38+
}
39+
40+
ClientPlatformInfo._();
41+
42+
factory ClientPlatformInfo.fromBuffer($core.List<$core.int> data,
43+
[$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) =>
44+
create()..mergeFromBuffer(data, registry);
45+
factory ClientPlatformInfo.fromJson($core.String json,
46+
[$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) =>
47+
create()..mergeFromJson(json, registry);
48+
49+
static final $pb.BuilderInfo _i = $pb.BuilderInfo(
50+
_omitMessageNames ? '' : 'ClientPlatformInfo',
51+
package: const $pb.PackageName(_omitMessageNames ? '' : 'defguard.proxy'),
52+
createEmptyInstance: create)
53+
..aOS(1, _omitFieldNames ? '' : 'osFamily')
54+
..aOS(2, _omitFieldNames ? '' : 'osType')
55+
..aOS(3, _omitFieldNames ? '' : 'version')
56+
..aOS(4, _omitFieldNames ? '' : 'edition')
57+
..aOS(5, _omitFieldNames ? '' : 'codename')
58+
..aOS(6, _omitFieldNames ? '' : 'bitness')
59+
..aOS(7, _omitFieldNames ? '' : 'architecture')
60+
..hasRequiredFields = false;
61+
62+
@$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
63+
ClientPlatformInfo clone() => deepCopy();
64+
@$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
65+
ClientPlatformInfo copyWith(void Function(ClientPlatformInfo) updates) =>
66+
super.copyWith((message) => updates(message as ClientPlatformInfo))
67+
as ClientPlatformInfo;
68+
69+
@$core.override
70+
$pb.BuilderInfo get info_ => _i;
71+
72+
@$core.pragma('dart2js:noInline')
73+
static ClientPlatformInfo create() => ClientPlatformInfo._();
74+
@$core.override
75+
ClientPlatformInfo createEmptyInstance() => create();
76+
@$core.pragma('dart2js:noInline')
77+
static ClientPlatformInfo getDefault() => _defaultInstance ??=
78+
$pb.GeneratedMessage.$_defaultFor<ClientPlatformInfo>(create);
79+
static ClientPlatformInfo? _defaultInstance;
80+
81+
@$pb.TagNumber(1)
82+
$core.String get osFamily => $_getSZ(0);
83+
@$pb.TagNumber(1)
84+
set osFamily($core.String value) => $_setString(0, value);
85+
@$pb.TagNumber(1)
86+
$core.bool hasOsFamily() => $_has(0);
87+
@$pb.TagNumber(1)
88+
void clearOsFamily() => $_clearField(1);
89+
90+
@$pb.TagNumber(2)
91+
$core.String get osType => $_getSZ(1);
92+
@$pb.TagNumber(2)
93+
set osType($core.String value) => $_setString(1, value);
94+
@$pb.TagNumber(2)
95+
$core.bool hasOsType() => $_has(1);
96+
@$pb.TagNumber(2)
97+
void clearOsType() => $_clearField(2);
98+
99+
@$pb.TagNumber(3)
100+
$core.String get version => $_getSZ(2);
101+
@$pb.TagNumber(3)
102+
set version($core.String value) => $_setString(2, value);
103+
@$pb.TagNumber(3)
104+
$core.bool hasVersion() => $_has(2);
105+
@$pb.TagNumber(3)
106+
void clearVersion() => $_clearField(3);
107+
108+
@$pb.TagNumber(4)
109+
$core.String get edition => $_getSZ(3);
110+
@$pb.TagNumber(4)
111+
set edition($core.String value) => $_setString(3, value);
112+
@$pb.TagNumber(4)
113+
$core.bool hasEdition() => $_has(3);
114+
@$pb.TagNumber(4)
115+
void clearEdition() => $_clearField(4);
116+
117+
@$pb.TagNumber(5)
118+
$core.String get codename => $_getSZ(4);
119+
@$pb.TagNumber(5)
120+
set codename($core.String value) => $_setString(4, value);
121+
@$pb.TagNumber(5)
122+
$core.bool hasCodename() => $_has(4);
123+
@$pb.TagNumber(5)
124+
void clearCodename() => $_clearField(5);
125+
126+
@$pb.TagNumber(6)
127+
$core.String get bitness => $_getSZ(5);
128+
@$pb.TagNumber(6)
129+
set bitness($core.String value) => $_setString(5, value);
130+
@$pb.TagNumber(6)
131+
$core.bool hasBitness() => $_has(5);
132+
@$pb.TagNumber(6)
133+
void clearBitness() => $_clearField(6);
134+
135+
@$pb.TagNumber(7)
136+
$core.String get architecture => $_getSZ(6);
137+
@$pb.TagNumber(7)
138+
set architecture($core.String value) => $_setString(6, value);
139+
@$pb.TagNumber(7)
140+
$core.bool hasArchitecture() => $_has(6);
141+
@$pb.TagNumber(7)
142+
void clearArchitecture() => $_clearField(7);
143+
}
144+
145+
const $core.bool _omitFieldNames =
146+
$core.bool.fromEnvironment('protobuf.omit_field_names');
147+
const $core.bool _omitMessageNames =
148+
$core.bool.fromEnvironment('protobuf.omit_message_names');
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// This is a generated file - do not edit.
2+
//
3+
// Generated from client_platform_info.proto.
4+
5+
// @dart = 3.3
6+
7+
// ignore_for_file: annotate_overrides, camel_case_types, comment_references
8+
// ignore_for_file: constant_identifier_names
9+
// ignore_for_file: curly_braces_in_flow_control_structures
10+
// ignore_for_file: deprecated_member_use_from_same_package, library_prefixes
11+
// ignore_for_file: non_constant_identifier_names, prefer_relative_imports
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
// This is a generated file - do not edit.
2+
//
3+
// Generated from client_platform_info.proto.
4+
5+
// @dart = 3.3
6+
7+
// ignore_for_file: annotate_overrides, camel_case_types, comment_references
8+
// ignore_for_file: constant_identifier_names
9+
// ignore_for_file: curly_braces_in_flow_control_structures
10+
// ignore_for_file: deprecated_member_use_from_same_package, library_prefixes
11+
// ignore_for_file: non_constant_identifier_names, prefer_relative_imports
12+
// ignore_for_file: unused_import
13+
14+
import 'dart:convert' as $convert;
15+
import 'dart:core' as $core;
16+
import 'dart:typed_data' as $typed_data;
17+
18+
@$core.Deprecated('Use clientPlatformInfoDescriptor instead')
19+
const ClientPlatformInfo$json = {
20+
'1': 'ClientPlatformInfo',
21+
'2': [
22+
{'1': 'os_family', '3': 1, '4': 1, '5': 9, '10': 'osFamily'},
23+
{'1': 'os_type', '3': 2, '4': 1, '5': 9, '10': 'osType'},
24+
{'1': 'version', '3': 3, '4': 1, '5': 9, '10': 'version'},
25+
{
26+
'1': 'edition',
27+
'3': 4,
28+
'4': 1,
29+
'5': 9,
30+
'9': 0,
31+
'10': 'edition',
32+
'17': true
33+
},
34+
{
35+
'1': 'codename',
36+
'3': 5,
37+
'4': 1,
38+
'5': 9,
39+
'9': 1,
40+
'10': 'codename',
41+
'17': true
42+
},
43+
{
44+
'1': 'bitness',
45+
'3': 6,
46+
'4': 1,
47+
'5': 9,
48+
'9': 2,
49+
'10': 'bitness',
50+
'17': true
51+
},
52+
{
53+
'1': 'architecture',
54+
'3': 7,
55+
'4': 1,
56+
'5': 9,
57+
'9': 3,
58+
'10': 'architecture',
59+
'17': true
60+
},
61+
],
62+
'8': [
63+
{'1': '_edition'},
64+
{'1': '_codename'},
65+
{'1': '_bitness'},
66+
{'1': '_architecture'},
67+
],
68+
};
69+
70+
/// Descriptor for `ClientPlatformInfo`. Decode as a `google.protobuf.DescriptorProto`.
71+
final $typed_data.Uint8List clientPlatformInfoDescriptor = $convert.base64Decode(
72+
'ChJDbGllbnRQbGF0Zm9ybUluZm8SGwoJb3NfZmFtaWx5GAEgASgJUghvc0ZhbWlseRIXCgdvc1'
73+
'90eXBlGAIgASgJUgZvc1R5cGUSGAoHdmVyc2lvbhgDIAEoCVIHdmVyc2lvbhIdCgdlZGl0aW9u'
74+
'GAQgASgJSABSB2VkaXRpb26IAQESHwoIY29kZW5hbWUYBSABKAlIAVIIY29kZW5hbWWIAQESHQ'
75+
'oHYml0bmVzcxgGIAEoCUgCUgdiaXRuZXNziAEBEicKDGFyY2hpdGVjdHVyZRgHIAEoCUgDUgxh'
76+
'cmNoaXRlY3R1cmWIAQFCCgoIX2VkaXRpb25CCwoJX2NvZGVuYW1lQgoKCF9iaXRuZXNzQg8KDV'
77+
'9hcmNoaXRlY3R1cmU=');

client/lib/open/api.dart

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
1+
import 'dart:convert';
12
import 'dart:io';
23

34
import 'package:cookie_jar/cookie_jar.dart';
5+
import 'package:device_info_plus/device_info_plus.dart';
46
import 'package:dio/dio.dart';
57
import 'package:dio_cookie_manager/dio_cookie_manager.dart';
68
import 'package:mobile/data/db/enums.dart';
9+
import 'package:mobile/data/proto/client_platform_info.pb.dart';
710
import 'package:mobile/data/proxy/config.dart';
11+
import 'package:package_info_plus/package_info_plus.dart';
812
import 'package:mobile/data/proxy/enrollment.dart';
913
import 'package:mobile/data/proxy/mfa.dart';
1014

@@ -44,6 +48,48 @@ class _ProxyApi {
4448
final cookieJar = CookieJar();
4549
_dio.interceptors.add(CookieManager(cookieJar));
4650
_dio.interceptors.add(TalkerDioLogger(talker: talker));
51+
_initHeaders();
52+
}
53+
54+
Future<void> _initHeaders() async {
55+
try {
56+
final deviceInfo = DeviceInfoPlugin();
57+
final ClientPlatformInfo platformInfo;
58+
59+
if (Platform.isAndroid) {
60+
final android = await deviceInfo.androidInfo;
61+
platformInfo = ClientPlatformInfo(
62+
osType: 'Android',
63+
version: android.version.release,
64+
codename: android.version.codename,
65+
architecture: android.supportedAbis.first,
66+
bitness: '64',
67+
);
68+
} else if (Platform.isIOS) {
69+
final ios = await deviceInfo.iosInfo;
70+
platformInfo = ClientPlatformInfo(
71+
osType: 'iOS',
72+
version: ios.systemVersion,
73+
architecture: 'arm64',
74+
bitness: '64',
75+
);
76+
} else {
77+
platformInfo = ClientPlatformInfo(
78+
osFamily: Platform.operatingSystem,
79+
osType: Platform.operatingSystem,
80+
version: Platform.operatingSystemVersion,
81+
);
82+
}
83+
84+
final platformBytes = platformInfo.writeToBuffer();
85+
final platformBase64 = base64Encode(platformBytes);
86+
87+
final packageInfo = await PackageInfo.fromPlatform();
88+
_dio.options.headers['defguard-client-version'] = packageInfo.version;
89+
_dio.options.headers['defguard-client-platform'] = platformBase64;
90+
} catch (e) {
91+
talker.error("Failed to set client headers", e);
92+
}
4793
}
4894

4995
Future<(ConfigurationPollResponse?, int?, Headers?)> pollConfiguration(

0 commit comments

Comments
 (0)