Skip to content

Commit 9ac3725

Browse files
author
TheNoumanDev
committed
feat: added docs for SSL configuration and android manifestation
1 parent 4fde92c commit 9ac3725

File tree

3 files changed

+311
-26
lines changed

3 files changed

+311
-26
lines changed
Lines changed: 228 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,247 @@
11
# SSL Configuration
22

3-
SSL (Secure Sockets Layer) configuration in Ensemble allows you to secure your API communications through certificate pinning and verification controls. This guide explains the available SSL configuration options and their proper usage.
3+
SSL (Secure Sockets Layer) configuration in Ensemble allows you to secure your API communications through certificate pinning and verification controls. This guide explains both global and per-API SSL configuration options and their proper usage.
44

5-
## Available Configuration Options
5+
## Configuration Levels
66

7-
### ssl_pinning_enabled
7+
Ensemble supports SSL configuration at two levels:
88

9+
1. **Global Configuration** - Applied to all APIs by default using environment variables and secrets
10+
2. **Per-API Configuration** - Overrides global settings for specific APIs using the `sslConfig` property
11+
12+
## Global SSL Configuration
13+
14+
### Environment Variables
15+
16+
These settings apply to all APIs unless overridden by per-API configuration:
17+
18+
#### ssl_pinning_enabled
919
- **Type:** Environment Variable
10-
- **Purpose:** Controls whether SSL certificate pinning is active
11-
- **Behavior:** When set to 'true', the app will only trust connections with certificates matching the provided certificate
20+
- **Purpose:** Controls whether SSL certificate pinning is active globally
21+
- **Values:** 'true' or 'false'
22+
- **Default:** false
1223
- **Availability:** Only supported in native apps (not available for web apps)
24+
25+
#### bypass_ssl_pinning
26+
- **Type:** Environment Variable
27+
- **Purpose:** Allows bypassing SSL certificate verification globally
28+
- **Values:** 'true' or 'false'
29+
- **Default:** false
30+
- **Warning:** Should only be used in development environments, never in production
31+
32+
#### bypass_ssl_pinning_with_validation
33+
- **Type:** Environment Variable
34+
- **Purpose:** Bypass SSL pinning while validating against stored certificate fingerprints
35+
- **Values:** 'true' or 'false'
1336
- **Default:** false
37+
- **Usage:** Compares current certificate fingerprint with stored fingerprint from secure storage
1438

15-
### ssl_pinning_certificate
39+
### Secrets
1640

41+
#### ssl_pinning_certificate
1742
- **Type:** Secret
1843
- **Purpose:** Provides the certificate for SSL pinning verification
1944
- **Format:** Must be Base64 encoded
2045
- **Behavior:** The app will only trust servers presenting this certificate
2146
- **Dependencies:** Requires `ssl_pinning_enabled` to be 'true'
2247

23-
### bypass_ssl_pinning
48+
## Per-API SSL Configuration
2449

25-
- **Type:** Environment Variable
26-
- **Purpose:** Allows bypassing SSL certificate verification
27-
- **Warning:** Should only be used in development environments, never in production
28-
- **Behavior:** When 'true', bypasses all SSL certificate verification
29-
- **Default:** false
50+
For more granular control, you can override global SSL settings for individual APIs using the `sslConfig` property in your API definition.
51+
52+
### Basic Syntax
53+
54+
```yaml
55+
API:
56+
mySecureAPI:
57+
uri: https://api.example.com/data
58+
method: GET
59+
sslConfig:
60+
pinningEnabled: true
61+
bypassPinning: false
62+
bypassPinningWithFingerprint: false
63+
fingerprintKey: "api_example_com_fingerprint"
64+
headers:
65+
Authorization: Bearer ${token}
66+
```
67+
68+
### sslConfig Properties
69+
70+
#### pinningEnabled
71+
- **Type:** Boolean
72+
- **Purpose:** Enable/disable SSL certificate pinning for this specific API
73+
- **Values:** true or false
74+
- **Overrides:** Global `ssl_pinning_enabled` environment variable
75+
- **Example:** `pinningEnabled: true`
76+
77+
#### bypassPinning
78+
- **Type:** Boolean
79+
- **Purpose:** Bypass SSL certificate verification for this specific API
80+
- **Values:** true or false
81+
- **Overrides:** Global `bypass_ssl_pinning` environment variable
82+
- **Warning:** Use only in development
83+
- **Example:** `bypassPinning: true`
84+
85+
#### bypassPinningWithFingerprint
86+
- **Type:** Boolean
87+
- **Purpose:** Bypass SSL pinning while validating against stored certificate fingerprints
88+
- **Values:** true or false
89+
- **Overrides:** Global `bypass_ssl_pinning_with_validation` environment variable
90+
- **Example:** `bypassPinningWithFingerprint: true`
91+
- **Requirement:** `fingerprintKey` should be set with the same key given in API defination as secureStorage.
92+
93+
#### fingerprintKey
94+
- **Type:** String
95+
- **Purpose:** Specifies the key in secure storage where the certificate fingerprint is stored
96+
- **Default:** "bypass_ssl_fingerprint"
97+
- **Usage:** Used with `bypassPinningWithFingerprint` to retrieve the stored certificate fingerprint for validation
98+
- **Example:** `fingerprintKey: "api_example_com_fingerprint"`
99+
100+
## Certificate Fingerprint Management
101+
102+
When using `bypassPinningWithFingerprint`, you need to store the certificate fingerprint in secure storage. There are two main approaches:
103+
104+
### Method 1: Using Ensemble's setSecureStorage Action
105+
106+
Store the certificate fingerprint manually using Ensemble's secure storage:
107+
108+
```yaml
109+
Button:
110+
label: Store Certificate Fingerprint
111+
onTap:
112+
setSecureStorage:
113+
key: "api_example_com_fingerprint"
114+
value: "a1b2c3d4e5f6..." # SHA256 fingerprint of the certificate
115+
onComplete:
116+
showToast:
117+
message: Certificate fingerprint stored
118+
```
119+
120+
### Method 2: Using External Methods (Dynamic Certificate Capture)
121+
122+
For dynamic certificate capture, you can expose external methods from your host application:
123+
124+
#### Host Application Setup (Flutter/Dart Example)
125+
126+
```dart
127+
// In your main.dart or wherever you initialize EnsembleApp
128+
Future<Map<String, dynamic>> captureCertificateForHost({
129+
required String host,
130+
int port = 443
131+
}) async {
132+
HttpClient httpClient = HttpClient();
133+
httpClient.connectionTimeout = const Duration(seconds: 10);
134+
135+
String sha256Certificate = '';
136+
137+
httpClient.badCertificateCallback = (X509Certificate cert, String certHost, int certPort) {
138+
if (certHost.toLowerCase() == host.toLowerCase()) {
139+
sha256Certificate = sha256.convert(cert.der).toString();
140+
return true;
141+
}
142+
return false;
143+
};
144+
145+
try {
146+
HttpClientRequest request = await httpClient.getUrl(Uri.parse('https://$host:$port/'));
147+
HttpClientResponse response = await request.close();
148+
await response.drain();
149+
httpClient.close();
150+
151+
if (sha256Certificate != '') {
152+
await StorageManager().writeSecurely(
153+
key: 'bypass_ssl_certificate',
154+
value: sha256Certificate,
155+
);
156+
return {'status': true, 'fingerprint': sha256Certificate};
157+
} else {
158+
return {'success': false, 'error': 'Failed to capture certificate'};
159+
}
160+
} catch (e) {
161+
return {'success': false, 'error': e.toString()};
162+
}
163+
}
164+
165+
// Register the external method
166+
EnsembleApp(
167+
externalMethods: const {
168+
'captureCertificateForHost': captureCertificateForHost,
169+
},
170+
// ... other properties
171+
)
172+
```
173+
174+
#### Using External Method in Ensemble EDL
175+
176+
```yaml
177+
View:
178+
onLoad:
179+
callExternalMethod:
180+
name: captureCertificateForHost
181+
payload:
182+
host: ${HOST}
183+
port: ${PORT_NUMBER}
184+
onComplete:
185+
invokeAPI:
186+
name: secureAPI
187+
onError:
188+
showToast:
189+
message: "Failed to capture certificate: ${response.error}"
190+
options:
191+
type: error
192+
193+
API:
194+
secureAPI:
195+
uri: ${HOST}/endpoint
196+
method: GET
197+
sslConfig:
198+
bypassPinningWithFingerprint: true
199+
fingerprintKey: "api_fingerprint"
200+
headers:
201+
Authorization: Bearer ${token}
202+
```
203+
204+
205+
206+
## Configuration Examples
207+
208+
### Example 1: High-Security API with Certificate Pinning
209+
210+
```yaml
211+
API:
212+
paymentAPI:
213+
uri: https://secure-payment.example.com/process
214+
method: POST
215+
sslConfig:
216+
pinningEnabled: true
217+
bypassPinning: false
218+
bypassPinningWithFingerprint: false
219+
headers:
220+
Authorization: Bearer ${paymentToken}
221+
Content-Type: application/json
222+
body:
223+
amount: ${amount}
224+
currency: USD
225+
```
226+
227+
### Example 2: Development API with SSL Bypass
228+
229+
```yaml
230+
API:
231+
devTestAPI:
232+
uri: https://dev-server.example.com/test
233+
method: GET
234+
sslConfig:
235+
pinningEnabled: false
236+
bypassPinning: true # Only for development!
237+
bypassPinningWithFingerprint: false
238+
headers:
239+
Authorization: Bearer ${devToken}
240+
```
30241

31-
## Security Considerations
242+
## Security Best Practices
32243

33-
1. Certificate pinning provides protection against man-in-the-middle attacks
34-
2. SSL bypass shouldn't be used in production environments as it will create security concerns.
35-
3. Web applications cannot use SSL pinning and rely on browser-based verification
244+
1. **Production Environment**: Always use certificate pinning (`pinningEnabled: true`) for production APIs
245+
2. **Development Environment**: Use `bypassPinning: true` only during development
246+
3. **Dynamic Environments**: Use `bypassPinningWithFingerprint: true` when dealing with dynamic certificates or multiple environments
247+
4. **Certificate Storage**: Store certificate fingerprints securely using `setSecureStorage` or external methods

pages/tips-and-tricks.md

Lines changed: 28 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,28 +2,46 @@
22

33
Welcome to our Tips and Tricks page! Here, you'll find a collection of expert insights and best practices to help you make the most of our platform. Discover shortcuts, time-saving techniques, and hidden features to enhance your productivity.
44

5+
## Development and Build Tips
6+
57
- [Getting help from Ensemble team](/tips-and-tricks/getting-help)
8+
- [Securing API calls with SSL Configuration](/apis/api-ssl-configuration.md)
9+
- [Using local assets](/tips-and-tricks/using-local-assets)
10+
- [Managing Unnecessary Android Permissions](/tips-and-tricks/managing-android-permissions)
11+
12+
## UI and Styling
13+
614
- [Custom BottomNavBar item styling](/tips-and-tricks/custom-navbar-items)
715
- [Making the UI responsive](/tips-and-tricks/responsive-ui)
8-
- [Use device's camera for updating profile picture](/tips-and-tricks/user-profile-picture)
9-
- [Inputs to ChartJs](/tips-and-tricks/inputs-chartjs)
1016
- [How to modify the BottomNavBar with custom styling and widgets](/tips-and-tricks/how-to-modify-bottom-navbar)
11-
- [Open Maps with Coordinates on Android and iOS](/tips-and-tricks/open-maps-with-coordinates)
12-
- [Using navigate Screen with BottomNavBar](/tips-and-tricks/navigateScreen-with-bottomNavBar)
1317
- [Dynamic color modification](/tips-and-tricks/dynamic-color-modification)
1418
- [Icons in bottomNavBar](/tips-and-tricks/icons-in-bottomNavBar)
19+
- [Using BottomSafeArea for responsive layouts](/tips-and-tricks/bottom-safe-area.md)
20+
- [Floating Button](/tips-and-tricks/floatingButton.md)
21+
22+
## Device and Platform Features
23+
24+
- [Use device's camera for updating profile picture](/tips-and-tricks/user-profile-picture)
25+
- [Using device width and height](/tips-and-tricks/using-device-dimentions.md)
1526
- [InvokeHaptic](/tips-and-tricks/invoke-haptic.md)
27+
- [Open Maps with Coordinates on Android and iOS](/tips-and-tricks/open-maps-with-coordinates)
28+
- [Push Notification](/tips-and-tricks/push-notification.md)
29+
30+
## Navigation and User Experience
31+
32+
- [Using navigate Screen with BottomNavBar](/tips-and-tricks/navigateScreen-with-bottomNavBar)
33+
34+
## Advanced Features
35+
36+
- [Inputs to ChartJs](/tips-and-tricks/inputs-chartjs)
1637
- [Lottie Animations](/tips-and-tricks/lottie-animations.md)
1738
- [HTML Widget](/tips-and-tricks/html-widget.md)
18-
- [Push Notification](/tips-and-tricks/push-notification.md)
19-
- [Using device width and height](/tips-and-tricks/using-device-dimentions.md)
20-
- [Using BottomSafeArea for responsive layouts](/tips-and-tricks/bottom-safe-area.md)
21-
- [Floating Button](/tips-and-tricks/floatingButton.md)
22-
- [Securing API calls with SSL Configuration](/apis/api-ssl-configuration.md)
39+
40+
## Common Error Solutions
2341

2442
Below are some common error messages and their solutions:
2543

2644
- [Widget requires a width](/error/no-bounded-width)
2745
- [Widget requires a height](/error/no-bounded-height)
2846
- [FlexRow requires a width for child distribution](/error/flexrow-no-bounded-width)
29-
- [FlexColumn requires a height for child distribution](/error/flexcolumn-no-bounded-height)
47+
- [FlexColumn requires a height for child distribution](/error/flexcolumn-no-bounded-height)
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
# Managing Unnecessary Android Permissions
2+
3+
When we include Ensemble dependencies in our host project, the merged Android manifest file of APK can include permissions from all plugins, even those we're not actively using. This can lead to unnecessary permissions being requested from users and potential app store rejections.
4+
5+
## The Problem
6+
7+
For example, if we include Firebase Analytics in our dependencies, it might automatically add advertising-related permissions like:
8+
- `com.google.android.gms.permission.AD_ID`
9+
- `android.permission.ACCESS_ADSERVICES_ATTRIBUTION`
10+
- `android.permission.ACCESS_ADSERVICES_AD_ID`
11+
12+
Even if we're not using advertising features, these permissions will be included in our final APK.
13+
14+
## The Solution
15+
16+
we can explicitly remove unwanted permissions from our Android manifest using the `tools:node="remove"` attribute.
17+
18+
### Step 1: Add the tools namespace
19+
20+
Make sure our `android/app/src/main/AndroidManifest.xml` file includes the tools namespace in the root manifest tag:
21+
22+
```xml
23+
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
24+
xmlns:tools="http://schemas.android.com/tools">
25+
```
26+
27+
### Step 2: Remove unwanted permissions
28+
29+
Add the following permissions with `tools:node="remove"` to explicitly remove them from our final manifest:
30+
31+
```xml
32+
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
33+
xmlns:tools="http://schemas.android.com/tools">
34+
35+
<!-- Remove ad-related permissions -->
36+
<uses-permission android:name="com.google.android.gms.permission.AD_ID" tools:node="remove" />
37+
<uses-permission android:name="android.permission.ACCESS_ADSERVICES_ATTRIBUTION" tools:node="remove" />
38+
<uses-permission android:name="android.permission.ACCESS_ADSERVICES_AD_ID" tools:node="remove" />
39+
40+
<!-- our other permissions and application tag -->
41+
<application>
42+
<!-- our app content -->
43+
</application>
44+
</manifest>
45+
```
46+
47+
### Step 3: Verify the changes
48+
49+
After making these changes:
50+
51+
1. Clean our project: `flutter clean`
52+
2. Rebuild our app: `flutter build apk --debug`
53+
3. Verify the permissions oure removed by checking the final APK using tools like `aapt` or Android Studio's APK Analyzer
54+
55+
This approach ensures our Ensemble app only requests the permissions it actually needs, providing a better user experience and maintaining security best practices.

0 commit comments

Comments
 (0)