Skip to content

Fix InsecureConnectionBuilder (Android) and add allowInsecureConnections support (iOS/macOS)#650

Open
jrevillard wants to merge 1 commit into
MaikuB:masterfrom
jrevillard:fix/insecure-connection-builder
Open

Fix InsecureConnectionBuilder (Android) and add allowInsecureConnections support (iOS/macOS)#650
jrevillard wants to merge 1 commit into
MaikuB:masterfrom
jrevillard:fix/insecure-connection-builder

Conversation

@jrevillard
Copy link
Copy Markdown

Summary

Fixes #386

The allowInsecureConnections parameter does not work on any platform:

  • Android: InsecureConnectionBuilder.openConnection() is a no-op — it calls URL.openConnection() without any SSL/TLS configuration, so HTTPS connections still validate the certificate chain normally.
  • iOS/macOS: The allowInsecureConnections parameter is completely ignored. There is no equivalent of InsecureConnectionBuilder, and no SSL bypass mechanism exists.

This makes it impossible to use the plugin against OIDC providers with self-signed certificates during development (e.g., local Keycloak instances).

Changes

Android

Replaced the no-op InsecureConnectionBuilder.openConnection() with a real implementation that:

  • Creates an SSLContext with a trust-all X509TrustManager
  • Sets a permissive HostnameVerifier on HttpsURLConnection
  • Only applies when allowInsecureConnections=true (the existing routing in FlutterAppauthPlugin via getConnectionBuilder() is unchanged)

iOS & macOS

Added InsecureURLProtocol (an NSURLProtocol subclass) that:

  • Intercepts HTTPS requests when registered
  • Creates a private NSURLSession with empty protocolClasses to avoid recursion
  • Trusts all server certificates via NSURLSessionAuthChallengeUseCredential
  • Is registered before and unregistered after each plugin method call, only when allowInsecureConnections=true

Also added allowInsecureConnections property to TokenRequestParameters and EndSessionRequestParameters on iOS/macOS (was previously only defined on Android).

Testing

Verified on Android emulator against a local Keycloak instance with a self-signed certificate:

  • Endpoint discovery
  • Authorization + token exchange
  • Token refresh
  • End session (logout)

All operations succeed without "Network error" when allowInsecureConnections=true.

Security Note

The SSL bypass is only active when allowInsecureConnections=true is explicitly passed by the caller. Production configurations use CA-signed certificates and allowInsecureConnections=false (the default), so this change has no production impact.

…ecureConnections support for iOS/macOS

Android: Replace no-op InsecureConnectionBuilder.openConnection() with real SSL
bypass using TrustManager + HostnameVerifier when allowInsecureConnections=true.

iOS/macOS: Add InsecureURLProtocol (NSURLProtocol subclass) that intercepts HTTPS
connections and trusts all certificates. Register/unregister conditionally based on
allowInsecureConnections flag. Previously, the flag was completely ignored on Apple
platforms.

Fixes MaikuB#386
@MaikuB
Copy link
Copy Markdown
Owner

MaikuB commented May 23, 2026

Thanks for the PR. Before I go take a look into in more depth, do you have instructions I could follow to test this PR and ensure the functionality continues to work in the future?

@jrevillard
Copy link
Copy Markdown
Author

Hi @MaikuB, here's how to test this PR:

Prerequisites

You need an OIDC provider with a self-signed HTTPS certificate. The easiest way is a local Keycloak:

# Generate a self-signed cert
openssl req -x509 -newkey rsa:2048 -keyout key.pem -out cert.pem -days 365 -nodes -subj "/CN=localhost"

# Run Keycloak with the self-signed cert
docker run -d --name keycloak-test -p 8443:8443 \
  -v $(pwd)/cert.pem:/etc/keycloak/cert.pem \
  -v $(pwd)/key.pem:/etc/keycloak/key.pem \
  -e KC_BOOTSTRAP_ADMIN_USERNAME=admin \
  -e KC_BOOTSTRAP_ADMIN_PASSWORD=admin \
  quay.io/keycloak/keycloak:26.6 \
  start \
  --hostname=localhost \
  --hostname-strict=false \
  --https-certificate-file=/etc/keycloak/cert.pem \
  --https-certificate-key-file=/etc/keycloak/key.pem

Wait for it to start (~15s), then create a client and a test user via the admin console at https://localhost:8443 (admin/admin):

  • Create a client test-app (public client, with redirect URI matching the example app, enable direct access grants)
  • Create a user with a non-temporary password

Alternatively, via the API:

# Get admin token
ADMIN_TOKEN=$(curl -sk -X POST "https://localhost:8443/realms/master/protocol/openid-connect/token" \
  -d "client_id=admin-cli" -d "username=admin" -d "password=admin" -d "grant_type=password" \
  | python3 -c "import sys,json; print(json.load(sys.stdin)['access_token'])")

# Create client
curl -sk -X POST "https://localhost:8443/admin/realms/master/clients" \
  -H "Authorization: Bearer $ADMIN_TOKEN" -H "Content-Type: application/json" \
  -d '{"clientId":"test-app","enabled":true,"publicClient":true,"redirectUris":["com.example.app://callback","com.example.app://logout"],"standardFlowEnabled":true,"directAccessGrantsEnabled":true}'

# Create user
curl -sk -X POST "https://localhost:8443/admin/realms/master/users" \
  -H "Authorization: Bearer $ADMIN_TOKEN" -H "Content-Type: application/json" \
  -d '{"username":"testuser","email":"testuser@example.com","emailVerified":true,"firstName":"Test","lastName":"User","enabled":true,"credentials":[{"type":"password","value":"testpass123","temporary":false}]}'

Test with the example app

  1. Point the example app (flutter_appauth/example/) at your local Keycloak:

    • discoveryUrl: https://localhost:8443/realms/master/.well-known/openid-configuration
    • clientId: test-app
    • redirectUrl: your redirect URI
    • allowInsecureConnections: true
  2. Run on Android and/or iOS and exercise these flows:

    • Authorization + token exchange
    • Token refresh
    • End session / logout

What to verify

Scenario Expected
allowInsecureConnections: true against self-signed HTTPS All flows succeed (discovery, auth, token refresh, end session)
allowInsecureConnections: false (default) against self-signed HTTPS All flows fail with a network/SSL error — bypass is NOT active by default
allowInsecureConnections: false against a valid CA-signed server All flows succeed normally — no regression

The key thing to confirm is that the flag is opt-in only: when false (the default), behavior is identical to the current release.

I've tested this exact setup — Keycloak 26.6 starts correctly with the self-signed cert, the OIDC discovery endpoint responds, and token grants work against it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment